# This is a shell archive. Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by kundert at athos on Fri Jul 1 01:56:11 1988
#
# This archive contains:
# Makefile spAllocate.c spBuild.c spConfig.h
# spDefs.h spFactor.c spMatrix.h spSolve.c
#
LANG=""; export LANG
echo x - Makefile
cat >Makefile <<'@EOF'
# Makefile for Sparse1.3
#
# Ken Kundert
# UC Berkeley
#
CFLAGS = -O
LINTFLAGS = -lc -lm
SHELL = /bin/sh
CC = cc
HFILES = spConfig.h spDefs.h spMatrix.h
CFILES = spAllocate.c spBuild.c spFactor.c spOutput.c spSolve.c spUtils.c \
spFortran.c
OFILES = spAllocate.o spBuild.o spFactor.o spOutput.o spSolve.o spUtils.o \
spFortran.o
LIBRARY = sparse.a
DESTINATION = sparse
TESTC = spTest.c
TESTO = spTest.o
SOURCE = $(HFILES) $(CFILES)
$(DESTINATION) : $(LIBRARY) $(TESTO)
$(CC) $(CFLAGS) -o $(DESTINATION) $(TESTO) $(LIBRARY) -lm
$(LIBRARY) : $(OFILES)
ar r $(LIBRARY) $?
ranlib $(LIBRARY)
lint :
@lint $(LINTFLAGS) $(CFILES) $(TESTC) | grep -v "but never used"
clean :
rm -f $(OFILES) $(LIBRARY) $(TESTO) $(DESTINATION) core
touch :
touch -c $(OFILES) $(LIBRARY)
ranlib $(LIBRARY)
tags : $(SOURCE) $(TESTC)
ctags -t -w $(SOURCE) $(TESTC)
$(OFILES) : $(HFILES)
$(TESTO) : $(HFILES)
@EOF
chmod 644 Makefile
echo x - spAllocate.c
cat >spAllocate.c <<'@EOF'
/*
* MATRIX ALLOCATION MODULE
*
* Author: Advising professor:
* Kenneth S. Kundert Alberto Sangiovanni-Vincentelli
* UC Berkeley
*
* This file contains the allocation and deallocation routines for the
* sparse matrix routines.
*
* >>> User accessible functions contained in this file:
* spCreate
* spDestroy
* spError
* spWhereSingular
* spGetSize
* spSetReal
* spSetComplex
* spFillinCount
* spElementCount
*
* >>> Other functions contained in this file:
* spcGetElement
* InitializeElementBlocks
* spcGetFillin
* RecordAllocation
* AllocateBlockOfAllocationList
* EnlargeMatrix
* ExpandTranslationArrays
*/
/*
* 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.
*/
#ifndef lint
static char copyright[] =
"Sparse1.3: Copyright (c) 1985,86,87,88 by Kenneth S. Kundert";
static char RCSid[] =
"@(#)$Header: spAllocate.c,v 1.2 88/06/18 11:12:22 kundert Exp $";
#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 "spConfig.h"
#include "spMatrix.h"
#include "spDefs.h"
/*
* MATRIX ALLOCATION
*
* Allocates and initializes the data structures associated with a matrix.
*
* >>> Returned:
* A pointer to the matrix is returned cast into the form of a pointer to
* a character. This pointer is then passed and used by the other matrix
* routines to refer to a particular matrix. If an error occurs, the NULL
* pointer is returned.
*
* >>> Arguments:
* Size (int)
* Size of matrix or estimate of size of matrix if matrix is EXPANDABLE.
* Complex (int)
* Type of matrix. If Complex is 0 then the matrix is real, otherwise
* the matrix will be complex. Note that if the routines are not set up
* to handle the type of matrix requested, then a spPANIC error will occur.
* Further note that if a matrix will be both real and complex, it must
* be specified here as being complex.
* pError (int *)
* Returns error flag, needed because function spError() will not work
* correctly if spCreate() returns NULL.
*
* >>> Local variables:
* AllocatedSize (int)
* The size of the matrix being allocated.
* Matrix (MatrixPtr)
* A pointer to the matrix frame being created.
*
* >>> Possible errors:
* spNO_MEMORY
* spPANIC
* Error is cleared in this routine.
*/
char *
spCreate( Size, Complex, pError )
int Size, *pError;
BOOLEAN Complex;
{
register unsigned SizePlusOne;
register MatrixPtr Matrix;
register int I;
int AllocatedSize;
/* Begin `spCreate'. */
/* Clear error flag. */
*pError = spOKAY;
/* Test for valid size. */
if ((Size < 0) OR (Size == 0 AND NOT EXPANDABLE))
{ *pError = spPANIC;
return NULL;
}
/* Test for valid type. */
#if NOT spCOMPLEX
if (Complex)
{ *pError = spPANIC;
return NULL;
}
#endif
#if NOT REAL
if (NOT Complex)
{ *pError = spPANIC;
return NULL;
}
#endif
/* Create Matrix. */
AllocatedSize = MAX( Size, MINIMUM_ALLOCATED_SIZE );
SizePlusOne = (unsigned)(AllocatedSize + 1);
if ((Matrix = ALLOC(struct MatrixFrame, 1)) == NULL)
{ *pError = spNO_MEMORY;
return NULL;
}
/* Initialize matrix */
Matrix->ID = SPARSE_ID;
Matrix->Complex = Complex;
Matrix->PreviousMatrixWasComplex = Complex;
Matrix->Factored = NO;
Matrix->Elements = 0;
Matrix->Error = *pError;
Matrix->Fillins = 0;
Matrix->Reordered = NO;
Matrix->NeedsOrdering = YES;
Matrix->NumberOfInterchangesIsOdd = NO;
Matrix->Partitioned = NO;
Matrix->RowsLinked = NO;
Matrix->InternalVectorsAllocated = NO;
Matrix->SingularCol = 0;
Matrix->SingularRow = 0;
Matrix->Size = Size;
Matrix->AllocatedSize = AllocatedSize;
Matrix->ExtSize = Size;
Matrix->AllocatedExtSize = AllocatedSize;
Matrix->CurrentSize = 0;
Matrix->ExtToIntColMap = NULL;
Matrix->ExtToIntRowMap = NULL;
Matrix->IntToExtColMap = NULL;
Matrix->IntToExtRowMap = NULL;
Matrix->MarkowitzRow = NULL;
Matrix->MarkowitzCol = NULL;
Matrix->MarkowitzProd = NULL;
Matrix->DoCmplxDirect = NULL;
Matrix->DoRealDirect = NULL;
Matrix->Intermediate = NULL;
Matrix->RelThreshold = DEFAULT_THRESHOLD;
Matrix->AbsThreshold = 0.0;
Matrix->TopOfAllocationList = NULL;
Matrix->RecordsRemaining = 0;
Matrix->ElementsRemaining = 0;
Matrix->FillinsRemaining = 0;
RecordAllocation( Matrix, (char *)Matrix );
if (Matrix->Error == spNO_MEMORY) goto MemoryError;
/* Take out the trash. */
Matrix->TrashCan.Real = 0.0;
#if spCOMPLEX
Matrix->TrashCan.Imag = 0.0;
#endif
Matrix->TrashCan.Row = 0;
Matrix->TrashCan.Col = 0;
Matrix->TrashCan.NextInRow = NULL;
Matrix->TrashCan.NextInCol = NULL;
#if INITIALIZE
Matrix->TrashCan.pInitInfo = NULL;
#endif
/* Allocate space in memory for Diag pointer vector. */
CALLOC( Matrix->Diag, ElementPtr, SizePlusOne);
if (Matrix->Diag == NULL)
goto MemoryError;
/* Allocate space in memory for FirstInCol pointer vector. */
CALLOC( Matrix->FirstInCol, ElementPtr, SizePlusOne);
if (Matrix->FirstInCol == NULL)
goto MemoryError;
/* Allocate space in memory for FirstInRow pointer vector. */
CALLOC( Matrix->FirstInRow, ElementPtr, SizePlusOne);
if (Matrix->FirstInRow == NULL)
goto MemoryError;
/* Allocate space in memory for IntToExtColMap vector. */
if (( Matrix->IntToExtColMap = ALLOC(int, SizePlusOne)) == NULL)
goto MemoryError;
/* Allocate space in memory for IntToExtRowMap vector. */
if (( Matrix->IntToExtRowMap = ALLOC(int, SizePlusOne)) == NULL)
goto MemoryError;
/* Initialize MapIntToExt vectors. */
for (I = 1; I <= AllocatedSize; I++)
{ Matrix->IntToExtRowMap[I] = I;
Matrix->IntToExtColMap[I] = I;
}
#if TRANSLATE
/* Allocate space in memory for ExtToIntColMap vector. */
if (( Matrix->ExtToIntColMap = ALLOC(int, SizePlusOne)) == NULL)
goto MemoryError;
/* Allocate space in memory for ExtToIntRowMap vector. */
if (( Matrix->ExtToIntRowMap = ALLOC(int, SizePlusOne)) == NULL)
goto MemoryError;
/* Initialize MapExtToInt vectors. */
for (I = 1; I <= AllocatedSize; I++)
{ Matrix->ExtToIntColMap[I] = -1;
Matrix->ExtToIntRowMap[I] = -1;
}
Matrix->ExtToIntColMap[0] = 0;
Matrix->ExtToIntRowMap[0] = 0;
#endif
/* Allocate space for fill-ins and initial set of elements. */
InitializeElementBlocks( Matrix, SPACE_FOR_ELEMENTS*AllocatedSize,
SPACE_FOR_FILL_INS*AllocatedSize );
if (Matrix->Error == spNO_MEMORY)
goto MemoryError;
return (char *)Matrix;
MemoryError:
/* Deallocate matrix and return no pointer to matrix if there is not enough
memory. */
*pError = spNO_MEMORY;
spDestroy( (char *)Matrix);
return NULL;
}
/*
* ELEMENT ALLOCATION
*
* This routine allocates space for matrix elements. It requests large blocks
* of storage from the system and doles out individual elements as required.
* This technique, as opposed to allocating elements individually, tends to
* speed the allocation process.
*
* >>> Returned:
* A pointer to an element.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to matrix.
*
* >>> Local variables:
* pElement (ElementPtr)
* A pointer to the first element in the group of elements being allocated.
*
* >>> Possible errors:
* spNO_MEMORY
*/
ElementPtr
spcGetElement( Matrix )
MatrixPtr Matrix;
{
ElementPtr pElement;
/* Begin `spcGetElement'. */
/* Allocate block of MatrixElements if necessary. */
if (Matrix->ElementsRemaining == 0)
{ pElement = ALLOC(struct MatrixElement, ELEMENTS_PER_ALLOCATION);
RecordAllocation( Matrix, (char *)pElement );
if (Matrix->Error == spNO_MEMORY) return NULL;
Matrix->ElementsRemaining = ELEMENTS_PER_ALLOCATION;
Matrix->NextAvailElement = pElement;
}
/* Update Element counter and return pointer to Element. */
Matrix->ElementsRemaining--;
return Matrix->NextAvailElement++;
}
/*
* ELEMENT ALLOCATION INITIALIZATION
*
* This routine allocates space for matrix fill-ins and an initial set of
* elements. Besides being faster than allocating space for elements one
* at a time, it tends to keep the fill-ins physically close to the other
* matrix elements in the computer memory. This keeps virtual memory paging
* to a minimum.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* InitialNumberOfElements (int)
* This number is used as the size of the block of memory, in
* MatrixElements, reserved for elements. If more than this number of
* elements are generated, then more space is allocated later.
* NumberOfFillinsExpected (int)
* This number is used as the size of the block of memory, in
* MatrixElements, reserved for fill-ins. If more than this number of
* fill-ins are generated, then more space is allocated, but they may
* not be physically close in computer's memory.
*
* >>> Local variables:
* pElement (ElementPtr)
* A pointer to the first element in the group of elements being allocated.
*
* >>> Possible errors:
* spNO_MEMORY
*/
static
InitializeElementBlocks( Matrix, InitialNumberOfElements,
NumberOfFillinsExpected )
MatrixPtr Matrix;
int InitialNumberOfElements, NumberOfFillinsExpected;
{
ElementPtr pElement;
/* Begin `InitializeElementBlocks'. */
/* Allocate block of MatrixElements for elements. */
pElement = ALLOC(struct MatrixElement, InitialNumberOfElements);
RecordAllocation( Matrix, (char *)pElement );
if (Matrix->Error == spNO_MEMORY) return;
Matrix->ElementsRemaining = InitialNumberOfElements;
Matrix->NextAvailElement = pElement;
/* Allocate block of MatrixElements for fill-ins. */
pElement = ALLOC(struct MatrixElement, NumberOfFillinsExpected);
RecordAllocation( Matrix, (char *)pElement );
if (Matrix->Error == spNO_MEMORY) return;
Matrix->FillinsRemaining = NumberOfFillinsExpected;
Matrix->NextAvailFillin = pElement;
/* Allocate a fill-in list structure. */
Matrix->FirstFillinListNode = ALLOC(struct FillinListNodeStruct,1);
RecordAllocation( Matrix, (char *)Matrix->FirstFillinListNode );
if (Matrix->Error == spNO_MEMORY) return;
Matrix->LastFillinListNode = Matrix->FirstFillinListNode;
Matrix->FirstFillinListNode->pFillinList = pElement;
Matrix->FirstFillinListNode->NumberOfFillinsInList =NumberOfFillinsExpected;
Matrix->FirstFillinListNode->Next = NULL;
return;
}
/*
* FILL-IN ALLOCATION
*
* This routine allocates space for matrix fill-ins. It requests large blocks
* of storage from the system and doles out individual elements as required.
* This technique, as opposed to allocating elements individually, tends to
* speed the allocation process.
*
* >>> Returned:
* A pointer to the fill-in.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to matrix.
*
* >>> Possible errors:
* spNO_MEMORY
*/
ElementPtr
spcGetFillin( Matrix )
MatrixPtr Matrix;
{
struct FillinListNodeStruct *pListNode;
ElementPtr pFillins;
/* Begin `spcGetFillin'. */
#if NOT STRIP OR LINT
if (Matrix->FillinsRemaining == 0)
return spcGetElement( Matrix );
#endif
#if STRIP OR LINT
if (Matrix->FillinsRemaining == 0)
{ pListNode = Matrix->LastFillinListNode;
/* First see if there are any stripped fill-ins left. */
if (pListNode->Next != NULL)
{ Matrix->LastFillinListNode = pListNode = pListNode->Next;
Matrix->FillinsRemaining = pListNode->NumberOfFillinsInList;
Matrix->NextAvailFillin = pListNode->pFillinList;
}
else
{
/* Allocate block of fill-ins. */
pFillins = ALLOC(struct MatrixElement, ELEMENTS_PER_ALLOCATION);
RecordAllocation( Matrix, (char *)pFillins );
if (Matrix->Error == spNO_MEMORY) return NULL;
Matrix->FillinsRemaining = ELEMENTS_PER_ALLOCATION;
Matrix->NextAvailFillin = pFillins;
/* Allocate a fill-in list structure. */
pListNode->Next = ALLOC(struct FillinListNodeStruct,1);
RecordAllocation( Matrix, (char *)pListNode->Next );
if (Matrix->Error == spNO_MEMORY) return NULL;
Matrix->LastFillinListNode = pListNode = pListNode->Next;
pListNode->pFillinList = pFillins;
pListNode->NumberOfFillinsInList = ELEMENTS_PER_ALLOCATION;
pListNode->Next = NULL;
}
}
#endif
/* Update Fill-in counter and return pointer to Fill-in. */
Matrix->FillinsRemaining--;
return Matrix->NextAvailFillin++;
}
/*
* RECORD A MEMORY ALLOCATION
*
* This routine is used to record all memory allocations so that the memory
* can be freed later.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* AllocatedPtr (char *)
* The pointer returned by malloc or calloc. These pointers are saved in
* a list so that they can be easily freed.
*
* >>> Possible errors:
* spNO_MEMORY
*/
static
RecordAllocation( Matrix, AllocatedPtr )
MatrixPtr Matrix;
char *AllocatedPtr;
{
/* Begin `RecordAllocation'. */
/*
* If Allocated pointer is NULL, assume that malloc returned a NULL pointer,
* which indicates a spNO_MEMORY error.
*/
if (AllocatedPtr == NULL)
{ Matrix->Error = spNO_MEMORY;
return;
}
/* Allocate block of MatrixElements if necessary. */
if (Matrix->RecordsRemaining == 0)
{ AllocateBlockOfAllocationList( Matrix );
if (Matrix->Error == spNO_MEMORY)
{ FREE(AllocatedPtr);
return;
}
}
/* Add Allocated pointer to Allocation List. */
(++Matrix->TopOfAllocationList)->AllocatedPtr = AllocatedPtr;
Matrix->RecordsRemaining--;
return;
}
/*
* ADD A BLOCK OF SLOTS TO ALLOCATION LIST
*
* This routine increases the size of the allocation list.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
*
* >>> Local variables:
* ListPtr (AllocationListPtr)
* Pointer to the list that contains the pointers to segments of memory
* that were allocated by the operating system for the current matrix.
*
* >>> Possible errors:
* spNO_MEMORY
*/
static
AllocateBlockOfAllocationList( Matrix )
MatrixPtr Matrix;
{
register int I;
register AllocationListPtr ListPtr;
/* Begin `AllocateBlockOfAllocationList'. */
/* Allocate block of records for allocation list. */
ListPtr = ALLOC(struct AllocationRecord, (ELEMENTS_PER_ALLOCATION+1));
if (ListPtr == NULL)
{ Matrix->Error = spNO_MEMORY;
return;
}
/* String entries of allocation list into singly linked list. List is linked
such that any record points to the one before it. */
ListPtr->NextRecord = Matrix->TopOfAllocationList;
Matrix->TopOfAllocationList = ListPtr;
ListPtr += ELEMENTS_PER_ALLOCATION;
for (I = ELEMENTS_PER_ALLOCATION; I > 0; I--)
{ ListPtr->NextRecord = ListPtr - 1;
ListPtr--;
}
/* Record allocation of space for allocation list on allocation list. */
Matrix->TopOfAllocationList->AllocatedPtr = (char *)ListPtr;
Matrix->RecordsRemaining = ELEMENTS_PER_ALLOCATION;
return;
}
/*
* MATRIX DEALLOCATION
*
* Deallocates pointers and elements of Matrix.
*
* >>> Arguments:
* Matrix (char *)
* Pointer to the matrix frame which is to be removed from memory.
*
* >>> Local variables:
* ListPtr (AllocationListPtr)
* Pointer into the linked list of pointers to allocated data structures.
* Points to pointer to structure to be freed.
* NextListPtr (AllocationListPtr)
* Pointer into the linked list of pointers to allocated data structures.
* Points to the next pointer to structure to be freed. This is needed
* because the data structure to be freed could include the current node
* in the allocation list.
*/
void
spDestroy( eMatrix )
register char *eMatrix;
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
register AllocationListPtr ListPtr, NextListPtr;
/* Begin `spDestroy'. */
ASSERT( IS_SPARSE( Matrix ) );
/* Deallocate the vectors that are located in the matrix frame. */
FREE( Matrix->IntToExtColMap );
FREE( Matrix->IntToExtRowMap );
FREE( Matrix->ExtToIntColMap );
FREE( Matrix->ExtToIntRowMap );
FREE( Matrix->Diag );
FREE( Matrix->FirstInRow );
FREE( Matrix->FirstInCol );
FREE( Matrix->MarkowitzRow );
FREE( Matrix->MarkowitzCol );
FREE( Matrix->MarkowitzProd );
FREE( Matrix->DoCmplxDirect );
FREE( Matrix->DoRealDirect );
FREE( Matrix->Intermediate );
/* Sequentially step through the list of allocated pointers freeing pointers
* along the way. */
ListPtr = Matrix->TopOfAllocationList;
while (ListPtr != NULL)
{ NextListPtr = ListPtr->NextRecord;
FREE( ListPtr->AllocatedPtr );
ListPtr = NextListPtr;
}
return;
}
/*
* RETURN MATRIX ERROR STATUS
*
* This function is used to determine the error status of the given matrix.
*
* >>> Returned:
* The error status of the given matrix.
*
* >>> Arguments:
* eMatrix (char *)
* The matrix for which the error status is desired.
*/
int
spError( eMatrix )
char *eMatrix;
{
/* Begin `spError'. */
if (eMatrix != NULL)
{ ASSERT(((MatrixPtr)eMatrix)->ID == SPARSE_ID);
return ((MatrixPtr)eMatrix)->Error;
}
else return spNO_MEMORY; /* This error may actually be spPANIC,
* no way to tell. */
}
/*
* WHERE IS MATRIX SINGULAR
*
* This function returns the row and column number where the matrix was
* detected as singular or where a zero was detected on the diagonal.
*
* >>> Arguments:
* eMatrix (char *)
* The matrix for which the error status is desired.
* pRow (int *)
* The row number.
* pCol (int *)
* The column number.
*/
void
spWhereSingular( eMatrix, pRow, pCol )
char *eMatrix;
int *pRow, *pCol;
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
/* Begin `spWhereSingular'. */
ASSERT( IS_SPARSE( Matrix ) );
if (Matrix->Error == spSINGULAR OR Matrix->Error == spZERO_DIAG)
{ *pRow = Matrix->SingularRow;
*pCol = Matrix->SingularCol;
}
else *pRow = *pCol = 0;
return;
}
/*
* MATRIX SIZE
*
* Returns the size of the matrix. Either the internal or external size of
* the matrix is returned.
*
* >>> Arguments:
* eMatrix (char *)
* Pointer to matrix.
* External (BOOLEAN)
* If External is set true, the external size , i.e., the value of the
* largest external row or column number encountered is returned.
* Otherwise the true size of the matrix is returned. These two sizes
* may differ if the TRANSLATE option is set true.
*/
int
spGetSize( eMatrix, External )
char *eMatrix;
BOOLEAN External;
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
/* Begin `spGetSize'. */
ASSERT( IS_SPARSE( Matrix ) );
#if TRANSLATE
if (External)
return Matrix->ExtSize;
else
return Matrix->Size;
#else
return Matrix->Size;
#endif
}
/*
* SET MATRIX COMPLEX OR REAL
*
* Forces matrix to be either real or complex.
*
* >>> Arguments:
* eMatrix (char *)
* Pointer to matrix.
*/
void
spSetReal( eMatrix )
char *eMatrix;
{
/* Begin `spSetReal'. */
ASSERT( IS_SPARSE( (MatrixPtr)eMatrix ) AND REAL);
((MatrixPtr)eMatrix)->Complex = NO;
return;
}
void
spSetComplex( eMatrix )
char *eMatrix;
{
/* Begin `spSetComplex'. */
ASSERT( IS_SPARSE( (MatrixPtr)eMatrix ) AND spCOMPLEX);
((MatrixPtr)eMatrix)->Complex = YES;
return;
}
/*
* ELEMENT OR FILL-IN COUNT
*
* Two functions used to return simple statistics. Either the number
* of total elements, or the number of fill-ins can be returned.
*
* >>> Arguments:
* eMatrix (char *)
* Pointer to matrix.
*/
int
spFillinCount( eMatrix )
char *eMatrix;
{
/* Begin `spFillinCount'. */
ASSERT( IS_SPARSE( (MatrixPtr)eMatrix ) );
return ((MatrixPtr)eMatrix)->Fillins;
}
int
spElementCount( eMatrix )
char *eMatrix;
{
/* Begin `spElementCount'. */
ASSERT( IS_SPARSE( (MatrixPtr)eMatrix ) );
return ((MatrixPtr)eMatrix)->Elements;
}
@EOF
chmod 644 spAllocate.c
echo x - spBuild.c
cat >spBuild.c <<'@EOF'
/*
* MATRIX BUILD MODULE
*
* Author: Advising professor:
* Kenneth S. Kundert Alberto Sangiovanni-Vincentelli
* UC Berkeley
*
* This file contains the routines associated with clearing, loading and
* preprocessing the matrix for the sparse matrix routines.
*
* >>> User accessible functions contained in this file:
* spClear
* spGetElement
* spGetAdmittance
* spGetQuad
* spGetOnes
* spInstallInitInfo
* spGetInitInfo
* spInitialize
*
* >>> Other functions contained in this file:
* spcFindElementInCol
* Translate
* spcCreateElement
* spcLinkRows
* EnlargeMatrix
* ExpandTranslationArrays
*/
/*
* 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.
*/
#ifndef lint
static char copyright[] =
"Sparse1.3: Copyright (c) 1985,86,87,88 by Kenneth S. Kundert";
static char RCSid[] =
"@(#)$Header: spBuild.c,v 1.2 88/06/18 11:12:37 kundert Exp $";
#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 "spConfig.h"
#include "spMatrix.h"
#include "spDefs.h"
/*
* CLEAR MATRIX
*
* Sets every element of the matrix to zero and clears the error flag.
*
* >>> Arguments:
* Matrix (char *)
* Pointer to matrix that is to be cleared.
*
* >>> Local variables:
* pElement (ElementPtr)
* A pointer to the element being cleared.
*/
void
spClear( eMatrix )
char *eMatrix;
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
register ElementPtr pElement;
register int I;
/* Begin `spClear'. */
ASSERT( IS_SPARSE( Matrix ) );
/* Clear matrix. */
#if spCOMPLEX
if (Matrix->PreviousMatrixWasComplex OR Matrix->Complex)
{ for (I = Matrix->Size; I > 0; I--)
{ pElement = Matrix->FirstInCol[I];
while (pElement != NULL)
{ pElement->Real = 0.0;
pElement->Imag = 0.0;
pElement = pElement->NextInCol;
}
}
}
else
#endif
{ for (I = Matrix->Size; I > 0; I--)
{ pElement = Matrix->FirstInCol[I];
while (pElement != NULL)
{ pElement->Real = 0.0;
pElement = pElement->NextInCol;
}
}
}
/* Empty the trash. */
Matrix->TrashCan.Real = 0.0;
#if spCOMPLEX
Matrix->TrashCan.Imag = 0.0;
#endif
Matrix->Error = spOKAY;
Matrix->Factored = NO;
Matrix->SingularCol = 0;
Matrix->SingularRow = 0;
Matrix->PreviousMatrixWasComplex = Matrix->Complex;
return;
}
/*
* SINGLE ELEMENT ADDITION TO MATRIX BY INDEX
*
* Finds element [Row,Col] and returns a pointer to it. If element is
* not found then it is created and spliced into matrix. This routine
* is only to be used after spCreate() and before spMNA_Preorder(),
* spFactor() or spOrderAndFactor(). Returns a pointer to the
* Real portion of a MatrixElement. This pointer is later used by
* spADD_xxx_ELEMENT to directly access element.
*
* >>> Returns:
* Returns a pointer to the element. This pointer is then used to directly
* access the element during successive builds.
*
* >>> Arguments:
* Matrix (char *)
* Pointer to the matrix that the element is to be added to.
* Row (int)
* Row index for element. Must be in the range of [0..Size] unless
* the options EXPANDABLE or TRANSLATE are used. Elements placed in
* row zero are discarded. In no case may Row be less than zero.
* Col (int)
* Column index for element. Must be in the range of [0..Size] unless
* the options EXPANDABLE or TRANSLATE are used. Elements placed in
* column zero are discarded. In no case may Col be less than zero.
*
* >>> Local variables:
* pElement (RealNumber *)
* Pointer to the element.
*
* >>> Possible errors:
* spNO_MEMORY
* Error is not cleared in this routine.
*/
RealNumber *
spGetElement( eMatrix, Row, Col )
char *eMatrix;
int Row, Col;
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
RealNumber *pElement;
ElementPtr spcFindElementInCol();
void Translate();
/* Begin `spGetElement'. */
ASSERT( IS_SPARSE( Matrix ) AND Row >= 0 AND Col >= 0 );
if ((Row == 0) OR (Col == 0))
return &Matrix->TrashCan.Real;
#if NOT TRANSLATE
ASSERT(Matrix->NeedsOrdering);
#endif
#if TRANSLATE
Translate( Matrix, &Row, &Col );
if (Matrix->Error == spNO_MEMORY) return NULL;
#endif
#if NOT TRANSLATE
#if NOT EXPANDABLE
ASSERT(Row <= Matrix->Size AND Col <= Matrix->Size);
#endif
#if EXPANDABLE
/* Re-size Matrix if necessary. */
if ((Row > Matrix->Size) OR (Col > Matrix->Size))
EnlargeMatrix( Matrix, MAX(Row, Col) );
if (Matrix->Error == spNO_MEMORY) return NULL;
#endif
#endif
/*
* The condition part of the following if statement tests to see if the
* element resides along the diagonal, if it does then it tests to see
* if the element has been created yet (Diag pointer not NULL). The
* pointer to the element is then assigned to Element after it is cast
* into a pointer to a RealNumber. This casting makes the pointer into
* a pointer to Real. This statement depends on the fact that Real
* is the first record in the MatrixElement structure.
*/
if ((Row != Col) OR ((pElement = (RealNumber *)Matrix->Diag[Row]) == NULL))
{
/*
* Element does not exist or does not reside along diagonal. Search
* column for element. As in the if statement above, the pointer to the
* element which is returned by spcFindElementInCol is cast into a
* pointer to Real, a RealNumber.
*/
pElement = (RealNumber*)spcFindElementInCol( Matrix,
&(Matrix->FirstInCol[Col]),
Row, Col, YES );
}
return pElement;
}
/*
* FIND ELEMENT BY SEARCHING COLUMN
*
* Searches column starting at element specified at PtrAddr and finds element
* in Row. If Element does not exists, it is created. The pointer to the
* element is returned.
*
* >>> Returned:
* A pointer to the desired element:
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to Matrix.
* LastAddr (ElementPtr *)
* Address of pointer that initially points to the element in Col at which
* the search is started. The pointer in this location may be changed if
* a fill-in is required in and adjacent element. For this reason it is
* important that LastAddr be the address of a FirstInCol or a NextInCol
* rather than a temporary variable.
* Row (int)
* Row being searched for.
* Col (int)
* Column being searched.
* CreateIfMissing (BOOLEAN)
* Indicates what to do if element is not found, create one or return a
* NULL pointer.
*
* Local variables:
* pElement (ElementPtr)
* Pointer used to search through matrix.
*/
ElementPtr
spcFindElementInCol( Matrix, LastAddr, Row, Col, CreateIfMissing )
MatrixPtr Matrix;
register ElementPtr *LastAddr;
register int Row;
int Col;
BOOLEAN CreateIfMissing;
{
register ElementPtr pElement;
ElementPtr spcCreateElement();
/* Begin `spcFindElementInCol'. */
pElement = *LastAddr;
/* Search for element. */
while (pElement != NULL)
{ if (pElement->Row < Row)
{
/* Have not reached element yet. */
LastAddr = &(pElement->NextInCol);
pElement = pElement->NextInCol;
}
else if (pElement->Row == Row)
{
/* Reached element. */
return pElement;
}
else break; /* while loop */
}
/* Element does not exist and must be created. */
if (CreateIfMissing)
return spcCreateElement( Matrix, Row, Col, LastAddr, NO );
else return NULL;
}
#if TRANSLATE
/*
* TRANSLATE EXTERNAL INDICES TO INTERNAL
*
* Convert internal row and column numbers to internal row and column numbers.
* Also updates Ext/Int maps.
*
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* Row (int *)
* Upon entry Row is either a external row number of an external node
* number. Upon entry, the internal equivalent is supplied.
* Col (int *)
* Upon entry Column is either a external column number of an external node
* number. Upon entry, the internal equivalent is supplied.
*
* >>> Local variables:
* ExtCol (int)
* Temporary variable used to hold the external column or node number
* during the external to internal column number translation.
* ExtRow (int)
* Temporary variable used to hold the external row or node number during
* the external to internal row number translation.
* IntCol (int)
* Temporary variable used to hold the internal column or node number
* during the external to internal column number translation.
* IntRow (int)
* Temporary variable used to hold the internal row or node number during
* the external to internal row number translation.
*/
static void
Translate( Matrix, Row, Col )
MatrixPtr Matrix;
int *Row, *Col;
{
register int IntRow, IntCol, ExtRow, ExtCol;
/* Begin `Translate'. */
ExtRow = *Row;
ExtCol = *Col;
/* Expand translation arrays if necessary. */
if ((ExtRow > Matrix->AllocatedExtSize) OR
(ExtCol > Matrix->AllocatedExtSize))
{
ExpandTranslationArrays( Matrix, MAX(ExtRow, ExtCol) );
if (Matrix->Error == spNO_MEMORY) return;
}
/* Set ExtSize if necessary. */
if ((ExtRow > Matrix->ExtSize) OR (ExtCol > Matrix->ExtSize))
Matrix->ExtSize = MAX(ExtRow, ExtCol);
/* Translate external row or node number to internal row or node number. */
if ((IntRow = Matrix->ExtToIntRowMap[ExtRow]) == -1)
{ Matrix->ExtToIntRowMap[ExtRow] = ++Matrix->CurrentSize;
Matrix->ExtToIntColMap[ExtRow] = Matrix->CurrentSize;
IntRow = Matrix->CurrentSize;
#if NOT EXPANDABLE
ASSERT(IntRow <= Matrix->Size);
#endif
#if EXPANDABLE
/* Re-size Matrix if necessary. */
if (IntRow > Matrix->Size)
EnlargeMatrix( Matrix, IntRow );
if (Matrix->Error == spNO_MEMORY) return;
#endif
Matrix->IntToExtRowMap[IntRow] = ExtRow;
Matrix->IntToExtColMap[IntRow] = ExtRow;
}
/* Translate external column or node number to internal column or node number.*/
if ((IntCol = Matrix->ExtToIntColMap[ExtCol]) == -1)
{ Matrix->ExtToIntRowMap[ExtCol] = ++Matrix->CurrentSize;
Matrix->ExtToIntColMap[ExtCol] = Matrix->CurrentSize;
IntCol = Matrix->CurrentSize;
#if NOT EXPANDABLE
ASSERT(IntCol <= Matrix->Size);
#endif
#if EXPANDABLE
/* Re-size Matrix if necessary. */
if (IntCol > Matrix->Size)
EnlargeMatrix( Matrix, IntCol );
if (Matrix->Error == spNO_MEMORY) return;
#endif
Matrix->IntToExtRowMap[IntCol] = ExtCol;
Matrix->IntToExtColMap[IntCol] = ExtCol;
}
*Row = IntRow;
*Col = IntCol;
return;
}
#endif
#if QUAD_ELEMENT
/*
* ADDITION OF ADMITTANCE TO MATRIX BY INDEX
*
* Performs same function as spGetElement except rather than one
* element, all four Matrix elements for a floating component are
* added. This routine also works if component is grounded. Positive
* elements are placed at [Node1,Node2] and [Node2,Node1]. This
* routine is only to be used after spCreate() and before
* spMNA_Preorder(), spFactor() or spOrderAndFactor().
*
* >>> Returns:
* Error code.
*
* >>> Arguments:
* Matrix (char *)
* Pointer to the matrix that component is to be entered in.
* Node1 (int)
* Row and column indices for elements. Must be in the range of [0..Size]
* unless the options EXPANDABLE or TRANSLATE are used. Node zero is the
* ground node. In no case may Node1 be less than zero.
* Node2 (int)
* Row and column indices for elements. Must be in the range of [0..Size]
* unless the options EXPANDABLE or TRANSLATE are used. Node zero is the
* ground node. In no case may Node2 be less than zero.
* Template (struct spTemplate *)
* Collection of pointers to four elements that are later used to directly
* address elements. User must supply the template, this routine will
* fill it.
*
* Possible errors:
* spNO_MEMORY
* Error is not cleared in this routine.
*/
int
spGetAdmittance( Matrix, Node1, Node2, Template )
char *Matrix;
int Node1, Node2;
struct spTemplate *Template;
{
/* Begin `spGetAdmittance'. */
Template->Element1 = spGetElement(Matrix, Node1, Node1 );
Template->Element2 = spGetElement(Matrix, Node2, Node2 );
Template->Element3Negated = spGetElement( Matrix, Node2, Node1 );
Template->Element4Negated = spGetElement( Matrix, Node1, Node2 );
if
( (Template->Element1 == NULL)
OR (Template->Element2 == NULL)
OR (Template->Element3Negated == NULL)
OR (Template->Element4Negated == NULL)
) return spNO_MEMORY;
if (Node1 == 0)
SWAP( RealNumber*, Template->Element1, Template->Element2 );
return spOKAY;
}
#endif /* QUAD_ELEMENT */
#if QUAD_ELEMENT
/*
* ADDITION OF FOUR ELEMENTS TO MATRIX BY INDEX
*
* Similar to spGetAdmittance, except that spGetAdmittance only
* handles 2-terminal components, whereas spGetQuad handles simple
* 4-terminals as well. These 4-terminals are simply generalized
* 2-terminals with the option of having the sense terminals different
* from the source and sink terminals. spGetQuad adds four
* elements to the matrix. Positive elements occur at Row1,Col1
* Row2,Col2 while negative elements occur at Row1,Col2 and Row2,Col1.
* The routine works fine if any of the rows and columns are zero.
* This routine is only to be used after spCreate() and before
* spMNA_Preorder(), spFactor() or spOrderAndFactor()
* unless TRANSLATE is set true.
*
* >>> Returns:
* Error code.
*
* >>> Arguments:
* Matrix (char *)
* Pointer to the matrix that component is to be entered in.
* Row1 (int)
* First row index for elements. Must be in the range of [0..Size]
* unless the options EXPANDABLE or TRANSLATE are used. Zero is the
* ground row. In no case may Row1 be less than zero.
* Row2 (int)
* Second row index for elements. Must be in the range of [0..Size]
* unless the options EXPANDABLE or TRANSLATE are used. Zero is the
* ground row. In no case may Row2 be less than zero.
* Col1 (int)
* First column index for elements. Must be in the range of [0..Size]
* unless the options EXPANDABLE or TRANSLATE are used. Zero is the
* ground column. In no case may Col1 be less than zero.
* Col2 (int)
* Second column index for elements. Must be in the range of [0..Size]
* unless the options EXPANDABLE or TRANSLATE are used. Zero is the
* ground column. In no case may Col2 be less than zero.
* Template (struct spTemplate *)
* Collection of pointers to four elements that are later used to directly
* address elements. User must supply the template, this routine will
* fill it.
* Real (RealNumber)
* Real data to be added to elements.
* Imag (RealNumber)
* Imag data to be added to elements. If matrix is real, this argument
* may be deleted.
*
* Possible errors:
* spNO_MEMORY
* Error is not cleared in this routine.
*/
int
spGetQuad( Matrix, Row1, Row2, Col1, Col2, Template )
char *Matrix;
int Row1, Row2, Col1, Col2;
struct spTemplate *Template;
{
/* Begin `spGetQuad'. */
Template->Element1 = spGetElement( Matrix, Row1, Col1);
Template->Element2 = spGetElement( Matrix, Row2, Col2 );
Template->Element3Negated = spGetElement( Matrix, Row2, Col1 );
Template->Element4Negated = spGetElement( Matrix, Row1, Col2 );
if
( (Template->Element1 == NULL)
OR (Template->Element2 == NULL)
OR (Template->Element3Negated == NULL)
OR (Template->Element4Negated == NULL)
) return spNO_MEMORY;
if (Template->Element1 == &((MatrixPtr)Matrix)->TrashCan.Real)
SWAP( RealNumber *, Template->Element1, Template->Element2 );
return spOKAY;
}
#endif /* QUAD_ELEMENT */
#if QUAD_ELEMENT
/*
* ADDITION OF FOUR STRUCTURAL ONES TO MATRIX BY INDEX
*
* Performs similar function to spGetQuad() except this routine is
* meant for components that do not have an admittance representation.
*
* The following stamp is used:
* Pos Neg Eqn
* Pos [ . . 1 ]
* Neg [ . . -1 ]
* Eqn [ 1 -1 . ]
*
* >>> Returns:
* Error code.
*
* >>> Arguments:
* Matrix (char *)
* Pointer to the matrix that component is to be entered in.
* Pos (int)
* See stamp above. Must be in the range of [0..Size]
* unless the options EXPANDABLE or TRANSLATE are used. Zero is the
* ground row. In no case may Pos be less than zero.
* Neg (int)
* See stamp above. Must be in the range of [0..Size]
* unless the options EXPANDABLE or TRANSLATE are used. Zero is the
* ground row. In no case may Neg be less than zero.
* Eqn (int)
* See stamp above. Must be in the range of [0..Size]
* unless the options EXPANDABLE or TRANSLATE are used. Zero is the
* ground row. In no case may Eqn be less than zero.
* Template (struct spTemplate *)
* Collection of pointers to four elements that are later used to directly
* address elements. User must supply the template, this routine will
* fill it.
*
* Possible errors:
* spNO_MEMORY
* Error is not cleared in this routine.
*/
int
spGetOnes(Matrix, Pos, Neg, Eqn, Template)
char *Matrix;
int Pos, Neg, Eqn;
struct spTemplate *Template;
{
/* Begin `spGetOnes'. */
Template->Element4Negated = spGetElement( Matrix, Neg, Eqn );
Template->Element3Negated = spGetElement( Matrix, Eqn, Neg );
Template->Element2 = spGetElement( Matrix, Pos, Eqn );
Template->Element1 = spGetElement( Matrix, Eqn, Pos );
if
( (Template->Element1 == NULL)
OR (Template->Element2 == NULL)
OR (Template->Element3Negated == NULL)
OR (Template->Element4Negated == NULL)
) return spNO_MEMORY;
spADD_REAL_QUAD( *Template, 1.0 );
return spOKAY;
}
#endif /* QUAD_ELEMENT */
/*
*
* CREATE AND SPLICE ELEMENT INTO MATRIX
*
* This routine is used to create new matrix elements and splice them into the
* matrix.
*
* >>> Returned:
* A pointer to the element that was created is returned.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to matrix.
* Row (int)
* Row index for element.
* Col (int)
* Column index for element.
* LastAddr (ElementPtr *)
* This contains the address of the pointer to the element just above the
* one being created. It is used to speed the search and it is updated with
* address of the created element.
* Fillin (BOOLEAN)
* Flag that indicates if created element is to be a fill-in.
*
* >>> Local variables:
* pElement (ElementPtr)
* Pointer to an element in the matrix. It is used to refer to the newly
* created element and to restring the pointers of the element's row and
* column.
* pLastElement (ElementPtr)
* Pointer to the element in the matrix that was just previously pointed
* to by pElement. It is used to restring the pointers of the element's
* row and column.
* pCreatedElement (ElementPtr)
* Pointer to the desired element, the one that was just created.
*
* >>> Possible errors:
* spNO_MEMORY
*/
ElementPtr
spcCreateElement( Matrix, Row, Col, LastAddr, Fillin )
MatrixPtr Matrix;
int Row;
register int Col;
register ElementPtr *LastAddr;
BOOLEAN Fillin;
{
register ElementPtr pElement, pLastElement;
ElementPtr pCreatedElement, spcGetElement(), spcGetFillin();
/* Begin `spcCreateElement'. */
if (Matrix->RowsLinked)
{
/* Row pointers cannot be ignored. */
if (Fillin)
{ pElement = spcGetFillin( Matrix );
Matrix->Fillins++;
}
else
{ pElement = spcGetElement( Matrix );
Matrix->NeedsOrdering = YES;
}
if (pElement == NULL) return NULL;
/* If element is on diagonal, store pointer in Diag. */
if (Row == Col) Matrix->Diag[Row] = pElement;
/* Initialize Element. */
pCreatedElement = pElement;
pElement->Row = Row;
pElement->Col = Col;
pElement->Real = 0.0;
#if spCOMPLEX
pElement->Imag = 0.0;
#endif
#if INITIALIZE
pElement->pInitInfo = NULL;
#endif
/* Splice element into column. */
pElement->NextInCol = *LastAddr;
*LastAddr = pElement;
/* Search row for proper element position. */
pElement = Matrix->FirstInRow[Row];
pLastElement = NULL;
while (pElement != NULL)
{
/* Search for element row position. */
if (pElement->Col < Col)
{
/* Have not reached desired element. */
pLastElement = pElement;
pElement = pElement->NextInRow;
}
else pElement = NULL;
}
/* Splice element into row. */
pElement = pCreatedElement;
if (pLastElement == NULL)
{
/* Element is first in row. */
pElement->NextInRow = Matrix->FirstInRow[Row];
Matrix->FirstInRow[Row] = pElement;
}
else
/* Element is not first in row. */
{
pElement->NextInRow = pLastElement->NextInRow;
pLastElement->NextInRow = pElement;
}
}
else
{
/*
* Matrix has not been factored yet. Thus get element rather than fill-in.
* Also, row pointers can be ignored.
*/
/* Allocate memory for Element. */
pElement = spcGetElement( Matrix );
if (pElement == NULL) return NULL;
/* If element is on diagonal, store pointer in Diag. */
if (Row == Col) Matrix->Diag[Row] = pElement;
/* Initialize Element. */
pCreatedElement = pElement;
pElement->Row = Row;
#if DEBUG
pElement->Col = Col;
#endif
pElement->Real = 0.0;
#if spCOMPLEX
pElement->Imag = 0.0;
#endif
#if INITIALIZE
pElement->pInitInfo = NULL;
#endif
/* Splice element into column. */
pElement->NextInCol = *LastAddr;
*LastAddr = pElement;
}
Matrix->Elements++;
return pCreatedElement;
}
/*
*
* LINK ROWS
*
* This routine is used to generate the row links. The spGetElement()
* routines do not create row links, which are needed by the spFactor()
* routines.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
*
* >>> Local variables:
* pElement (ElementPtr)
* Pointer to an element in the matrix.
* FirstInRowEntry (ElementPtr *)
* A pointer into the FirstInRow array. Points to the FirstInRow entry
* currently being operated upon.
* FirstInRowArray (ArrayOfElementPtrs)
* A pointer to the FirstInRow array. Same as Matrix->FirstInRow but
* resides in a register and requires less indirection so is faster to
* use.
* Col (int)
* Column currently being operated upon.
*/
spcLinkRows( Matrix )
MatrixPtr Matrix;
{
register ElementPtr pElement, *FirstInRowEntry;
register ArrayOfElementPtrs FirstInRowArray;
register int Col;
/* Begin `spcLinkRows'. */
FirstInRowArray = Matrix->FirstInRow;
for (Col = Matrix->Size; Col >= 1; Col--)
{
/* Generate row links for the elements in the Col'th column. */
pElement = Matrix->FirstInCol[Col];
while (pElement != NULL)
{ pElement->Col = Col;
FirstInRowEntry = &FirstInRowArray[pElement->Row];
pElement->NextInRow = *FirstInRowEntry;
*FirstInRowEntry = pElement;
pElement = pElement->NextInCol;
}
}
Matrix->RowsLinked = YES;
return;
}
/*
* ENLARGE MATRIX
*
* Increases the size of the matrix.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* NewSize (int)
* The new size of the matrix.
*
* >>> Local variables:
* OldAllocatedSize (int)
* The allocated size of the matrix before it is expanded.
*/
static
EnlargeMatrix( Matrix, NewSize )
MatrixPtr Matrix;
register int NewSize;
{
register int I, OldAllocatedSize = Matrix->AllocatedSize;
/* Begin `EnlargeMatrix'. */
Matrix->Size = NewSize;
if (NewSize <= OldAllocatedSize)
return;
/* Expand the matrix frame. */
NewSize = MAX( NewSize, EXPANSION_FACTOR * OldAllocatedSize );
Matrix->AllocatedSize = NewSize;
if (( REALLOC(Matrix->IntToExtColMap, int, NewSize+1)) == NULL)
{ Matrix->Error = spNO_MEMORY;
return;
}
if (( REALLOC(Matrix->IntToExtRowMap, int, NewSize+1)) == NULL)
{ Matrix->Error = spNO_MEMORY;
return;
}
if (( REALLOC(Matrix->Diag, ElementPtr, NewSize+1)) == NULL)
{ Matrix->Error = spNO_MEMORY;
return;
}
if (( REALLOC(Matrix->FirstInCol, ElementPtr, NewSize+1)) == NULL)
{ Matrix->Error = spNO_MEMORY;
return;
}
if (( REALLOC(Matrix->FirstInRow, ElementPtr, NewSize+1)) == NULL)
{ Matrix->Error = spNO_MEMORY;
return;
}
/*
* Destroy the Markowitz and Intermediate vectors, they will be recreated
* in spOrderAndFactor().
*/
FREE( Matrix->MarkowitzRow );
FREE( Matrix->MarkowitzCol );
FREE( Matrix->MarkowitzProd );
FREE( Matrix->DoRealDirect );
FREE( Matrix->DoCmplxDirect );
FREE( Matrix->Intermediate );
Matrix->InternalVectorsAllocated = NO;
/* Initialize the new portion of the vectors. */
for (I = OldAllocatedSize+1; I <= NewSize; I++)
{ Matrix->IntToExtColMap[I] = I;
Matrix->IntToExtRowMap[I] = I;
Matrix->Diag[I] = NULL;
Matrix->FirstInRow[I] = NULL;
Matrix->FirstInCol[I] = NULL;
}
return;
}
#if TRANSLATE
/*
* EXPAND TRANSLATION ARRAYS
*
* Increases the size arrays that are used to translate external to internal
* row and column numbers.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* NewSize (int)
* The new size of the translation arrays.
*
* >>> Local variables:
* OldAllocatedSize (int)
* The allocated size of the translation arrays before being expanded.
*/
static
ExpandTranslationArrays( Matrix, NewSize )
MatrixPtr Matrix;
register int NewSize;
{
register int I, OldAllocatedSize = Matrix->AllocatedExtSize;
/* Begin `ExpandTranslationArrays'. */
Matrix->ExtSize = NewSize;
if (NewSize <= OldAllocatedSize)
return;
/* Expand the translation arrays ExtToIntRowMap and ExtToIntColMap. */
NewSize = MAX( NewSize, EXPANSION_FACTOR * OldAllocatedSize );
Matrix->AllocatedExtSize = NewSize;
if (( REALLOC(Matrix->ExtToIntRowMap, int, NewSize+1)) == NULL)
{ Matrix->Error = spNO_MEMORY;
return;
}
if (( REALLOC(Matrix->ExtToIntColMap, int, NewSize+1)) == NULL)
{ Matrix->Error = spNO_MEMORY;
return;
}
/* Initialize the new portion of the vectors. */
for (I = OldAllocatedSize+1; I <= NewSize; I++)
{ Matrix->ExtToIntRowMap[I] = -1;
Matrix->ExtToIntColMap[I] = -1;
}
return;
}
#endif
#if INITIALIZE
/*
* INITIALIZE MATRIX
*
* With the INITIALIZE compiler option (see spConfig.h) set true,
* Sparse allows the user to keep initialization information with each
* structurally nonzero matrix element. Each element has a pointer
* that is set and used by the user. The user can set this pointer
* using spInstallInitInfo and may be read using spGetInitInfo. Both
* may be used only after the element exists. The function
* spInitialize() is a user customizable way to initialize the matrix.
* Passed to this routine is a function pointer. spInitialize() sweeps
* through every element in the matrix and checks the pInitInfo
* pointer (the user supplied pointer). If the pInitInfo is NULL,
* which is true unless the user changes it (almost always true for
* fill-ins), then the element is zeroed. Otherwise, the function
* pointer is called and passed the pInitInfo pointer as well as the
* element pointer and the external row and column numbers. If the
* user sets the value of each element, then spInitialize() replaces
* spClear().
*
* The user function is expected to return a nonzero integer if there
* is a fatal error and zero otherwise. Upon encountering a nonzero
* return code, spInitialize() terminates and returns the error code.
*
* >>> Arguments:
* Matrix (char *)
* Pointer to matrix.
*
* >>> Possible Errors:
* Returns nonzero if error, zero otherwise.
*/
void
spInstallInitInfo( pElement, pInitInfo )
RealNumber *pElement;
char *pInitInfo;
{
/* Begin `spInstallInitInfo'. */
ASSERT(pElement != NULL);
((ElementPtr)pElement)->pInitInfo = pInitInfo;
}
char *
spGetInitInfo( pElement )
RealNumber *pElement;
{
/* Begin `spGetInitInfo'. */
ASSERT(pElement != NULL);
return (char *)((ElementPtr)pElement)->pInitInfo;
}
int
spInitialize( eMatrix, pInit )
char *eMatrix;
int (*pInit)();
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
register ElementPtr pElement;
int J, Error, Col;
/* Begin `spInitialize'. */
ASSERT( IS_SPARSE( Matrix ) );
#if spCOMPLEX
/* Clear imaginary part of matrix if matrix is real but was complex. */
if (Matrix->PreviousMatrixWasComplex AND NOT Matrix->Complex)
{ for (J = Matrix->Size; J > 0; J--)
{ pElement = Matrix->FirstInCol[J];
while (pElement != NULL)
{ pElement->Imag = 0.0;
pElement = pElement->NextInCol;
}
}
}
#endif /* spCOMPLEX */
/* Initialize the matrix. */
for (J = Matrix->Size; J > 0; J--)
{ pElement = Matrix->FirstInCol[J];
Col = Matrix->IntToExtColMap[J];
while (pElement != NULL)
{ if (pElement->pInitInfo == NULL)
{ pElement->Real = 0.0;
# if spCOMPLEX
pElement->Imag = 0.0;
# endif
}
else
{ Error = (*pInit)((RealNumber *)pElement, pElement->pInitInfo,
Matrix->IntToExtRowMap[pElement->Row], Col);
if (Error)
{ Matrix->Error = spFATAL;
return Error;
}
}
pElement = pElement->NextInCol;
}
}
/* Empty the trash. */
Matrix->TrashCan.Real = 0.0;
#if spCOMPLEX
Matrix->TrashCan.Imag = 0.0;
#endif
Matrix->Error = spOKAY;
Matrix->Factored = NO;
Matrix->SingularCol = 0;
Matrix->SingularRow = 0;
Matrix->PreviousMatrixWasComplex = Matrix->Complex;
return 0;
}
#endif /* INITIALIZE */
@EOF
chmod 644 spBuild.c
echo x - spConfig.h
cat >spConfig.h <<'@EOF'
/*
* CONFIGURATION MACRO DEFINITIONS for sparse matrix routines
*
* Author: Advising professor:
* Kenneth S. Kundert Alberto Sangiovanni-Vincentelli
* U.C. Berkeley
*
* This file contains macros for the sparse matrix routines that are used
* to define the personality of the routines. The user is expected to
* modify this file to maximize the performance of the routines with
* his/her matrices.
*
* Macros are distinguished by using solely capital letters in their
* identifiers. This contrasts with C defined identifiers which are
* strictly lower case, and program variable and procedure names which use
* both upper and lower case.
*/
/*
* 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.
*
* $Date: 88/06/18 11:13:29 $
* $Revision: 1.2 $
*/
#ifndef spCONFIG_DEFS
#define spCONFIG_DEFS
#ifdef spINSIDE_SPARSE
/*
* OPTIONS
*
* These are compiler options. Set each option to one to compile that
* section of the code. If a feature is not desired, set the macro
* to NO. Recommendations are given in brackets, [ignore them].
*
* >>> Option descriptions:
* Arithmetic Precision
* The precision of the arithmetic used by Sparse can be set by
* changing changing the spREAL macro. This macro is
* contained in the file spMatrix.h. It is strongly suggested to
* used double precision with circuit simulators. Note that
* because C always performs arithmetic operations in double
* precision, the only benefit to using single precision is that
* less storage is required. There is often a noticeable speed
* penalty when using single precision. Sparse internally refers
* to a spREAL as a RealNumber.
* REAL
* This specifies that the routines are expected to handle real
* systems of equations. The routines can be compiled to handle
* both real and complex systems at the same time, but there is a
* slight speed and memory advantage if the routines are complied
* to handle only real systems of equations.
* spCOMPLEX
* This specifies that the routines will be complied to handle
* complex systems of equations.
* EXPANDABLE
* Setting this compiler flag true (1) makes the matrix
* expandable before it has been factored. If the matrix is
* expandable, then if an element is added that would be
* considered out of bounds in the current matrix, the size of
* the matrix is increased to hold that element. As a result,
* the size of the matrix need not be known before the matrix is
* built. The matrix can be allocated with size zero and
* expanded.
* TRANSLATE
* This option allows the set of external row and column numbers
* to be non-packed. In other words, the row and column numbers
* do not have to be contiguous. The priced paid for this
* flexibility is that when TRANSLATE is set true, the time
* required to initially build the matrix will be greater because
* the external row and column number must be translated into
* internal equivalents. This translation brings about other
* benefits though. First, the spGetElement() and
* spGetAdmittance() routines may be used after the matrix has
* been factored. Further, elements, and even rows and columns,
* may be added to the matrix, and row and columns may be deleted
* from the matrix, after it has been factored. Note that when
* the set of row and column number is not a packed set, neither
* are the RHS and Solution vectors. Thus the size of these
* vectors must be at least as large as the external size, which
* is the value of the largest given row or column numbers.
* INITIALIZE
* Causes the spInitialize(), spGetInitInfo(), and
* spInstallInitInfo() routines to be compiled. These routines
* allow the user to store and read one pointer in each nonzero
* element in the matrix. spInitialize() then calls a user
* specified function for each structural nonzero in the matrix,
* and includes this pointer as well as the external row and
* column numbers as arguments. This allows the user to write
* custom matrix initialization routines.
* DIAGONAL_PIVOTING
* Many matrices, and in particular node- and modified-node
* admittance matrices, tend to be nearly symmetric and nearly
* diagonally dominant. For these matrices, it is a good idea to
* select pivots from the diagonal. With this option enabled,
* this is exactly what happens, though if no satisfactory pivot
* can be found on the diagonal, an off-diagonal pivot will be
* used. If this option is disabled, Sparse does not
* preferentially search the diagonal. Because of this, Sparse
* has a wider variety of pivot candidates available, and so
* presumably fewer fill-ins will be created. However, the
* initial pivot selection process will take considerably longer.
* If working with node admittance matrices, or other matrices
* with a strong diagonal, it is probably best to use
* DIAGONAL_PIVOTING for two reasons. First, accuracy will be
* better because pivots will be chosen from the large diagonal
* elements, thus reducing the chance of growth. Second, a near
* optimal ordering will be chosen quickly. If the class of
* matrices you are working with does not have a strong diagonal,
* do not use DIAGONAL_PIVOTING, but consider using a larger
* threshold. When DIAGONAL_PIVOTING is turned off, the following
* options and constants are not used: MODIFIED_MARKOWITZ,
* MAX_MARKOWITZ_TIES, and TIES_MULTIPLIER.
* ARRAY_OFFSET
* This determines whether arrays start at an index of zero or one.
* This option is necessitated by the fact that standard C
* convention dictates that arrays begin with an index of zero but
* the standard mathematic convention states that arrays begin with
* an index of one. So if you prefer to start your arrays with
* zero, or your calling Sparse from FORTRAN, set ARRAY_OFFSET to
* NO or 0. Otherwise, set ARRAY_OFFSET to YES or 1. Note that if
* you use an offset of one, the arrays that you pass to Sparse
* must have an allocated length of one plus the size of the
* matrix. ARRAY_OFFSET must be either 0 or 1, no other offsets
* are valid.
* spSEPARATED_COMPLEX_VECTORS
* This specifies the format for complex vectors. If this is set
* false then a complex vector is made up of one double sized
* array of RealNumber's in which the real and imaginary numbers
* are placed in the alternately array in the array. In other
* words, the first entry would be Complex[1].Real, then comes
* Complex[1].Imag, then Complex[1].Real, etc. If
* spSEPARATED_COMPLEX_VECTORS is set true, then each complex
* vector is represented by two arrays of RealNumbers, one with
* the real terms, the other with the imaginary. [NO]
* MODIFIED_MARKOWITZ
* This specifies that the modified Markowitz method of pivot
* selection is to be used. The modified Markowitz method differs
* from standard Markowitz in two ways. First, under modified
* Markowitz, the search for a pivot can be terminated early if a
* adequate (in terms of sparsity) pivot candidate is found.
* Thus, when using modified Markowitz, the initial factorization
* can be faster, but at the expense of a suboptimal pivoting
* order that may slow subsequent factorizations. The second
* difference is in the way modified Markowitz breaks Markowitz
* ties. When two or more elements are pivot candidates and they
* all have the same Markowitz product, then the tie is broken by
* choosing the element that is best numerically. The numerically
* best element is the one with the largest ratio of its magnitude
* to the magnitude of the largest element in the same column,
* excluding itself. The modified Markowitz method results in
* marginally better accuracy. This option is most appropriate
* for use when working with very large matrices where the initial
* factor time represents an unacceptable burden. [NO]
* DELETE
* This specifies that the spDeleteRowAndCol() routine
* should be compiled. Note that for this routine to be
* compiled, both DELETE and TRANSLATE should be set true.
* STRIP
* This specifies that the spStripFills() routine should be compiled.
* MODIFIED_NODAL
* This specifies that the routine that preorders modified node
* admittance matrices should be compiled. This routine results
* in greater speed and accuracy if used with this type of
* matrix.
* QUAD_ELEMENT
* This specifies that the routines that allow four related
* elements to be entered into the matrix at once should be
* compiled. These elements are usually related to an
* admittance. The routines affected by QUAD_ELEMENT are the
* spGetAdmittance, spGetQuad and spGetOnes routines.
* TRANSPOSE
* This specifies that the routines that solve the matrix as if
* it was transposed should be compiled. These routines are
* useful when performing sensitivity analysis using the adjoint
* method.
* SCALING
* This specifies that the routine that performs scaling on the
* matrix should be complied. Scaling is not strongly
* supported. The routine to scale the matrix is provided, but
* no routines are provided to scale and descale the RHS and
* Solution vectors. It is suggested that if scaling is desired,
* it only be preformed when the pivot order is being chosen [in
* spOrderAndFactor()]. This is the only time scaling has
* an effect. The scaling may then either be removed from the
* solution by the user or the scaled factors may simply be
* thrown away. [NO]
* DOCUMENTATION
* This specifies that routines that are used to document the
* matrix, such as spPrint() and spFileMatrix(), should be
* compiled.
* DETERMINANT
* This specifies that the routine spDeterminant() should be complied.
* STABILITY
* This specifies that spLargestElement() and spRoundoff() should
* be compiled. These routines are used to check the stability (and
* hence the quality of the pivoting) of the factorization by
* computing a bound on the size of the element is the matrix E =
* A - LU. If this bound is very high after applying
* spOrderAndFactor(), then the pivot threshold should be raised.
* If the bound increases greatly after using spFactor(), then the
* matrix should probably be reordered.
* CONDITION
* This specifies that spCondition() and spNorm(), the code that
* computes a good estimate of the condition number of the matrix,
* should be compiled.
* PSEUDOCONDITION
* This specifies that spPseudoCondition(), the code that computes
* a crude and easily fooled indicator of ill-conditioning in the
* matrix, should be compiled.
* MULTIPLICATION
* This specifies that the routines to multiply the unfactored
* matrix by a vector should be compiled.
* FORTRAN
* This specifies that the FORTRAN interface routines should be
* compiled. When interfacing to FORTRAN programs, the ARRAY_OFFSET
* options should be set to NO.
* DEBUG
* This specifies that additional error checking will be compiled.
* The type of error checked are those that are common when the
* matrix routines are first integrated into a user's program. Once
* the routines have been integrated in and are running smoothly, this
* option should be turned off.
*/
/* Begin options. */
#define REAL YES
#define EXPANDABLE YES
#define TRANSLATE YES
#define INITIALIZE YES
#define DIAGONAL_PIVOTING YES
#define ARRAY_OFFSET NOT FORTRAN
#define MODIFIED_MARKOWITZ NO
#define DELETE YES
#define STRIP YES
#define MODIFIED_NODAL YES
#define QUAD_ELEMENT YES
#define TRANSPOSE YES
#define SCALING YES
#define DOCUMENTATION YES
#define MULTIPLICATION YES
#define DETERMINANT YES
#define STABILITY YES
#define CONDITION YES
#define PSEUDOCONDITION YES
#define FORTRAN NO
#define DEBUG YES
/*
* The following options affect Sparse exports and so are exported as a
* side effect. For this reason they use the `sp' prefix. The boolean
* constants YES an NO are not defined in spMatrix.h to avoid conflicts
* with user code, so use 0 for NO and 1 for YES.
*/
#endif /* spINSIDE_SPARSE */
#define spCOMPLEX 1
#define spSEPARATED_COMPLEX_VECTORS 0
#ifdef spINSIDE_SPARSE
/*
* MATRIX CONSTANTS
*
* These constants are used throughout the sparse matrix routines. They
* should be set to suit the type of matrix being solved. Recommendations
* are given in brackets.
*
* Some terminology should be defined. The Markowitz row count is the number
* of non-zero elements in a row excluding the one being considered as pivot.
* There is one Markowitz row count for every row. The Markowitz column
* is defined similarly for columns. The Markowitz product for an element
* is the product of its row and column counts. It is a measure of how much
* work would be required on the next step of the factorization if that
* element were chosen to be pivot. A small Markowitz product is desirable.
*
* >>> Constants descriptions:
* DEFAULT_THRESHOLD
* The relative threshold used if the user enters an invalid
* threshold. Also the threshold used by spFactor() when
* calling spOrderAndFactor(). The default threshold should
* not be less than or equal to zero nor larger than one. [0.001]
* DIAG_PIVOTING_AS_DEFAULT
* This indicates whether spOrderAndFactor() should use diagonal
* pivoting as default. This issue only arises when
* spOrderAndFactor() is called from spFactor().
* SPACE_FOR_ELEMENTS
* This number multiplied by the size of the matrix equals the number
* of elements for which memory is initially allocated in
* spCreate(). [6]
* SPACE_FOR_FILL_INS
* This number multiplied by the size of the matrix equals the number
* of elements for which memory is initially allocated and specifically
* reserved for fill-ins in spCreate(). [4]
* ELEMENTS_PER_ALLOCATION
* The number of matrix elements requested from the malloc utility on
* each call to it. Setting this value greater than 1 reduces the
* amount of overhead spent in this system call. On a virtual memory
* machine, its good to allocate slightly less than a page worth of
* elements at a time (or some multiple thereof).
* [For the VAX, for real only use 41, otherwise use 31]
* MINIMUM_ALLOCATED_SIZE
* The minimum allocated size of a matrix. Note that this does not
* limit the minimum size of a matrix. This just prevents having to
* resize a matrix many times if the matrix is expandable, large and
* allocated with an estimated size of zero. This number should not
* be less than one.
* EXPANSION_FACTOR
* The amount the allocated size of the matrix is increased when it
* is expanded.
* MAX_MARKOWITZ_TIES
* This number is used for two slightly different things, both of which
* relate to the search for the best pivot. First, it is the maximum
* number of elements that are Markowitz tied that will be sifted
* through when trying to find the one that is numerically the best.
* Second, it creates an upper bound on how large a Markowitz product
* can be before it eliminates the possibility of early termination
* of the pivot search. In other words, if the product of the smallest
* Markowitz product yet found and TIES_MULTIPLIER is greater than
* MAX_MARKOWITZ_TIES, then no early termination takes place.
* Set MAX_MARKOWITZ_TIES to some small value if no early termination of
* the pivot search is desired. An array of RealNumbers is allocated
* of size MAX_MARKOWITZ_TIES so it must be positive and shouldn't
* be too large. Active when MODIFIED_MARKOWITZ is 1 (true). [100]
* TIES_MULTIPLIER
* Specifies the number of Markowitz ties that are allowed to occur
* before the search for the pivot is terminated early. Set to some
* large value if no early termination of the pivot search is desired.
* This number is multiplied times the Markowitz product to determine
* how many ties are required for early termination. This means that
* more elements will be searched before early termination if a large
* number of fill-ins could be created by accepting what is currently
* considered the best choice for the pivot. Active when
* MODIFIED_MARKOWITZ is 1 (true). Setting this number to zero
* effectively eliminates all pivoting, which should be avoided.
* This number must be positive. TIES_MULTIPLIER is also used when
* diagonal pivoting breaks down. [5]
* DEFAULT_PARTITION
* Which partition mode is used by spPartition() as default.
* Possibilities include
* spDIRECT_PARTITION -- each row used direct addressing, best for
* a few relatively dense matrices.
* spINDIRECT_PARTITION -- each row used indirect addressing, best
* for a few very sparse matrices.
* spAUTO_PARTITION -- direct or indirect addressing is chosen on
* a row-by-row basis, carries a large overhead, but speeds up
* both dense and sparse matrices, best if there is a large
* number of matrices that can use the same ordering.
*/
/* Begin constants. */
#define DEFAULT_THRESHOLD 1.0e-3
#define DIAG_PIVOTING_AS_DEFAULT YES
#define SPACE_FOR_ELEMENTS 6
#define SPACE_FOR_FILL_INS 4
#define ELEMENTS_PER_ALLOCATION 31
#define MINIMUM_ALLOCATED_SIZE 6
#define EXPANSION_FACTOR 1.5
#define MAX_MARKOWITZ_TIES 100
#define TIES_MULTIPLIER 5
#define DEFAULT_PARTITION spAUTO_PARTITION
/*
* PRINTER WIDTH
*
* This macro characterize the printer for the spPrint() routine.
*
* >>> Macros:
* PRINTER_WIDTH
* The number of characters per page width. Set to 80 for terminal,
* 132 for line printer.
*/
/* Begin printer constants. */
#define PRINTER_WIDTH 80
/*
* MACHINE CONSTANTS
*
* These numbers must be updated when the program is ported to a new machine.
*/
/* Begin machine constants. */
#ifdef notdef /* __STDC__ */
/*
* This code is currently deleted because most ANSI standard C compilers
* do not provide the standard header files yet.
*/
# include
# include
# define MACHINE_RESOLUTION DBL_EPSILON
# define LARGEST_REAL DBL_MAX
# define SMALLEST_REAL DBL_MIN
# define LARGEST_SHORT_INTEGER SHRT_MAX
# define LARGEST_LONG_INTEGER LONG_MAX
#else /* NOT defined(__STDC__) */
/* VAX machine constants */
#ifdef vax
# define MACHINE_RESOLUTION 6.93889e-18
# define LARGEST_REAL 1.70141e+38
# define SMALLEST_REAL 2.938743e-39
# define LARGEST_SHORT_INTEGER 32766
# define LARGEST_LONG_INTEGER 2147483646
#endif
/* hp9000 machine constants */
#ifdef hpux
/* These values are correct for hp9000/300. Should be correct for others. */
# define MACHINE_RESOLUTION 8.9e-15
# define LARGEST_REAL 1.79769313486231e+308
# define SMALLEST_REAL 2.22507385850721e-308
# define LARGEST_SHORT_INTEGER 32766
# define LARGEST_LONG_INTEGER 2147483646
#endif
/* Sun machine constants */
#ifdef sun
/* These values are rumored to be the correct values. */
# define MACHINE_RESOLUTION 8.9e-15
# define LARGEST_REAL 1.79769313486231e+308
# define SMALLEST_REAL 2.22507385850721e-308
# define LARGEST_SHORT_INTEGER 32766
# define LARGEST_LONG_INTEGER 2147483646
#endif
#endif /* NOT defined(__STDC__) */
/*
* ANNOTATION
*
* This macro changes the amount of annotation produced by the matrix
* routines. The annotation is used as a debugging aid. Change the number
* associated with ANNOTATE to change the amount of annotation produced by
* the program.
*/
/* Begin annotation definitions. */
#define ANNOTATE NONE
#define NONE 0
#define ON_STRANGE_BEHAVIOR 1
#define FULL 2
#endif /* spINSIDE_SPARSE */
#endif /* spCONFIG_DEFS */
@EOF
chmod 644 spConfig.h
echo x - spDefs.h
cat >spDefs.h <<'@EOF'
/*
* DATA STRUCTURE AND MACRO DEFINITIONS for Sparse.
*
* Author: Advising professor:
* Kenneth S. Kundert Alberto Sangiovanni-Vincentelli
* UC Berkeley
*
* This file contains common type definitions and macros for the sparse
* matrix routines. These definitions are of no interest to the user.
*/
/*
* 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.
*
* $Date: 88/06/18 11:13:40 $
* $Revision: 1.2 $
*/
/*
* IMPORTS
*/
#include
/*
* If running lint, change some of the compiler options to get a more
* complete inspection.
*/
#ifdef lint
#undef REAL
#undef spCOMPLEX
#undef EXPANDABLE
#undef TRANSLATE
#undef INITIALIZE
#undef DELETE
#undef STRIP
#undef MODIFIED_NODAL
#undef QUAD_ELEMENT
#undef TRANSPOSE
#undef SCALING
#undef DOCUMENTATION
#undef MULTIPLICATION
#undef DETERMINANT
#undef CONDITION
#undef PSEUDOCONDITION
#undef FORTRAN
#undef DEBUG
#define REAL YES
#define spCOMPLEX YES
#define EXPANDABLE YES
#define TRANSLATE YES
#define INITIALIZE YES
#define DELETE YES
#define STRIP YES
#define MODIFIED_NODAL YES
#define QUAD_ELEMENT YES
#define TRANSPOSE YES
#define SCALING YES
#define DOCUMENTATION YES
#define MULTIPLICATION YES
#define DETERMINANT YES
#define CONDITION YES
#define PSEUDOCONDITION YES
#define FORTRAN YES
#define DEBUG YES
#define LINT YES
#else /* not lint */
#define LINT NO
#endif /* not lint */
/*
* MACRO DEFINITIONS
*
* Macros are distinguished by using solely capital letters in their
* identifiers. This contrasts with C defined identifiers which are strictly
* lower case, and program variable and procedure names which use both upper
* and lower case.
*/
/* Begin macros. */
/* Boolean data type */
#define BOOLEAN int
#define NO 0
#define YES 1
#define NOT !
#define AND &&
#define OR ||
/* NULL pointer */
#ifndef NULL
#define NULL 0
#endif
#define SPARSE_ID 0x772773 /* Arbitrary (is Sparse on phone). */
#define IS_SPARSE(matrix) ((matrix) != NULL && \
(matrix)->ID == SPARSE_ID)
#define IS_VALID(matrix) ((matrix) != NULL && \
(matrix)->ID == SPARSE_ID && \
(matrix)->Error >= spOKAY && \
(matrix)->Error < spFATAL)
#define IS_FACTORED(matrix) ((matrix)->Factored && !(matrix)->NeedsOrdering)
/* Macro commands */
/* Macro functions that return the maximum or minimum independent of type. */
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
/* Macro function that returns the absolute value of a floating point number. */
#define ABS(a) ((a) < 0.0 ? -(a) : (a))
/* Macro function that returns the square of a number. */
#define SQR(a) ((a)*(a))
/* Macro procedure that swaps two entities. */
#define SWAP(type, a, b) {type swapx; swapx = a; a = b; b = swapx;}
/* Macro function that returns the approx absolute value of a complex number. */
#if spCOMPLEX
#define ELEMENT_MAG(ptr) (ABS((ptr)->Real) + ABS((ptr)->Imag))
#else
#define ELEMENT_MAG(ptr) ((ptr)->Real < 0.0 ? -(ptr)->Real : (ptr)->Real)
#endif
/* Complex assignment statements. */
#define CMPLX_ASSIGN(to,from) \
{ (to).Real = (from).Real; \
(to).Imag = (from).Imag; \
}
#define CMPLX_CONJ_ASSIGN(to,from) \
{ (to).Real = (from).Real; \
(to).Imag = -(from).Imag; \
}
#define CMPLX_NEGATE_ASSIGN(to,from) \
{ (to).Real = -(from).Real; \
(to).Imag = -(from).Imag; \
}
#define CMPLX_CONJ_NEGATE_ASSIGN(to,from) \
{ (to).Real = -(from).Real; \
(to).Imag = (from).Imag; \
}
#define CMPLX_CONJ(a) (a).Imag = -(a).Imag
#define CMPLX_NEGATE(a) \
{ (a).Real = -(a).Real; \
(a).Imag = -(a).Imag; \
}
/* Macro that returns the approx magnitude (L-1 norm) of a complex number. */
#define CMPLX_1_NORM(a) (ABS((a).Real) + ABS((a).Imag))
/* Macro that returns the approx magnitude (L-infinity norm) of a complex. */
#define CMPLX_INF_NORM(a) (MAX (ABS((a).Real),ABS((a).Imag)))
/* Macro function that returns the magnitude (L-2 norm) of a complex number. */
#define CMPLX_2_NORM(a) (sqrt((a).Real*(a).Real + (a).Imag*(a).Imag))
/* Macro function that performs complex addition. */
#define CMPLX_ADD(to,from_a,from_b) \
{ (to).Real = (from_a).Real + (from_b).Real; \
(to).Imag = (from_a).Imag + (from_b).Imag; \
}
/* Macro function that performs complex subtraction. */
#define CMPLX_SUBT(to,from_a,from_b) \
{ (to).Real = (from_a).Real - (from_b).Real; \
(to).Imag = (from_a).Imag - (from_b).Imag; \
}
/* Macro function that is equivalent to += operator for complex numbers. */
#define CMPLX_ADD_ASSIGN(to,from) \
{ (to).Real += (from).Real; \
(to).Imag += (from).Imag; \
}
/* Macro function that is equivalent to -= operator for complex numbers. */
#define CMPLX_SUBT_ASSIGN(to,from) \
{ (to).Real -= (from).Real; \
(to).Imag -= (from).Imag; \
}
/* Macro function that multiplies a complex number by a scalar. */
#define SCLR_MULT(to,sclr,cmplx) \
{ (to).Real = (sclr) * (cmplx).Real; \
(to).Imag = (sclr) * (cmplx).Imag; \
}
/* Macro function that multiply-assigns a complex number by a scalar. */
#define SCLR_MULT_ASSIGN(to,sclr) \
{ (to).Real *= (sclr); \
(to).Imag *= (sclr); \
}
/* Macro function that multiplies two complex numbers. */
#define CMPLX_MULT(to,from_a,from_b) \
{ (to).Real = (from_a).Real * (from_b).Real - \
(from_a).Imag * (from_b).Imag; \
(to).Imag = (from_a).Real * (from_b).Imag + \
(from_a).Imag * (from_b).Real; \
}
/* Macro function that implements to *= from for complex numbers. */
#define CMPLX_MULT_ASSIGN(to,from) \
{ RealNumber to_real_ = (to).Real; \
(to).Real = to_real_ * (from).Real - \
(to).Imag * (from).Imag; \
(to).Imag = to_real_ * (from).Imag + \
(to).Imag * (from).Real; \
}
/* Macro function that multiplies two complex numbers, the first of which is
* conjugated. */
#define CMPLX_CONJ_MULT(to,from_a,from_b) \
{ (to).Real = (from_a).Real * (from_b).Real + \
(from_a).Imag * (from_b).Imag; \
(to).Imag = (from_a).Real * (from_b).Imag - \
(from_a).Imag * (from_b).Real; \
}
/* Macro function that multiplies two complex numbers and then adds them
* to another. to = add + mult_a * mult_b */
#define CMPLX_MULT_ADD(to,mult_a,mult_b,add) \
{ (to).Real = (mult_a).Real * (mult_b).Real - \
(mult_a).Imag * (mult_b).Imag + (add).Real; \
(to).Imag = (mult_a).Real * (mult_b).Imag + \
(mult_a).Imag * (mult_b).Real + (add).Imag; \
}
/* Macro function that subtracts the product of two complex numbers from
* another. to = subt - mult_a * mult_b */
#define CMPLX_MULT_SUBT(to,mult_a,mult_b,subt) \
{ (to).Real = (subt).Real - (mult_a).Real * (mult_b).Real + \
(mult_a).Imag * (mult_b).Imag; \
(to).Imag = (subt).Imag - (mult_a).Real * (mult_b).Imag - \
(mult_a).Imag * (mult_b).Real; \
}
/* Macro function that multiplies two complex numbers and then adds them
* to another. to = add + mult_a* * mult_b where mult_a* represents mult_a
* conjugate. */
#define CMPLX_CONJ_MULT_ADD(to,mult_a,mult_b,add) \
{ (to).Real = (mult_a).Real * (mult_b).Real + \
(mult_a).Imag * (mult_b).Imag + (add).Real; \
(to).Imag = (mult_a).Real * (mult_b).Imag - \
(mult_a).Imag * (mult_b).Real + (add).Imag; \
}
/* Macro function that multiplies two complex numbers and then adds them
* to another. to += mult_a * mult_b */
#define CMPLX_MULT_ADD_ASSIGN(to,from_a,from_b) \
{ (to).Real += (from_a).Real * (from_b).Real - \
(from_a).Imag * (from_b).Imag; \
(to).Imag += (from_a).Real * (from_b).Imag + \
(from_a).Imag * (from_b).Real; \
}
/* Macro function that multiplies two complex numbers and then subtracts them
* from another. */
#define CMPLX_MULT_SUBT_ASSIGN(to,from_a,from_b) \
{ (to).Real -= (from_a).Real * (from_b).Real - \
(from_a).Imag * (from_b).Imag; \
(to).Imag -= (from_a).Real * (from_b).Imag + \
(from_a).Imag * (from_b).Real; \
}
/* Macro function that multiplies two complex numbers and then adds them
* to the destination. to += from_a* * from_b where from_a* represents from_a
* conjugate. */
#define CMPLX_CONJ_MULT_ADD_ASSIGN(to,from_a,from_b) \
{ (to).Real += (from_a).Real * (from_b).Real + \
(from_a).Imag * (from_b).Imag; \
(to).Imag += (from_a).Real * (from_b).Imag - \
(from_a).Imag * (from_b).Real; \
}
/* Macro function that multiplies two complex numbers and then subtracts them
* from the destination. to -= from_a* * from_b where from_a* represents from_a
* conjugate. */
#define CMPLX_CONJ_MULT_SUBT_ASSIGN(to,from_a,from_b) \
{ (to).Real -= (from_a).Real * (from_b).Real + \
(from_a).Imag * (from_b).Imag; \
(to).Imag -= (from_a).Real * (from_b).Imag - \
(from_a).Imag * (from_b).Real; \
}
/*
* Macro functions that provide complex division.
*/
/* Complex division: to = num / den */
#define CMPLX_DIV(to,num,den) \
{ RealNumber r_, s_; \
if (((den).Real >= (den).Imag AND (den).Real > -(den).Imag) OR \
((den).Real < (den).Imag AND (den).Real <= -(den).Imag)) \
{ r_ = (den).Imag / (den).Real; \
s_ = (den).Real + r_*(den).Imag; \
(to).Real = ((num).Real + r_*(num).Imag)/s_; \
(to).Imag = ((num).Imag - r_*(num).Real)/s_; \
} \
else \
{ r_ = (den).Real / (den).Imag; \
s_ = (den).Imag + r_*(den).Real; \
(to).Real = (r_*(num).Real + (num).Imag)/s_; \
(to).Imag = (r_*(num).Imag - (num).Real)/s_; \
} \
}
/* Complex division and assignment: num /= den */
#define CMPLX_DIV_ASSIGN(num,den) \
{ RealNumber r_, s_, t_; \
if (((den).Real >= (den).Imag AND (den).Real > -(den).Imag) OR \
((den).Real < (den).Imag AND (den).Real <= -(den).Imag)) \
{ r_ = (den).Imag / (den).Real; \
s_ = (den).Real + r_*(den).Imag; \
t_ = ((num).Real + r_*(num).Imag)/s_; \
(num).Imag = ((num).Imag - r_*(num).Real)/s_; \
(num).Real = t_; \
} \
else \
{ r_ = (den).Real / (den).Imag; \
s_ = (den).Imag + r_*(den).Real; \
t_ = (r_*(num).Real + (num).Imag)/s_; \
(num).Imag = (r_*(num).Imag - (num).Real)/s_; \
(num).Real = t_; \
} \
}
/* Complex reciprocation: to = 1.0 / den */
#define CMPLX_RECIPROCAL(to,den) \
{ RealNumber r_; \
if (((den).Real >= (den).Imag AND (den).Real > -(den).Imag) OR \
((den).Real < (den).Imag AND (den).Real <= -(den).Imag)) \
{ r_ = (den).Imag / (den).Real; \
(to).Imag = -r_*((to).Real = 1.0/((den).Real + r_*(den).Imag)); \
} \
else \
{ r_ = (den).Real / (den).Imag; \
(to).Real = -r_*((to).Imag = -1.0/((den).Imag + r_*(den).Real));\
} \
}
/*
* ASSERT and ABORT
*
* Macro used to assert that if the code is working correctly, then
* a condition must be true. If not, then execution is terminated
* and an error message is issued stating that there is an internal
* error and giving the file and line number. These assertions are
* not evaluated unless the DEBUG flag is true.
*/
#if DEBUG
#define ASSERT(condition) if (NOT(condition)) ABORT()
#else
#define ASSERT(condition)
#endif
#if DEBUG
#define ABORT() \
{ (void)fflush(stdout); \
(void)fprintf(stderr, "sparse: panic in file `%s' at line %d.\n", \
__FILE__, __LINE__); \
(void)fflush(stderr); \
abort(); \
}
#else
#define ABORT()
#endif
/*
* IMAGINARY VECTORS
*
* The imaginary vectors iRHS and iSolution are only needed when the
* options spCOMPLEX and spSEPARATED_COMPLEX_VECTORS are set. The following
* macro makes it easy to include or exclude these vectors as needed.
*/
#if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS
#define IMAG_VECTORS , iRHS, iSolution
#define IMAG_RHS , iRHS
#else
#define IMAG_VECTORS
#define IMAG_RHS
#endif
/*
* MEMORY ALLOCATION
*/
extern char *malloc(), *calloc(), *realloc();
#ifdef ultrix
extern void free();
extern void abort();
#else
extern free();
extern abort();
#endif
#define ALLOC(type,number) ((type *)malloc((unsigned)(sizeof(type)*(number))))
#define REALLOC(ptr,type,number) \
ptr = (type *)realloc((char *)ptr,(unsigned)(sizeof(type)*(number)))
#define FREE(ptr) { if ((ptr) != NULL) free((char *)(ptr)); (ptr) = NULL; }
/* Calloc that properly handles allocating a cleared vector. */
#define CALLOC(ptr,type,number) \
{ int i; ptr = ALLOC(type, number); \
if (ptr != (type *)NULL) \
for(i=(number)-1;i>=0; i--) ptr[i] = (type) 0; \
}
/*
* REAL NUMBER
*/
/* Begin `RealNumber'. */
typedef spREAL RealNumber, *RealVector;
/*
* COMPLEX NUMBER DATA STRUCTURE
*
* >>> Structure fields:
* Real (RealNumber)
* The real portion of the number. Real must be the first
* field in this structure.
* Imag (RealNumber)
* The imaginary portion of the number. This field must follow
* immediately after Real.
*/
/* Begin `ComplexNumber'. */
typedef struct
{ RealNumber Real;
RealNumber Imag;
} ComplexNumber, *ComplexVector;
/*
* MATRIX ELEMENT DATA STRUCTURE
*
* Every nonzero element in the matrix is stored in a dynamically allocated
* MatrixElement structure. These structures are linked together in an
* orthogonal linked list. Two different MatrixElement structures exist.
* One is used when only real matrices are expected, it is missing an entry
* for imaginary data. The other is used if complex matrices are expected.
* It contains an entry for imaginary data.
*
* >>> Structure fields:
* Real (RealNumber)
* The real portion of the value of the element. Real must be the first
* field in this structure.
* Imag (RealNumber)
* The imaginary portion of the value of the element. If the matrix
* routines are not compiled to handle complex matrices, then this
* field does not exist. If it exists, it must follow immediately after
* Real.
* Row (int)
* The row number of the element.
* Col (int)
* The column number of the element.
* NextInRow (struct MatrixElement *)
* NextInRow contains a pointer to the next element in the row to the
* right of this element. If this element is the last nonzero in the
* row then NextInRow contains NULL.
* NextInCol (struct MatrixElement *)
* NextInCol contains a pointer to the next element in the column below
* this element. If this element is the last nonzero in the column then
* NextInCol contains NULL.
* pInitInfo (char *)
* Pointer to user data used for initialization of the matrix element.
* Initialized to NULL.
*
* >>> Type definitions:
* ElementPtr
* A pointer to a MatrixElement.
* ArrayOfElementPtrs
* An array of ElementPtrs. Used for FirstInRow, FirstInCol and
* Diag pointer arrays.
*/
/* Begin `MatrixElement'. */
struct MatrixElement
{ RealNumber Real;
#if spCOMPLEX
RealNumber Imag;
#endif
int Row;
int Col;
struct MatrixElement *NextInRow;
struct MatrixElement *NextInCol;
#if INITIALIZE
char *pInitInfo;
#endif
};
typedef struct MatrixElement *ElementPtr;
typedef ElementPtr *ArrayOfElementPtrs;
/*
* ALLOCATION DATA STRUCTURE
*
* The sparse matrix routines keep track of all memory that is allocated by
* the operating system so the memory can later be freed. This is done by
* saving the pointers to all the chunks of memory that are allocated to a
* particular matrix in an allocation list. That list is organized as a
* linked list so that it can grow without a priori bounds.
*
* >>> Structure fields:
* AllocatedPtr (char *)
* Pointer to chunk of memory that has been allocated for the matrix.
* NextRecord (struct AllocationRecord *)
* Pointer to the next allocation record.
*/
/* Begin `AllocationRecord'. */
struct AllocationRecord
{ char *AllocatedPtr;
struct AllocationRecord *NextRecord;
};
typedef struct AllocationRecord *AllocationListPtr;
/*
* FILL-IN LIST DATA STRUCTURE
*
* The sparse matrix routines keep track of all fill-ins separately from
* user specified elements so they may be removed by spStripFills(). Fill-ins
* are allocated in bunched in what is called a fill-in lists. The data
* structure defined below is used to organize these fill-in lists into a
* linked-list.
*
* >>> Structure fields:
* pFillinList (ElementPtr)
* Pointer to a fill-in list, or a bunch of fill-ins arranged contiguously
* in memory.
* NumberOfFillinsInList (int)
* Seems pretty self explanatory to me.
* Next (struct FillinListNodeStruct *)
* Pointer to the next fill-in list structures.
*/
/* Begin `FillinListNodeStruct'. */
struct FillinListNodeStruct
{ ElementPtr pFillinList;
int NumberOfFillinsInList;
struct FillinListNodeStruct *Next;
};
/*
* MATRIX FRAME DATA STRUCTURE
*
* This structure contains all the pointers that support the orthogonal
* linked list that contains the matrix elements. Also included in this
* structure are other numbers and pointers that are used globally by the
* sparse matrix routines and are associated with one particular matrix.
*
* >>> Type definitions:
* MatrixPtr
* A pointer to MatrixFrame. Essentially, a pointer to the matrix.
*
* >>> Structure fields:
* AbsThreshold (RealNumber)
* The absolute magnitude an element must have to be considered as a
* pivot candidate, except as a last resort.
* AllocatedExtSize (int)
* The allocated size of the arrays used to translate external row and
* column numbers to their internal values.
* AllocatedSize (int)
* The currently allocated size of the matrix; the size the matrix can
* grow to when EXPANDABLE is set true and AllocatedSize is the largest
* the matrix can get without requiring that the matrix frame be
* reallocated.
* Complex (BOOLEAN)
* The flag which indicates whether the matrix is complex (true) or
* real.
* CurrentSize (int)
* This number is used during the building of the matrix when the
* TRANSLATE option is set true. It indicates the number of internal
* rows and columns that have elements in them.
* Diag (ArrayOfElementPtrs)
* Array of pointers that points to the diagonal elements.
* DoCmplxDirect (BOOLEAN *)
* Array of flags, one for each column in matrix. If a flag is true
* then corresponding column in a complex matrix should be eliminated
* in spFactor() using direct addressing (rather than indirect
* addressing).
* DoRealDirect (BOOLEAN *)
* Array of flags, one for each column in matrix. If a flag is true
* then corresponding column in a real matrix should be eliminated
* in spFactor() using direct addressing (rather than indirect
* addressing).
* Elements (int)
* The number of original elements (total elements minus fill ins)
* present in matrix.
* Error (int)
* The error status of the sparse matrix package.
* ExtSize (int)
* The value of the largest external row or column number encountered.
* ExtToIntColMap (int [])
* An array that is used to convert external columns number to internal
* external column numbers. Present only if TRANSLATE option is set true.
* ExtToIntRowMap (int [])
* An array that is used to convert external row numbers to internal
* external row numbers. Present only if TRANSLATE option is set true.
* Factored (BOOLEAN)
* Indicates if matrix has been factored. This flag is set true in
* spFactor() and spOrderAndFactor() and set false in spCreate()
* and spClear().
* Fillins (int)
* The number of fill-ins created during the factorization the matrix.
* FirstInCol (ArrayOfElementPtrs)
* Array of pointers that point to the first nonzero element of the
* column corresponding to the index.
* FirstInRow (ArrayOfElementPtrs)
* Array of pointers that point to the first nonzero element of the row
* corresponding to the index.
* ID (unsigned long int)
* A constant that provides the sparse data structure with a signature.
* When DEBUG is true, all externally available sparse routines check
* this signature to assure they are operating on a valid matrix.
* Intermediate (RealVector)
* Temporary storage used in the spSolve routines. Intermediate is an
* array used during forward and backward substitution. It is
* commonly called y when the forward and backward substitution process is
* denoted Ax = b => Ly = b and Ux = y.
* InternalVectorsAllocated (BOOLEAN)
* A flag that indicates whether the markowitz vectors and the
* Intermediate vector have been created.
* These vectors are created in CreateInternalVectors().
* IntToExtColMap (int [])
* An array that is used to convert internal column numbers to external
* external column numbers.
* IntToExtRowMap (int [])
* An array that is used to convert internal row numbers to external
* external row numbers.
* MarkowitzCol (int [])
* An array that contains the count of the non-zero elements excluding
* the pivots for each column. Used to generate and update MarkowitzProd.
* MarkowitzProd (long [])
* The array of the products of the Markowitz row and column counts. The
* element with the smallest product is the best pivot to use to maintain
* sparsity.
* MarkowitzRow (int [])
* An array that contains the count of the non-zero elements excluding
* the pivots for each row. Used to generate and update MarkowitzProd.
* MaxRowCountInLowerTri (int)
* The maximum number of off-diagonal element in the rows of L, the
* lower triangular matrix. This quantity is used when computing an
* estimate of the roundoff error in the matrix.
* NeedsOrdering (BOOLEAN)
* This is a flag that signifies that the matrix needs to be ordered
* or reordered. NeedsOrdering is set true in spCreate() and
* spGetElement() or spGetAdmittance() if new elements are added to the
* matrix after it has been previously factored. It is set false in
* spOrderAndFactor().
* NumberOfInterchangesIsOdd (BOOLEAN)
* Flag that indicates the sum of row and column interchange counts
* is an odd number. Used when determining the sign of the determinant.
* Partitioned (BOOLEAN)
* This flag indicates that the columns of the matrix have been
* partitioned into two groups. Those that will be addressed directly
* and those that will be addressed indirectly in spFactor().
* PivotsOriginalCol (int)
* Column pivot was chosen from.
* PivotsOriginalRow (int)
* Row pivot was chosen from.
* PivotSelectionMethod (char)
* Character that indicates which pivot search method was successful.
* PreviousMatrixWasComplex (BOOLEAN)
* This flag in needed to determine how to clear the matrix. When
* dealing with real matrices, it is important that the imaginary terms
* in the matrix elements be zero. Thus, if the previous matrix was
* complex, then the current matrix will be cleared as if it were complex
* even if it is real.
* RelThreshold (RealNumber)
* The magnitude an element must have relative to others in its row
* to be considered as a pivot candidate, except as a last resort.
* Reordered (BOOLEAN)
* This flag signifies that the matrix has been reordered. It
* is cleared in spCreate(), set in spMNA_Preorder() and
* spOrderAndFactor() and is used in spPrint().
* RowsLinked (BOOLEAN)
* A flag that indicates whether the row pointers exist. The AddByIndex
* routines do not generate the row pointers, which are needed by some
* of the other routines, such as spOrderAndFactor() and spScale().
* The row pointers are generated in the function spcLinkRows().
* SingularCol (int)
* Normally zero, but if matrix is found to be singular, SingularCol is
* assigned the external column number of pivot that was zero.
* SingularRow (int)
* Normally zero, but if matrix is found to be singular, SingularRow is
* assigned the external row number of pivot that was zero.
* Singletons (int)
* The number of singletons available for pivoting. Note that if row I
* and column I both contain singletons, only one of them is counted.
* Size (int)
* Number of rows and columns in the matrix. Does not change as matrix
* is factored.
* TrashCan (MatrixElement)
* This is a dummy MatrixElement that is used to by the user to stuff
* data related to the zero row or column. In other words, when the user
* adds an element in row zero or column zero, then the matrix returns
* a pointer to TrashCan. In this way the user can have a uniform way
* data into the matrix independent of whether a component is connected
* to ground.
*
* >>> The remaining fields are related to memory allocation.
* TopOfAllocationList (AllocationListPtr)
* Pointer which points to the top entry in a list. The list contains
* all the pointers to the segments of memory that have been allocated
* to this matrix. This is used when the memory is to be freed on
* deallocation of the matrix.
* RecordsRemaining (int)
* Number of slots left in the list of allocations.
* NextAvailElement (ElementPtr)
* Pointer to the next available element which has been allocated but as
* yet is unused. Matrix elements are allocated in groups of
* ELEMENTS_PER_ALLOCATION in order to speed element allocation and
* freeing.
* ElementsRemaining (int)
* Number of unused elements left in last block of elements allocated.
* NextAvailFillin (ElementPtr)
* Pointer to the next available fill-in which has been allocated but
* as yet is unused. Fill-ins are allocated in a group in order to keep
* them physically close in memory to the rest of the matrix.
* FillinsRemaining (int)
* Number of unused fill-ins left in the last block of fill-ins
* allocated.
* FirstFillinListNode (FillinListNodeStruct *)
* A pointer to the head of the linked-list that keeps track of the
* lists of fill-ins.
* LastFillinListNode (FillinListNodeStruct *)
* A pointer to the tail of the linked-list that keeps track of the
* lists of fill-ins.
*/
/* Begin `MatrixFrame'. */
struct MatrixFrame
{ RealNumber AbsThreshold;
int AllocatedSize;
int AllocatedExtSize;
BOOLEAN Complex;
int CurrentSize;
ArrayOfElementPtrs Diag;
BOOLEAN *DoCmplxDirect;
BOOLEAN *DoRealDirect;
int Elements;
int Error;
int ExtSize;
int *ExtToIntColMap;
int *ExtToIntRowMap;
BOOLEAN Factored;
int Fillins;
ArrayOfElementPtrs FirstInCol;
ArrayOfElementPtrs FirstInRow;
unsigned long ID;
RealVector Intermediate;
BOOLEAN InternalVectorsAllocated;
int *IntToExtColMap;
int *IntToExtRowMap;
int *MarkowitzRow;
int *MarkowitzCol;
long *MarkowitzProd;
int MaxRowCountInLowerTri;
BOOLEAN NeedsOrdering;
BOOLEAN NumberOfInterchangesIsOdd;
BOOLEAN Partitioned;
int PivotsOriginalCol;
int PivotsOriginalRow;
char PivotSelectionMethod;
BOOLEAN PreviousMatrixWasComplex;
RealNumber RelThreshold;
BOOLEAN Reordered;
BOOLEAN RowsLinked;
int SingularCol;
int SingularRow;
int Singletons;
int Size;
struct MatrixElement TrashCan;
AllocationListPtr TopOfAllocationList;
int RecordsRemaining;
ElementPtr NextAvailElement;
int ElementsRemaining;
ElementPtr NextAvailFillin;
int FillinsRemaining;
struct FillinListNodeStruct *FirstFillinListNode;
struct FillinListNodeStruct *LastFillinListNode;
};
typedef struct MatrixFrame *MatrixPtr;
@EOF
chmod 644 spDefs.h
echo x - spFactor.c
cat >spFactor.c <<'@EOF'
/*
* 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 CreateInternalVectors
* 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
* 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.
*/
#ifndef lint
static char copyright[] =
"Sparse1.3: Copyright (c) 1985,86,87,88 by Kenneth S. Kundert";
static char RCSid[] =
"@(#)$Header: spFactor.c,v 1.2 88/06/18 11:14:10 kundert Exp $";
#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 "spConfig.h"
#include "spMatrix.h"
#include "spDefs.h"
/*
* 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 (char *)
* Pointer to matrix.
* RHS (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 (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 (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 (BOOLEAN)
* 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 (BOOLEAN)
* Flag that indicates whether reordering is required.
*
* >>> Possible errors:
* spNO_MEMORY
* spSINGULAR
* spSMALL_PIVOT
* Error is cleared in this function.
*/
int
spOrderAndFactor( eMatrix, RHS, RelThreshold, AbsThreshold, DiagPivoting )
char *eMatrix;
RealNumber RHS[], RelThreshold, AbsThreshold;
BOOLEAN DiagPivoting;
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
ElementPtr pPivot;
int Step, Size, ReorderingRequired;
ElementPtr SearchForPivot();
RealNumber LargestInCol, FindLargestInCol();
/* Begin `spOrderAndFactor'. */
ASSERT( IS_VALID(Matrix) AND NOT 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 (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 (Matrix->Complex)
ComplexRowColElimination( Matrix, pPivot );
else
RealRowColElimination( Matrix, pPivot );
}
else
{ ReorderingRequired = YES;
break; /* for loop */
}
}
if (NOT 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(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)
CreateInternalVectors( 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 (char *)
* Pointer to matrix.
*
* >>> Possible errors:
* spNO_MEMORY
* spSINGULAR
* spZERO_DIAG
* spSMALL_PIVOT
* Error is cleared in this function.
*/
int
spFactor( eMatrix )
char *eMatrix;
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
register ElementPtr pElement;
register ElementPtr pColumn;
register int Step, Size;
RealNumber Mult;
/* Begin `spFactor'. */
ASSERT( IS_VALID(Matrix) AND NOT Matrix->Factored);
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;
/* Start factorization. */
for (Step = 2; Step <= Size; Step++)
{ if (Matrix->DoRealDirect[Step])
{ /* Update column using direct addressing scatter-gather. */
register 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];
pElement = pElement->NextInCol;
}
/* Check for singular matrix. */
if (Dest[Step] == 0.0) return ZeroPivot( Matrix, Step );
Matrix->Diag[Step]->Real = 1.0 / Dest[Step];
}
else
{ /* Update column using indirect addressing scatter-gather. */
register RealNumber **pDest = (RealNumber **)Matrix->Intermediate;
/* Scatter. */
pElement = Matrix->FirstInCol[Step];
while (pElement != NULL)
{ pDest[pElement->Row] = &pElement->Real;
pElement = pElement->NextInCol;
}
/* Update column. */
pColumn = Matrix->FirstInCol[Step];
while (pColumn->Row < Step)
{ pElement = Matrix->Diag[pColumn->Row];
Mult = (*pDest[pColumn->Row] *= pElement->Real);
while ((pElement = pElement->NextInCol) != NULL)
*pDest[pElement->Row] -= Mult * pElement->Real;
pColumn = pColumn->NextInCol;
}
/* Check for singular matrix. */
if (Matrix->Diag[Step]->Real == 0.0)
return ZeroPivot( Matrix, Step );
Matrix->Diag[Step]->Real = 1.0 / Matrix->Diag[Step]->Real;
}
}
Matrix->Factored = YES;
return (Matrix->Error = spOKAY);
#endif /* REAL */
}
#if spCOMPLEX
/*
* FACTOR COMPLEX MATRIX
*
* This routine is the companion routine to spFactor(), it
* handles complex matrices. It is otherwise identical.
*
* >>> Returned:
* The error code is returned. Possible errors are listed below.
*
* >>> Arguments:
* Matrix (char *)
* Pointer to matrix.
*
* >>> Possible errors:
* spSINGULAR
* Error is cleared in this function.
*/
static int
FactorComplexMatrix( Matrix )
MatrixPtr Matrix;
{
register ElementPtr pElement;
register ElementPtr pColumn;
register int Step, Size;
ComplexNumber Mult, Pivot;
/* Begin `FactorComplexMatrix'. */
ASSERT(Matrix->Complex);
Size = Matrix->Size;
pElement = Matrix->Diag[1];
if (ELEMENT_MAG(pElement) == 0.0) return ZeroPivot( Matrix, 1 );
/* Cmplx expr: *pPivot = 1.0 / *pPivot. */
CMPLX_RECIPROCAL( *pElement, *pElement );
/* Start factorization. */
for (Step = 2; Step <= Size; Step++)
{ if (Matrix->DoCmplxDirect[Step])
{ /* Update column using direct addressing scatter-gather. */
register ComplexNumber *Dest;
Dest = (ComplexNumber *)Matrix->Intermediate;
/* Scatter. */
pElement = Matrix->FirstInCol[Step];
while (pElement != NULL)
{ Dest[pElement->Row] = *(ComplexNumber *)pElement;
pElement = pElement->NextInCol;
}
/* Update column. */
pColumn = Matrix->FirstInCol[Step];
while (pColumn->Row < Step)
{ pElement = Matrix->Diag[pColumn->Row];
/* Cmplx expr: Mult = Dest[pColumn->Row] * (1.0 / *pPivot). */
CMPLX_MULT(Mult, Dest[pColumn->Row], *pElement);
CMPLX_ASSIGN(*pColumn, Mult);
while ((pElement = pElement->NextInCol) != NULL)
{ /* Cmplx expr: Dest[pElement->Row] -= Mult * pElement */
CMPLX_MULT_SUBT_ASSIGN(Dest[pElement->Row],Mult,*pElement);
}
pColumn = pColumn->NextInCol;
}
/* Gather. */
pElement = Matrix->Diag[Step]->NextInCol;
while (pElement != NULL)
{ *(ComplexNumber *)pElement = Dest[pElement->Row];
pElement = pElement->NextInCol;
}
/* Check for singular matrix. */
Pivot = Dest[Step];
if (CMPLX_1_NORM(Pivot) == 0.0) return ZeroPivot( Matrix, Step );
CMPLX_RECIPROCAL( *Matrix->Diag[Step], Pivot );
}
else
{ /* Update column using direct addressing scatter-gather. */
register ComplexNumber **pDest;
pDest = (ComplexNumber **)Matrix->Intermediate;
/* Scatter. */
pElement = Matrix->FirstInCol[Step];
while (pElement != NULL)
{ pDest[pElement->Row] = (ComplexNumber *)pElement;
pElement = pElement->NextInCol;
}
/* Update column. */
pColumn = Matrix->FirstInCol[Step];
while (pColumn->Row < Step)
{ pElement = Matrix->Diag[pColumn->Row];
/* Cmplx expr: Mult = *pDest[pColumn->Row] * (1.0 / *pPivot). */
CMPLX_MULT(Mult, *pDest[pColumn->Row], *pElement);
CMPLX_ASSIGN(*pDest[pColumn->Row], Mult);
while ((pElement = pElement->NextInCol) != NULL)
{ /* Cmplx expr: *pDest[pElement->Row] -= Mult * pElement */
CMPLX_MULT_SUBT_ASSIGN(*pDest[pElement->Row],Mult,*pElement);
}
pColumn = pColumn->NextInCol;
}
/* Check for singular matrix. */
pElement = Matrix->Diag[Step];
if (ELEMENT_MAG(pElement) == 0.0) return ZeroPivot( Matrix, Step );
CMPLX_RECIPROCAL( *pElement, *pElement );
}
}
Matrix->Factored = YES;
return (Matrix->Error = spOKAY);
}
#endif /* spCOMPLEX */
/*
* PARTITION MATRIX
*
* This routine determines the cost to factor each row using both
* direct and indirect addressing and decides, on a row-by-row basis,
* which addressing mode is fastest. This information is used in
* spFactor() to speed the factorization.
*
* When factoring a previously ordered matrix using spFactor(), Sparse
* operates on a row-at-a-time basis. For speed, on each step, the
* row being updated is copied into a full vector and the operations
* are performed on that vector. This can be done one of two ways,
* either using direct addressing or indirect addressing. Direct
* addressing is fastest when the matrix is relatively dense and
* indirect addressing is best when the matrix is quite sparse. The
* user selects the type of partition used with Mode. If Mode is set
* to spDIRECT_PARTITION, then the all rows are placed in the direct
* addressing partition. Similarly, if Mode is set to
* spINDIRECT_PARTITION, then the all rows are placed in the indirect
* addressing partition. By setting Mode to spAUTO_PARTITION, the
* user allows Sparse to select the partition for each row
* individually. spFactor() generally runs faster if Sparse is
* allowed to choose its own partitioning, however choosing a
* partition is expensive. The time required to choose a partition is
* of the same order of the cost to factor the matrix. If you plan to
* factor a large number of matrices with the same structure, it is
* best to let Sparse choose the partition. Otherwise, you should
* choose the partition based on the predicted density of the matrix.
*
* >>> Arguments:
* Matrix (char *)
* Pointer to matrix.
* Mode (int)
* Mode must be one of three special codes: spDIRECT_PARTITION,
* spINDIRECT_PARTITION, or spAUTO_PARTITION.
*/
void
spPartition( eMatrix, Mode )
char *eMatrix;
int Mode;
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
register ElementPtr pElement, pColumn;
register int Step, Size;
register int *Nc, *No, *Nm;
BOOLEAN *DoRealDirect, *DoCmplxDirect;
/* Begin `spPartition'. */
ASSERT( IS_SPARSE( Matrix ) );
if (Matrix->Partitioned) return;
Size = Matrix->Size;
DoRealDirect = Matrix->DoRealDirect;
DoCmplxDirect = Matrix->DoCmplxDirect;
Matrix->Partitioned = YES;
/* If partition is specified by the user, this is easy. */
if (Mode == spDEFAULT_PARTITION) Mode = DEFAULT_PARTITION;
if (Mode == spDIRECT_PARTITION)
{ for (Step = 1; Step <= Size; Step++)
#if REAL
DoRealDirect[Step] = YES;
#endif
#if spCOMPLEX
DoCmplxDirect[Step] = YES;
#endif
return;
}
else if (Mode == spINDIRECT_PARTITION)
{ for (Step = 1; Step <= Size; Step++)
#if REAL
DoRealDirect[Step] = NO;
#endif
#if spCOMPLEX
DoCmplxDirect[Step] = NO;
#endif
return;
}
else ASSERT( Mode == spAUTO_PARTITION );
/* Otherwise, count all operations needed in when factoring matrix. */
Nc = (int *)Matrix->MarkowitzRow;
No = (int *)Matrix->MarkowitzCol;
Nm = (int *)Matrix->MarkowitzProd;
/* Start mock-factorization. */
for (Step = 1; Step <= Size; Step++)
{ Nc[Step] = No[Step] = Nm[Step] = 0;
pElement = Matrix->FirstInCol[Step];
while (pElement != NULL)
{ Nc[Step]++;
pElement = pElement->NextInCol;
}
pColumn = Matrix->FirstInCol[Step];
while (pColumn->Row < Step)
{ pElement = Matrix->Diag[pColumn->Row];
Nm[Step]++;
while ((pElement = pElement->NextInCol) != NULL)
No[Step]++;
pColumn = pColumn->NextInCol;
}
}
for (Step = 1; Step <= Size; Step++)
{
/*
* The following are just estimates based on a count on the number of
* machine instructions used on each machine to perform the various
* tasks. It was assumed that each machine instruction required the
* same amount of time (I don't believe this is true for the VAX, and
* have no idea if this is true for the 68000 family). For optimum
* performance, these numbers should be tuned to the machine.
* Nc is the number of nonzero elements in the column.
* Nm is the number of multipliers in the column.
* No is the number of operations in the inner loop.
*/
#define generic
#ifdef hp9000s300
#if REAL
DoRealDirect[Step] = (Nm[Step] + No[Step] > 3*Nc[Step] - 2*Nm[Step]);
#endif
#if spCOMPLEX
/* On the hp350, it is never profitable to use direct for complex. */
DoCmplxDirect[Step] = NO;
#endif
#undef generic
#endif
#ifdef vax
#if REAL
DoRealDirect[Step] = (Nm[Step] + No[Step] > 3*Nc[Step] - 2*Nm[Step]);
#endif
#if spCOMPLEX
DoCmplxDirect[Step] = (Nm[Step] + No[Step] > 7*Nc[Step] - 4*Nm[Step]);
#endif
#undef generic
#endif
#ifdef generic
#if REAL
DoRealDirect[Step] = (Nm[Step] + No[Step] > 3*Nc[Step] - 2*Nm[Step]);
#endif
#if spCOMPLEX
DoCmplxDirect[Step] = (Nm[Step] + No[Step] > 7*Nc[Step] - 4*Nm[Step]);
#endif
#undef generic
#endif
}
#if (ANNOTATE == FULL)
{ int Ops = 0;
for (Step = 1; Step <= Size; Step++)
Ops += No[Step];
printf("Operation count for inner loop of factorization = %d.\n", Ops);
}
#endif
return;
}
/*
* CREATE INTERNAL VECTORS
*
* Creates the Markowitz and Intermediate vectors.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to matrix.
*
* >>> Local variables:
* SizePlusOne (unsigned)
* Size of the arrays to be allocated.
*
* >>> Possible errors:
* spNO_MEMORY
*/
static
CreateInternalVectors( Matrix )
MatrixPtr Matrix;
{
int Size;
/* Begin `CreateInternalVectors'. */
/* Create Markowitz arrays. */
Size= Matrix->Size;
if (Matrix->MarkowitzRow == NULL)
{ if (( Matrix->MarkowitzRow = ALLOC(int, Size+1)) == NULL)
Matrix->Error = spNO_MEMORY;
}
if (Matrix->MarkowitzCol == NULL)
{ if (( Matrix->MarkowitzCol = ALLOC(int, Size+1)) == NULL)
Matrix->Error = spNO_MEMORY;
}
if (Matrix->MarkowitzProd == NULL)
{ if (( Matrix->MarkowitzProd = ALLOC(long, Size+2)) == NULL)
Matrix->Error = spNO_MEMORY;
}
/* Create DoDirect vectors for use in spFactor(). */
#if REAL
if (Matrix->DoRealDirect == NULL)
{ if (( Matrix->DoRealDirect = ALLOC(BOOLEAN, Size+1)) == NULL)
Matrix->Error = spNO_MEMORY;
}
#endif
#if spCOMPLEX
if (Matrix->DoCmplxDirect == NULL)
{ if (( Matrix->DoCmplxDirect = ALLOC(BOOLEAN, Size+1)) == NULL)
Matrix->Error = spNO_MEMORY;
}
#endif
/* Create Intermediate vectors for use in MatrixSolve. */
#if spCOMPLEX
if (Matrix->Intermediate == NULL)
{ if ((Matrix->Intermediate = ALLOC(RealNumber,2*(Size+1))) == NULL)
Matrix->Error = spNO_MEMORY;
}
#else
if (Matrix->Intermediate == NULL)
{ if ((Matrix->Intermediate = ALLOC(RealNumber, Size+1)) == NULL)
Matrix->Error = spNO_MEMORY;
}
#endif
if (Matrix->Error != spNO_MEMORY)
Matrix->InternalVectorsAllocated = YES;
return;
}
/*
* COUNT MARKOWITZ
*
* Scans Matrix to determine the Markowitz counts for each row and column.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to matrix.
* RHS (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.
* Step (int)
* Index of the diagonal currently being eliminated.
*
* >>> Local variables:
* Count (int)
* Temporary counting variable.
* ExtRow (int)
* The external row number that corresponds to I.
* pElement (ElementPtr)
* Pointer to matrix elements.
* Size (int)
* The size of the matrix.
*/
static
CountMarkowitz( Matrix, RHS, Step )
MatrixPtr Matrix;
register RealVector RHS;
int Step;
{
register int Count, I, Size = Matrix->Size;
register ElementPtr pElement;
int ExtRow;
/* Begin `CountMarkowitz'. */
/* Correct array pointer for ARRAY_OFFSET. */
#if NOT ARRAY_OFFSET
#if spSEPARATED_COMPLEX_VECTORS OR NOT spCOMPLEX
if (RHS != NULL) --RHS;
#else
if (RHS != NULL)
{ if (Matrix->Complex) RHS -= 2;
else --RHS;
}
#endif
#endif
/* Generate MarkowitzRow Count for each row. */
for (I = Step; I <= Size; I++)
{
/* Set Count to -1 initially to remove count due to pivot element. */
Count = -1;
pElement = Matrix->FirstInRow[I];
while (pElement != NULL AND pElement->Col < Step)
pElement = pElement->NextInRow;
while (pElement != NULL)
{ Count++;
pElement = pElement->NextInRow;
}
/* Include nonzero elements in the RHS vector. */
ExtRow = Matrix->IntToExtRowMap[I];
#if spSEPARATED_COMPLEX_VECTORS OR NOT spCOMPLEX
if (RHS != NULL)
if (RHS[ExtRow] != 0.0) Count++;
#else
if (RHS != NULL)
{ if (Matrix->Complex)
{ if ((RHS[2*ExtRow] != 0.0) OR (RHS[2*ExtRow+1] != 0.0))
Count++;
}
else if (RHS[I] != 0.0) Count++;
}
#endif
Matrix->MarkowitzRow[I] = Count;
}
/* Generate the MarkowitzCol count for each column. */
for (I = Step; I <= Size; I++)
{
/* Set Count to -1 initially to remove count due to pivot element. */
Count = -1;
pElement = Matrix->FirstInCol[I];
while (pElement != NULL AND pElement->Row < Step)
pElement = pElement->NextInCol;
while (pElement != NULL)
{ Count++;
pElement = pElement->NextInCol;
}
Matrix->MarkowitzCol[I] = Count;
}
return;
}
/*
* MARKOWITZ PRODUCTS
*
* Calculates MarkowitzProduct for each diagonal element from the Markowitz
* counts.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to matrix.
* Step (int)
* Index of the diagonal currently being eliminated.
*
* >>> Local Variables:
* pMarkowitzProduct (long *)
* Pointer that points into MarkowitzProduct array. Is used to
* sequentially access entries quickly.
* pMarkowitzRow (int *)
* Pointer that points into MarkowitzRow array. Is used to sequentially
* access entries quickly.
* pMarkowitzCol (int *)
* Pointer that points into MarkowitzCol array. Is used to sequentially
* access entries quickly.
* Product (long)
* Temporary storage for Markowitz product./
* Size (int)
* The size of the matrix.
*/
static
MarkowitzProducts( Matrix, Step )
MatrixPtr Matrix;
int Step;
{
register int I, *pMarkowitzRow, *pMarkowitzCol;
register long Product, *pMarkowitzProduct;
register int Size = Matrix->Size;
double fProduct;
/* Begin `MarkowitzProducts'. */
Matrix->Singletons = 0;
pMarkowitzProduct = &(Matrix->MarkowitzProd[Step]);
pMarkowitzRow = &(Matrix->MarkowitzRow[Step]);
pMarkowitzCol = &(Matrix->MarkowitzCol[Step]);
for (I = Step; I <= Size; I++)
{
/* If chance of overflow, use real numbers. */
if ((*pMarkowitzRow > LARGEST_SHORT_INTEGER AND *pMarkowitzCol != 0) OR
(*pMarkowitzCol > LARGEST_SHORT_INTEGER AND *pMarkowitzRow != 0))
{ fProduct = (double)(*pMarkowitzRow++) * (double)(*pMarkowitzCol++);
if (fProduct >= LARGEST_LONG_INTEGER)
*pMarkowitzProduct++ = LARGEST_LONG_INTEGER;
else
*pMarkowitzProduct++ = fProduct;
}
else
{ Product = *pMarkowitzRow++ * *pMarkowitzCol++;
if ((*pMarkowitzProduct++ = Product) == 0)
Matrix->Singletons++;
}
}
return;
}
/*
* SEARCH FOR BEST PIVOT
*
* Performs a search to determine the element with the lowest Markowitz
* Product that is also acceptable. An acceptable element is one that is
* larger than the AbsThreshold and at least as large as RelThreshold times
* the largest element in the same column. The first step is to look for
* singletons if any exist. If none are found, then all the diagonals are
* searched. The diagonal is searched once quickly using the assumption that
* elements on the diagonal are large compared to other elements in their
* column, and so the pivot can be chosen only on the basis of the Markowitz
* criterion. After a element has been chosen to be pivot on the basis of
* its Markowitz product, it is checked to see if it is large enough.
* Waiting to the end of the Markowitz search to check the size of a pivot
* candidate saves considerable time, but is not guaranteed to find an
* acceptable pivot. Thus if unsuccessful a second pass of the diagonal is
* made. This second pass checks to see if an element is large enough during
* the search, not after it. If still no acceptable pivot candidate has
* been found, the search expands to cover the entire matrix.
*
* >>> Returned:
* A pointer to the element chosen to be pivot. If every element in the
* matrix is zero, then NULL is returned.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to matrix.
* Step (int)
* The row and column number of the beginning of the reduced submatrix.
*
* >>> Local variables:
* ChosenPivot (ElementPtr)
* Pointer to element that has been chosen to be the pivot.
*
* >>> Possible errors:
* spSINGULAR
* spSMALL_PIVOT
*/
static ElementPtr
SearchForPivot( Matrix, Step, DiagPivoting )
MatrixPtr Matrix;
int Step, DiagPivoting;
{
register ElementPtr ChosenPivot;
ElementPtr SearchForSingleton();
ElementPtr QuicklySearchDiagonal();
ElementPtr SearchDiagonal();
ElementPtr SearchEntireMatrix();
/* Begin `SearchForPivot'. */
/* If singletons exist, look for an acceptable one to use as pivot. */
if (Matrix->Singletons)
{ ChosenPivot = SearchForSingleton( Matrix, Step );
if (ChosenPivot != NULL)
{ Matrix->PivotSelectionMethod = 's';
return ChosenPivot;
}
}
#if DIAGONAL_PIVOTING
if (DiagPivoting)
{
/*
* Either no singletons exist or they weren't acceptable. Take quick first
* pass at searching diagonal. First search for element on diagonal of
* remaining submatrix with smallest Markowitz product, then check to see
* if it okay numerically. If not, QuicklySearchDiagonal fails.
*/
ChosenPivot = QuicklySearchDiagonal( Matrix, Step );
if (ChosenPivot != NULL)
{ Matrix->PivotSelectionMethod = 'q';
return ChosenPivot;
}
/*
* Quick search of diagonal failed, carefully search diagonal and check each
* pivot candidate numerically before even tentatively accepting it.
*/
ChosenPivot = SearchDiagonal( Matrix, Step );
if (ChosenPivot != NULL)
{ Matrix->PivotSelectionMethod = 'd';
return ChosenPivot;
}
}
#endif /* DIAGONAL_PIVOTING */
/* No acceptable pivot found yet, search entire matrix. */
ChosenPivot = SearchEntireMatrix( Matrix, Step );
Matrix->PivotSelectionMethod = 'e';
return ChosenPivot;
}
/*
* SEARCH FOR SINGLETON TO USE AS PIVOT
*
* Performs a search to find a singleton to use as the pivot. The
* first acceptable singleton is used. A singleton is acceptable if
* it is larger in magnitude than the AbsThreshold and larger
* than RelThreshold times the largest of any other elements in the same
* column. It may seem that a singleton need not satisfy the
* relative threshold criterion, however it is necessary to prevent
* excessive growth in the RHS from resulting in overflow during the
* forward and backward substitution. A singleton does not need to
* be on the diagonal to be selected.
*
* >>> Returned:
* A pointer to the singleton chosen to be pivot. In no singleton is
* acceptable, return NULL.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to matrix.
* Step (int)
* Index of the diagonal currently being eliminated.
*
* >>> Local variables:
* ChosenPivot (ElementPtr)
* Pointer to element that has been chosen to be the pivot.
* PivotMag (RealNumber)
* Magnitude of ChosenPivot.
* Singletons (int)
* The count of the number of singletons that can be used as pivots.
* A local version of Matrix->Singletons.
* pMarkowitzProduct (long *)
* Pointer that points into MarkowitzProduct array. It is used to quickly
* access successive Markowitz products.
*/
static ElementPtr
SearchForSingleton( Matrix, Step )
MatrixPtr Matrix;
int Step;
{
register ElementPtr ChosenPivot;
register int I;
register long *pMarkowitzProduct;
int Singletons;
RealNumber PivotMag, FindBiggestInColExclude();
/* Begin `SearchForSingleton'. */
/* Initialize pointer that is to scan through MarkowitzProduct vector. */
pMarkowitzProduct = &(Matrix->MarkowitzProd[Matrix->Size+1]);
Matrix->MarkowitzProd[Matrix->Size+1] = Matrix->MarkowitzProd[Step];
/* Decrement the count of available singletons, on the assumption that an
* acceptable one will be found. */
Singletons = Matrix->Singletons--;
/*
* Assure that following while loop will always terminate, this is just
* preventive medicine, if things are working right this should never
* be needed.
*/
Matrix->MarkowitzProd[Step-1] = 0;
while (Singletons-- > 0)
{
/* Singletons exist, find them. */
/*
* This is tricky. Am using a pointer to sequentially step through the
* MarkowitzProduct array. Search terminates when singleton (Product = 0)
* is found. Note that the conditional in the while statement
* ( *pMarkowitzProduct ) is true as long as the MarkowitzProduct is not
* equal to zero. The row (and column) index on the diagonal is then
* calculated by subtracting the pointer to the Markowitz product of
* the first diagonal from the pointer to the Markowitz product of the
* desired element, the singleton.
*
* Search proceeds from the end (high row and column numbers) to the
* beginning (low row and column numbers) so that rows and columns with
* large Markowitz products will tend to be move to the bottom of the
* matrix. However, choosing Diag[Step] is desirable because it would
* require no row and column interchanges, so inspect it first by
* putting its Markowitz product at the end of the MarkowitzProd
* vector.
*/
while ( *pMarkowitzProduct-- )
{ /*
* N bottles of beer on the wall;
* N bottles of beer.
* you take one down and pass it around;
* N-1 bottles of beer on the wall.
*/
}
I = pMarkowitzProduct - Matrix->MarkowitzProd + 1;
/* Assure that I is valid. */
if (I < Step) break; /* while (Singletons-- > 0) */
if (I > Matrix->Size) I = Step;
/* Singleton has been found in either/both row or/and column I. */
if ((ChosenPivot = Matrix->Diag[I]) != NULL)
{
/* Singleton lies on the diagonal. */
PivotMag = ELEMENT_MAG(ChosenPivot);
if
( PivotMag > Matrix->AbsThreshold AND
PivotMag > Matrix->RelThreshold *
FindBiggestInColExclude( Matrix, ChosenPivot, Step )
) return ChosenPivot;
}
else
{
/* Singleton does not lie on diagonal, find it. */
if (Matrix->MarkowitzCol[I] == 0)
{ ChosenPivot = Matrix->FirstInCol[I];
while ((ChosenPivot != NULL) AND (ChosenPivot->Row < Step))
ChosenPivot = ChosenPivot->NextInCol;
PivotMag = ELEMENT_MAG(ChosenPivot);
if
( PivotMag > Matrix->AbsThreshold AND
PivotMag > Matrix->RelThreshold *
FindBiggestInColExclude( Matrix, ChosenPivot,
Step )
) return ChosenPivot;
else
{ if (Matrix->MarkowitzRow[I] == 0)
{ ChosenPivot = Matrix->FirstInRow[I];
while((ChosenPivot != NULL) AND (ChosenPivot->ColNextInRow;
PivotMag = ELEMENT_MAG(ChosenPivot);
if
( PivotMag > Matrix->AbsThreshold AND
PivotMag > Matrix->RelThreshold *
FindBiggestInColExclude( Matrix,
ChosenPivot,
Step )
) return ChosenPivot;
}
}
}
else
{ ChosenPivot = Matrix->FirstInRow[I];
while ((ChosenPivot != NULL) AND (ChosenPivot->Col < Step))
ChosenPivot = ChosenPivot->NextInRow;
PivotMag = ELEMENT_MAG(ChosenPivot);
if
( PivotMag > Matrix->AbsThreshold AND
PivotMag > Matrix->RelThreshold *
FindBiggestInColExclude( Matrix, ChosenPivot,
Step )
) return ChosenPivot;
}
}
/* Singleton not acceptable (too small), try another. */
} /* end of while(lSingletons>0) */
/*
* All singletons were unacceptable. Restore Matrix->Singletons count.
* Initial assumption that an acceptable singleton would be found was wrong.
*/
Matrix->Singletons++;
return NULL;
}
#if DIAGONAL_PIVOTING
#if MODIFIED_MARKOWITZ
/*
* QUICK SEARCH OF DIAGONAL FOR PIVOT WITH MODIFIED MARKOWITZ CRITERION
*
* Searches the diagonal looking for the best pivot. For a pivot to be
* acceptable it must be larger than the pivot RelThreshold times the largest
* element in its reduced column. Among the acceptable diagonals, the
* one with the smallest MarkowitzProduct is sought. Search terminates
* early if a diagonal is found with a MarkowitzProduct of one and its
* magnitude is larger than the other elements in its row and column.
* Since its MarkowitzProduct is one, there is only one other element in
* both its row and column, and, as a condition for early termination,
* these elements must be located symmetricly in the matrix. If a tie
* occurs between elements of equal MarkowitzProduct, then the element with
* the largest ratio between its magnitude and the largest element in its
* column is used. The search will be terminated after a given number of
* ties have occurred and the best (largest ratio) of the tied element will
* be used as the pivot. The number of ties that will trigger an early
* termination is MinMarkowitzProduct * TIES_MULTIPLIER.
*
* >>> Returned:
* A pointer to the diagonal element chosen to be pivot. If no diagonal is
* acceptable, a NULL is returned.
*
* >>> Arguments:
* Step (int)
* Index of the diagonal currently being eliminated.
*
* >>> Local variables:
* ChosenPivot (ElementPtr)
* Pointer to the element that has been chosen to be the pivot.
* LargestOffDiagonal (RealNumber)
* Magnitude of the largest of the off-diagonal terms associated with
* a diagonal with MarkowitzProduct equal to one.
* Magnitude (RealNumber)
* Absolute value of diagonal element.
* MaxRatio (RealNumber)
* Among the elements tied with the smallest Markowitz product, MaxRatio
* is the best (smallest) ratio of LargestInCol to the diagonal Magnitude
* found so far. The smaller the ratio, the better numerically the
* element will be as pivot.
* MinMarkowitzProduct (long)
* Smallest Markowitz product found of pivot candidates that lie along
* diagonal.
* NumberOfTies (int)
* A count of the number of Markowitz ties that have occurred at current
* MarkowitzProduct.
* pDiag (ElementPtr)
* Pointer to current diagonal element.
* pMarkowitzProduct (long *)
* Pointer that points into MarkowitzProduct array. It is used to quickly
* access successive Markowitz products.
* Ratio (RealNumber)
* For the current pivot candidate, Ratio is the ratio of the largest
* element in its column (excluding itself) to its magnitude.
* TiedElements (ElementPtr[])
* Array of pointers to the elements with the minimum Markowitz
* product.
* pOtherInCol (ElementPtr)
* When there is only one other element in a column other than the
* diagonal, pOtherInCol is used to point to it. Used when Markowitz
* product is to determine if off diagonals are placed symmetricly.
* pOtherInRow (ElementPtr)
* When there is only one other element in a row other than the diagonal,
* pOtherInRow is used to point to it. Used when Markowitz product is
* to determine if off diagonals are placed symmetricly.
*/
static ElementPtr
QuicklySearchDiagonal( Matrix, Step )
MatrixPtr Matrix;
int Step;
{
register long MinMarkowitzProduct, *pMarkowitzProduct;
register ElementPtr pDiag, pOtherInRow, pOtherInCol;
int I, NumberOfTies;
ElementPtr ChosenPivot, TiedElements[MAX_MARKOWITZ_TIES + 1];
RealNumber Magnitude, LargestInCol, Ratio, MaxRatio;
RealNumber LargestOffDiagonal;
RealNumber FindBiggestInColExclude();
/* Begin `QuicklySearchDiagonal'. */
NumberOfTies = -1;
MinMarkowitzProduct = LARGEST_LONG_INTEGER;
pMarkowitzProduct = &(Matrix->MarkowitzProd[Matrix->Size+2]);
Matrix->MarkowitzProd[Matrix->Size+1] = Matrix->MarkowitzProd[Step];
/* Assure that following while loop will always terminate. */
Matrix->MarkowitzProd[Step-1] = -1;
/*
* This is tricky. Am using a pointer in the inner while loop to
* sequentially step through the MarkowitzProduct array. Search
* terminates when the Markowitz product of zero placed at location
* Step-1 is found. The row (and column) index on the diagonal is then
* calculated by subtracting the pointer to the Markowitz product of
* the first diagonal from the pointer to the Markowitz product of the
* desired element. The outer for loop is infinite, broken by using
* break.
*
* Search proceeds from the end (high row and column numbers) to the
* beginning (low row and column numbers) so that rows and columns with
* large Markowitz products will tend to be move to the bottom of the
* matrix. However, choosing Diag[Step] is desirable because it would
* require no row and column interchanges, so inspect it first by
* putting its Markowitz product at the end of the MarkowitzProd
* vector.
*/
for(;;) /* Endless for loop. */
{ while (MinMarkowitzProduct < *(--pMarkowitzProduct))
{ /*
* N bottles of beer on the wall;
* N bottles of beer.
* You take one down and pass it around;
* N-1 bottles of beer on the wall.
*/
}
I = pMarkowitzProduct - Matrix->MarkowitzProd;
/* Assure that I is valid; if I < Step, terminate search. */
if (I < Step) break; /* Endless for loop */
if (I > Matrix->Size) I = Step;
if ((pDiag = Matrix->Diag[I]) == NULL)
continue; /* Endless for loop */
if ((Magnitude = ELEMENT_MAG(pDiag)) <= Matrix->AbsThreshold)
continue; /* Endless for loop */
if (*pMarkowitzProduct == 1)
{
/* Case where only one element exists in row and column other than diagonal. */
/* Find off diagonal elements. */
pOtherInRow = pDiag->NextInRow;
pOtherInCol = pDiag->NextInCol;
if (pOtherInRow == NULL AND pOtherInCol == NULL)
{ pOtherInRow = Matrix->FirstInRow[I];
while(pOtherInRow != NULL)
{ if (pOtherInRow->Col >= Step AND pOtherInRow->Col != I)
break;
pOtherInRow = pOtherInRow->NextInRow;
}
pOtherInCol = Matrix->FirstInCol[I];
while(pOtherInCol != NULL)
{ if (pOtherInCol->Row >= Step AND pOtherInCol->Row != I)
break;
pOtherInCol = pOtherInCol->NextInCol;
}
}
/* Accept diagonal as pivot if diagonal is larger than off diagonals and the
* off diagonals are placed symmetricly. */
if (pOtherInRow != NULL AND pOtherInCol != NULL)
{ if (pOtherInRow->Col == pOtherInCol->Row)
{ LargestOffDiagonal = MAX(ELEMENT_MAG(pOtherInRow),
ELEMENT_MAG(pOtherInCol));
if (Magnitude >= LargestOffDiagonal)
{
/* Accept pivot, it is unlikely to contribute excess error. */
return pDiag;
}
}
}
}
if (*pMarkowitzProduct < MinMarkowitzProduct)
{
/* Notice strict inequality in test. This is a new smallest MarkowitzProduct. */
TiedElements[0] = pDiag;
MinMarkowitzProduct = *pMarkowitzProduct;
NumberOfTies = 0;
}
else
{
/* This case handles Markowitz ties. */
if (NumberOfTies < MAX_MARKOWITZ_TIES)
{ TiedElements[++NumberOfTies] = pDiag;
if (NumberOfTies >= MinMarkowitzProduct * TIES_MULTIPLIER)
break; /* Endless for loop */
}
}
} /* End of endless for loop. */
/* Test to see if any element was chosen as a pivot candidate. */
if (NumberOfTies < 0)
return NULL;
/* Determine which of tied elements is best numerically. */
ChosenPivot = NULL;
MaxRatio = 1.0 / Matrix->RelThreshold;
for (I = 0; I <= NumberOfTies; I++)
{ pDiag = TiedElements[I];
Magnitude = ELEMENT_MAG(pDiag);
LargestInCol = FindBiggestInColExclude( Matrix, pDiag, Step );
Ratio = LargestInCol / Magnitude;
if (Ratio < MaxRatio)
{ ChosenPivot = pDiag;
MaxRatio = Ratio;
}
}
return ChosenPivot;
}
#else /* Not MODIFIED_MARKOWITZ */
/*
* QUICK SEARCH OF DIAGONAL FOR PIVOT WITH CONVENTIONAL MARKOWITZ
* CRITERION
*
* Searches the diagonal looking for the best pivot. For a pivot to be
* acceptable it must be larger than the pivot RelThreshold times the largest
* element in its reduced column. Among the acceptable diagonals, the
* one with the smallest MarkowitzProduct is sought. Search terminates
* early if a diagonal is found with a MarkowitzProduct of one and its
* magnitude is larger than the other elements in its row and column.
* Since its MarkowitzProduct is one, there is only one other element in
* both its row and column, and, as a condition for early termination,
* these elements must be located symmetricly in the matrix.
*
* >>> Returned:
* A pointer to the diagonal element chosen to be pivot. If no diagonal is
* acceptable, a NULL is returned.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to matrix.
* Step (int)
* Index of the diagonal currently being eliminated.
*
* >>> Local variables:
* ChosenPivot (ElementPtr)
* Pointer to the element that has been chosen to be the pivot.
* LargestOffDiagonal (RealNumber)
* Magnitude of the largest of the off-diagonal terms associated with
* a diagonal with MarkowitzProduct equal to one.
* Magnitude (RealNumber)
* Absolute value of diagonal element.
* MinMarkowitzProduct (long)
* Smallest Markowitz product found of pivot candidates which are
* acceptable.
* pDiag (ElementPtr)
* Pointer to current diagonal element.
* pMarkowitzProduct (long *)
* Pointer that points into MarkowitzProduct array. It is used to quickly
* access successive Markowitz products.
* pOtherInCol (ElementPtr)
* When there is only one other element in a column other than the
* diagonal, pOtherInCol is used to point to it. Used when Markowitz
* product is to determine if off diagonals are placed symmetricly.
* pOtherInRow (ElementPtr)
* When there is only one other element in a row other than the diagonal,
* pOtherInRow is used to point to it. Used when Markowitz product is
* to determine if off diagonals are placed symmetricly.
*/
static ElementPtr
QuicklySearchDiagonal( Matrix, Step )
MatrixPtr Matrix;
int Step;
{
register long MinMarkowitzProduct, *pMarkowitzProduct;
register ElementPtr pDiag;
int I;
ElementPtr ChosenPivot, pOtherInRow, pOtherInCol;
RealNumber Magnitude, LargestInCol, LargestOffDiagonal;
RealNumber FindBiggestInColExclude();
/* Begin `QuicklySearchDiagonal'. */
ChosenPivot = NULL;
MinMarkowitzProduct = LARGEST_LONG_INTEGER;
pMarkowitzProduct = &(Matrix->MarkowitzProd[Matrix->Size+2]);
Matrix->MarkowitzProd[Matrix->Size+1] = Matrix->MarkowitzProd[Step];
/* Assure that following while loop will always terminate. */
Matrix->MarkowitzProd[Step-1] = -1;
/*
* This is tricky. Am using a pointer in the inner while loop to
* sequentially step through the MarkowitzProduct array. Search
* terminates when the Markowitz product of zero placed at location
* Step-1 is found. The row (and column) index on the diagonal is then
* calculated by subtracting the pointer to the Markowitz product of
* the first diagonal from the pointer to the Markowitz product of the
* desired element. The outer for loop is infinite, broken by using
* break.
*
* Search proceeds from the end (high row and column numbers) to the
* beginning (low row and column numbers) so that rows and columns with
* large Markowitz products will tend to be move to the bottom of the
* matrix. However, choosing Diag[Step] is desirable because it would
* require no row and column interchanges, so inspect it first by
* putting its Markowitz product at the end of the MarkowitzProd
* vector.
*/
for (;;) /* Endless for loop. */
{ while (*(--pMarkowitzProduct) >= MinMarkowitzProduct)
{ /* Just passing through. */
}
I = pMarkowitzProduct - Matrix->MarkowitzProd;
/* Assure that I is valid; if I < Step, terminate search. */
if (I < Step) break; /* Endless for loop */
if (I > Matrix->Size) I = Step;
if ((pDiag = Matrix->Diag[I]) == NULL)
continue; /* Endless for loop */
if ((Magnitude = ELEMENT_MAG(pDiag)) <= Matrix->AbsThreshold)
continue; /* Endless for loop */
if (*pMarkowitzProduct == 1)
{
/* Case where only one element exists in row and column other than diagonal. */
/* Find off-diagonal elements. */
pOtherInRow = pDiag->NextInRow;
pOtherInCol = pDiag->NextInCol;
if (pOtherInRow == NULL AND pOtherInCol == NULL)
{ pOtherInRow = Matrix->FirstInRow[I];
while(pOtherInRow != NULL)
{ if (pOtherInRow->Col >= Step AND pOtherInRow->Col != I)
break;
pOtherInRow = pOtherInRow->NextInRow;
}
pOtherInCol = Matrix->FirstInCol[I];
while(pOtherInCol != NULL)
{ if (pOtherInCol->Row >= Step AND pOtherInCol->Row != I)
break;
pOtherInCol = pOtherInCol->NextInCol;
}
}
/* Accept diagonal as pivot if diagonal is larger than off-diagonals and the
* off-diagonals are placed symmetricly. */
if (pOtherInRow != NULL AND pOtherInCol != NULL)
{ if (pOtherInRow->Col == pOtherInCol->Row)
{ LargestOffDiagonal = MAX(ELEMENT_MAG(pOtherInRow),
ELEMENT_MAG(pOtherInCol));
if (Magnitude >= LargestOffDiagonal)
{
/* Accept pivot, it is unlikely to contribute excess error. */
return pDiag;
}
}
}
}
MinMarkowitzProduct = *pMarkowitzProduct;
ChosenPivot = pDiag;
} /* End of endless for loop. */
if (ChosenPivot != NULL)
{ LargestInCol = FindBiggestInColExclude( Matrix, ChosenPivot, Step );
if( ELEMENT_MAG(ChosenPivot) <= Matrix->RelThreshold * LargestInCol )
ChosenPivot = NULL;
}
return ChosenPivot;
}
#endif /* Not MODIFIED_MARKOWITZ */
/*
* SEARCH DIAGONAL FOR PIVOT WITH MODIFIED MARKOWITZ CRITERION
*
* Searches the diagonal looking for the best pivot. For a pivot to be
* acceptable it must be larger than the pivot RelThreshold times the largest
* element in its reduced column. Among the acceptable diagonals, the
* one with the smallest MarkowitzProduct is sought. If a tie occurs
* between elements of equal MarkowitzProduct, then the element with
* the largest ratio between its magnitude and the largest element in its
* column is used. The search will be terminated after a given number of
* ties have occurred and the best (smallest ratio) of the tied element will
* be used as the pivot. The number of ties that will trigger an early
* termination is MinMarkowitzProduct * TIES_MULTIPLIER.
*
* >>> Returned:
* A pointer to the diagonal element chosen to be pivot. If no diagonal is
* acceptable, a NULL is returned.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to matrix.
* Step (int)
* Index of the diagonal currently being eliminated.
*
* >>> Local variables:
* ChosenPivot (ElementPtr)
* Pointer to the element that has been chosen to be the pivot.
* Size (int)
* Local version of size which is placed in a register to increase speed.
* Magnitude (RealNumber)
* Absolute value of diagonal element.
* MinMarkowitzProduct (long)
* Smallest Markowitz product found of those pivot candidates which are
* acceptable.
* NumberOfTies (int)
* A count of the number of Markowitz ties that have occurred at current
* MarkowitzProduct.
* pDiag (ElementPtr)
* Pointer to current diagonal element.
* pMarkowitzProduct (long*)
* Pointer that points into MarkowitzProduct array. It is used to quickly
* access successive Markowitz products.
* Ratio (RealNumber)
* For the current pivot candidate, Ratio is the
* Ratio of the largest element in its column to its magnitude.
* RatioOfAccepted (RealNumber)
* For the best pivot candidate found so far, RatioOfAccepted is the
* Ratio of the largest element in its column to its magnitude.
*/
static ElementPtr
SearchDiagonal( Matrix, Step )
MatrixPtr Matrix;
register int Step;
{
register int J;
register long MinMarkowitzProduct, *pMarkowitzProduct;
register int I;
register ElementPtr pDiag;
int NumberOfTies, Size = Matrix->Size;
ElementPtr ChosenPivot;
RealNumber Magnitude, Ratio, RatioOfAccepted, LargestInCol;
RealNumber FindBiggestInColExclude();
/* Begin `SearchDiagonal'. */
ChosenPivot = NULL;
MinMarkowitzProduct = LARGEST_LONG_INTEGER;
pMarkowitzProduct = &(Matrix->MarkowitzProd[Size+2]);
Matrix->MarkowitzProd[Size+1] = Matrix->MarkowitzProd[Step];
/* Start search of diagonal. */
for (J = Size+1; J > Step; J--)
{
if (*(--pMarkowitzProduct) > MinMarkowitzProduct)
continue; /* for loop */
if (J > Matrix->Size)
I = Step;
else
I = J;
if ((pDiag = Matrix->Diag[I]) == NULL)
continue; /* for loop */
if ((Magnitude = ELEMENT_MAG(pDiag)) <= Matrix->AbsThreshold)
continue; /* for loop */
/* Test to see if diagonal's magnitude is acceptable. */
LargestInCol = FindBiggestInColExclude( Matrix, pDiag, Step );
if (Magnitude <= Matrix->RelThreshold * LargestInCol)
continue; /* for loop */
if (*pMarkowitzProduct < MinMarkowitzProduct)
{
/* Notice strict inequality in test. This is a new smallest MarkowitzProduct. */
ChosenPivot = pDiag;
MinMarkowitzProduct = *pMarkowitzProduct;
RatioOfAccepted = LargestInCol / Magnitude;
NumberOfTies = 0;
}
else
{
/* This case handles Markowitz ties. */
NumberOfTies++;
Ratio = LargestInCol / Magnitude;
if (Ratio < RatioOfAccepted)
{ ChosenPivot = pDiag;
RatioOfAccepted = Ratio;
}
if (NumberOfTies >= MinMarkowitzProduct * TIES_MULTIPLIER)
return ChosenPivot;
}
} /* End of for(Step) */
return ChosenPivot;
}
#endif /* DIAGONAL_PIVOTING */
/*
* SEARCH ENTIRE MATRIX FOR BEST PIVOT
*
* Performs a search over the entire matrix looking for the acceptable
* element with the lowest MarkowitzProduct. If there are several that
* are tied for the smallest MarkowitzProduct, the tie is broken by using
* the ratio of the magnitude of the element being considered to the largest
* element in the same column. If no element is acceptable then the largest
* element in the reduced submatrix is used as the pivot and the
* matrix is declared to be spSMALL_PIVOT. If the largest element is
* zero, the matrix is declared to be spSINGULAR.
*
* >>> Returned:
* A pointer to the diagonal element chosen to be pivot. If no element is
* found, then NULL is returned and the matrix is spSINGULAR.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to matrix.
* Step (int)
* Index of the diagonal currently being eliminated.
*
* >>> Local variables:
* ChosenPivot (ElementPtr)
* Pointer to the element that has been chosen to be the pivot.
* LargestElementMag (RealNumber)
* Magnitude of the largest element yet found in the reduced submatrix.
* Size (int)
* Local version of Size; placed in a register for speed.
* Magnitude (RealNumber)
* Absolute value of diagonal element.
* MinMarkowitzProduct (long)
* Smallest Markowitz product found of pivot candidates which are
* acceptable.
* NumberOfTies (int)
* A count of the number of Markowitz ties that have occurred at current
* MarkowitzProduct.
* pElement (ElementPtr)
* Pointer to current element.
* pLargestElement (ElementPtr)
* Pointer to the largest element yet found in the reduced submatrix.
* Product (long)
* Markowitz product for the current row and column.
* Ratio (RealNumber)
* For the current pivot candidate, Ratio is the
* Ratio of the largest element in its column to its magnitude.
* RatioOfAccepted (RealNumber)
* For the best pivot candidate found so far, RatioOfAccepted is the
* Ratio of the largest element in its column to its magnitude.
*
* >>> Possible errors:
* spSINGULAR
* spSMALL_PIVOT
*/
static ElementPtr
SearchEntireMatrix( Matrix, Step )
MatrixPtr Matrix;
int Step;
{
register int I, Size = Matrix->Size;
register ElementPtr pElement;
int NumberOfTies;
long Product, MinMarkowitzProduct;
ElementPtr ChosenPivot, pLargestElement;
RealNumber Magnitude, LargestElementMag, Ratio, RatioOfAccepted, LargestInCol;
RealNumber FindLargestInCol();
/* Begin `SearchEntireMatrix'. */
ChosenPivot = NULL;
LargestElementMag = 0.0;
MinMarkowitzProduct = LARGEST_LONG_INTEGER;
/* Start search of matrix on column by column basis. */
for (I = Step; I <= Size; I++)
{ pElement = Matrix->FirstInCol[I];
while (pElement != NULL AND pElement->Row < Step)
pElement = pElement->NextInCol;
if((LargestInCol = FindLargestInCol(pElement)) == 0.0)
continue; /* for loop */
while (pElement != NULL)
{
/* Check to see if element is the largest encountered so far. If so, record
its magnitude and address. */
if ((Magnitude = ELEMENT_MAG(pElement)) > LargestElementMag)
{ LargestElementMag = Magnitude;
pLargestElement = pElement;
}
/* Calculate element's MarkowitzProduct. */
Product = Matrix->MarkowitzRow[pElement->Row] *
Matrix->MarkowitzCol[pElement->Col];
/* Test to see if element is acceptable as a pivot candidate. */
if ((Product <= MinMarkowitzProduct) AND
(Magnitude > Matrix->RelThreshold * LargestInCol) AND
(Magnitude > Matrix->AbsThreshold))
{
/* Test to see if element has lowest MarkowitzProduct yet found, or whether it
is tied with an element found earlier. */
if (Product < MinMarkowitzProduct)
{
/* Notice strict inequality in test. This is a new smallest MarkowitzProduct. */
ChosenPivot = pElement;
MinMarkowitzProduct = Product;
RatioOfAccepted = LargestInCol / Magnitude;
NumberOfTies = 0;
}
else
{
/* This case handles Markowitz ties. */
NumberOfTies++;
Ratio = LargestInCol / Magnitude;
if (Ratio < RatioOfAccepted)
{ ChosenPivot = pElement;
RatioOfAccepted = Ratio;
}
if (NumberOfTies >= MinMarkowitzProduct * TIES_MULTIPLIER)
return ChosenPivot;
}
}
pElement = pElement->NextInCol;
} /* End of while(pElement != NULL) */
} /* End of for(Step) */
if (ChosenPivot != NULL) return ChosenPivot;
if (LargestElementMag == 0.0)
{ Matrix->Error = spSINGULAR;
return NULL;
}
Matrix->Error = spSMALL_PIVOT;
return pLargestElement;
}
/*
* DETERMINE THE MAGNITUDE OF THE LARGEST ELEMENT IN A COLUMN
*
* This routine searches a column and returns the magnitude of the largest
* element. This routine begins the search at the element pointed to by
* pElement, the parameter.
*
* The search is conducted by starting at the element specified by a pointer,
* which should be one below the diagonal, and moving down the column. On
* the way down the column, the magnitudes of the elements are tested to see
* if they are the largest yet found.
*
* >>> Returned:
* The magnitude of the largest element in the column below and including
* the one pointed to by the input parameter.
*
* >>> Arguments:
* pElement (ElementPtr)
* The pointer to the first element to be tested. Also, used by the
* routine to access all lower elements in the column.
*
* >>> Local variables:
* Largest (RealNumber)
* The magnitude of the largest element.
* Magnitude (RealNumber)
* The magnitude of the currently active element.
*/
static RealNumber
FindLargestInCol( pElement )
register ElementPtr pElement;
{
RealNumber Magnitude, Largest = 0.0;
/* Begin `FindLargestInCol'. */
/* Search column for largest element beginning at Element. */
while (pElement != NULL)
{ if ((Magnitude = ELEMENT_MAG(pElement)) > Largest)
Largest = Magnitude;
pElement = pElement->NextInCol;
}
return Largest;
}
/*
* DETERMINE THE MAGNITUDE OF THE LARGEST ELEMENT IN A COLUMN
* EXCLUDING AN ELEMENT
*
* This routine searches a column and returns the magnitude of the largest
* element. One given element is specifically excluded from the search.
*
* The search is conducted by starting at the first element in the column
* and moving down the column until the active part of the matrix is entered,
* i.e. the reduced submatrix. The rest of the column is then traversed
* looking for the largest element.
*
* >>> Returned:
* The magnitude of the largest element in the active portion of the column,
* excluding the specified element, is returned.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* pElement (ElementPtr)
* The pointer to the element that is to be excluded from search. Column
* to be searched is one that contains this element. Also used to
* access the elements in the column.
* Step (int)
* Index of the diagonal currently being eliminated. Indicates where
* the active part of the matrix begins.
*
* >>> Local variables:
* Col (int)
* The number of the column to be searched. Also the column number of
* the element to be avoided in the search.
* Largest (RealNumber)
* The magnitude of the largest element.
* Magnitude (RealNumber)
* The magnitude of the currently active element.
* Row (int)
* The row number of element to be excluded from the search.
*/
static RealNumber
FindBiggestInColExclude( Matrix, pElement, Step )
MatrixPtr Matrix;
register ElementPtr pElement;
register int Step;
{
register int Row;
int Col;
RealNumber Largest, Magnitude;
/* Begin `FindBiggestInColExclude'. */
Row = pElement->Row;
Col = pElement->Col;
pElement = Matrix->FirstInCol[Col];
/* Travel down column until reduced submatrix is entered. */
while ((pElement != NULL) AND (pElement->Row < Step))
pElement = pElement->NextInCol;
/* Initialize the variable Largest. */
if (pElement->Row != Row)
Largest = ELEMENT_MAG(pElement);
else
Largest = 0.0;
/* Search rest of column for largest element, avoiding excluded element. */
while ((pElement = pElement->NextInCol) != NULL)
{ if ((Magnitude = ELEMENT_MAG(pElement)) > Largest)
{ if (pElement->Row != Row)
Largest = Magnitude;
}
}
return Largest;
}
/*
* EXCHANGE ROWS AND COLUMNS
*
* Exchanges two rows and two columns so that the selected pivot is moved to
* the upper left corner of the remaining submatrix.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* pPivot (ElementPtr)
* Pointer to the current pivot.
* Step (int)
* Index of the diagonal currently being eliminated.
*
* >>> Local variables:
* Col (int)
* Column where the pivot was found.
* Row (int)
* Row where the pivot was found.
* OldMarkowitzProd_Col (long)
* Markowitz product associated with the diagonal element in the row
* the pivot was found in.
* OldMarkowitzProd_Row (long)
* Markowitz product associated with the diagonal element in the column
* the pivot was found in.
* OldMarkowitzProd_Step (long)
* Markowitz product associated with the diagonal element that is being
* moved so that the pivot can be placed in the upper left-hand corner
* of the reduced submatrix.
*/
static
ExchangeRowsAndCols( Matrix, pPivot, Step )
MatrixPtr Matrix;
ElementPtr pPivot;
register int Step;
{
register int Row, Col;
long OldMarkowitzProd_Step, OldMarkowitzProd_Row, OldMarkowitzProd_Col;
ElementPtr spcFindElementInCol();
/* Begin `ExchangeRowsAndCols'. */
Row = pPivot->Row;
Col = pPivot->Col;
Matrix->PivotsOriginalRow = Row;
Matrix->PivotsOriginalCol = Col;
if ((Row == Step) AND (Col == Step)) return;
/* Exchange rows and columns. */
if (Row == Col)
{ spcRowExchange( Matrix, Step, Row );
spcColExchange( Matrix, Step, Col );
SWAP( long, Matrix->MarkowitzProd[Step], Matrix->MarkowitzProd[Row] );
SWAP( ElementPtr, Matrix->Diag[Row], Matrix->Diag[Step] );
}
else
{
/* Initialize variables that hold old Markowitz products. */
OldMarkowitzProd_Step = Matrix->MarkowitzProd[Step];
OldMarkowitzProd_Row = Matrix->MarkowitzProd[Row];
OldMarkowitzProd_Col = Matrix->MarkowitzProd[Col];
/* Exchange rows. */
if (Row != Step)
{ spcRowExchange( Matrix, Step, Row );
Matrix->NumberOfInterchangesIsOdd =
NOT Matrix->NumberOfInterchangesIsOdd;
Matrix->MarkowitzProd[Row] = Matrix->MarkowitzRow[Row] *
Matrix->MarkowitzCol[Row];
/* Update singleton count. */
if ((Matrix->MarkowitzProd[Row]==0) != (OldMarkowitzProd_Row==0))
{ if (OldMarkowitzProd_Row == 0)
Matrix->Singletons--;
else
Matrix->Singletons++;
}
}
/* Exchange columns. */
if (Col != Step)
{ spcColExchange( Matrix, Step, Col );
Matrix->NumberOfInterchangesIsOdd =
NOT Matrix->NumberOfInterchangesIsOdd;
Matrix->MarkowitzProd[Col] = Matrix->MarkowitzCol[Col] *
Matrix->MarkowitzRow[Col];
/* Update singleton count. */
if ((Matrix->MarkowitzProd[Col]==0) != (OldMarkowitzProd_Col==0))
{ if (OldMarkowitzProd_Col == 0)
Matrix->Singletons--;
else
Matrix->Singletons++;
}
Matrix->Diag[Col] = spcFindElementInCol( Matrix,
Matrix->FirstInCol+Col,
Col, Col, NO );
}
if (Row != Step)
{ Matrix->Diag[Row] = spcFindElementInCol( Matrix,
Matrix->FirstInCol+Row,
Row, Row, NO );
}
Matrix->Diag[Step] = spcFindElementInCol( Matrix,
Matrix->FirstInCol+Step,
Step, Step, NO );
/* Update singleton count. */
Matrix->MarkowitzProd[Step] = Matrix->MarkowitzCol[Step] *
Matrix->MarkowitzRow[Step];
if ((Matrix->MarkowitzProd[Step]==0) != (OldMarkowitzProd_Step==0))
{ if (OldMarkowitzProd_Step == 0)
Matrix->Singletons--;
else
Matrix->Singletons++;
}
}
return;
}
/*
* EXCHANGE ROWS
*
* Performs all required operations to exchange two rows. Those operations
* include: swap FirstInRow pointers, fixing up the NextInCol pointers,
* swapping row indexes in MatrixElements, and swapping Markowitz row
* counts.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* Row1 (int)
* Row index of one of the rows, becomes the smallest index.
* Row2 (int)
* Row index of the other row, becomes the largest index.
*
* Local variables:
* Column (int)
* Column in which row elements are currently being exchanged.
* Row1Ptr (ElementPtr)
* Pointer to an element in Row1.
* Row2Ptr (ElementPtr)
* Pointer to an element in Row2.
* Element1 (ElementPtr)
* Pointer to the element in Row1 to be exchanged.
* Element2 (ElementPtr)
* Pointer to the element in Row2 to be exchanged.
*/
spcRowExchange( Matrix, Row1, Row2 )
MatrixPtr Matrix;
int Row1, Row2;
{
register ElementPtr Row1Ptr, Row2Ptr;
int Column;
ElementPtr Element1, Element2;
/* Begin `spcRowExchange'. */
if (Row1 > Row2) SWAP(int, Row1, Row2);
Row1Ptr = Matrix->FirstInRow[Row1];
Row2Ptr = Matrix->FirstInRow[Row2];
while (Row1Ptr != NULL OR Row2Ptr != NULL)
{
/* Exchange elements in rows while traveling from left to right. */
if (Row1Ptr == NULL)
{ Column = Row2Ptr->Col;
Element1 = NULL;
Element2 = Row2Ptr;
Row2Ptr = Row2Ptr->NextInRow;
}
else if (Row2Ptr == NULL)
{ Column = Row1Ptr->Col;
Element1 = Row1Ptr;
Element2 = NULL;
Row1Ptr = Row1Ptr->NextInRow;
}
else if (Row1Ptr->Col < Row2Ptr->Col)
{ Column = Row1Ptr->Col;
Element1 = Row1Ptr;
Element2 = NULL;
Row1Ptr = Row1Ptr->NextInRow;
}
else if (Row1Ptr->Col > Row2Ptr->Col)
{ Column = Row2Ptr->Col;
Element1 = NULL;
Element2 = Row2Ptr;
Row2Ptr = Row2Ptr->NextInRow;
}
else /* Row1Ptr->Col == Row2Ptr->Col */
{ Column = Row1Ptr->Col;
Element1 = Row1Ptr;
Element2 = Row2Ptr;
Row1Ptr = Row1Ptr->NextInRow;
Row2Ptr = Row2Ptr->NextInRow;
}
ExchangeColElements( Matrix, Row1, Element1, Row2, Element2, Column);
} /* end of while(Row1Ptr != NULL OR Row2Ptr != NULL) */
if (Matrix->InternalVectorsAllocated)
SWAP( int, Matrix->MarkowitzRow[Row1], Matrix->MarkowitzRow[Row2]);
SWAP( ElementPtr, Matrix->FirstInRow[Row1], Matrix->FirstInRow[Row2]);
SWAP( int, Matrix->IntToExtRowMap[Row1], Matrix->IntToExtRowMap[Row2]);
#if TRANSLATE
Matrix->ExtToIntRowMap[ Matrix->IntToExtRowMap[Row1] ] = Row1;
Matrix->ExtToIntRowMap[ Matrix->IntToExtRowMap[Row2] ] = Row2;
#endif
return;
}
/*
* EXCHANGE COLUMNS
*
* Performs all required operations to exchange two columns. Those operations
* include: swap FirstInCol pointers, fixing up the NextInRow pointers,
* swapping column indexes in MatrixElements, and swapping Markowitz
* column counts.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* Col1 (int)
* Column index of one of the columns, becomes the smallest index.
* Col2 (int)
* Column index of the other column, becomes the largest index
*
* Local variables:
* Row (int)
* Row in which column elements are currently being exchanged.
* Col1Ptr (ElementPtr)
* Pointer to an element in Col1.
* Col2Ptr (ElementPtr)
* Pointer to an element in Col2.
* Element1 (ElementPtr)
* Pointer to the element in Col1 to be exchanged.
* Element2 (ElementPtr)
* Pointer to the element in Col2 to be exchanged.
*/
spcColExchange( Matrix, Col1, Col2 )
MatrixPtr Matrix;
int Col1, Col2;
{
register ElementPtr Col1Ptr, Col2Ptr;
int Row;
ElementPtr Element1, Element2;
/* Begin `spcColExchange'. */
if (Col1 > Col2) SWAP(int, Col1, Col2);
Col1Ptr = Matrix->FirstInCol[Col1];
Col2Ptr = Matrix->FirstInCol[Col2];
while (Col1Ptr != NULL OR Col2Ptr != NULL)
{
/* Exchange elements in rows while traveling from top to bottom. */
if (Col1Ptr == NULL)
{ Row = Col2Ptr->Row;
Element1 = NULL;
Element2 = Col2Ptr;
Col2Ptr = Col2Ptr->NextInCol;
}
else if (Col2Ptr == NULL)
{ Row = Col1Ptr->Row;
Element1 = Col1Ptr;
Element2 = NULL;
Col1Ptr = Col1Ptr->NextInCol;
}
else if (Col1Ptr->Row < Col2Ptr->Row)
{ Row = Col1Ptr->Row;
Element1 = Col1Ptr;
Element2 = NULL;
Col1Ptr = Col1Ptr->NextInCol;
}
else if (Col1Ptr->Row > Col2Ptr->Row)
{ Row = Col2Ptr->Row;
Element1 = NULL;
Element2 = Col2Ptr;
Col2Ptr = Col2Ptr->NextInCol;
}
else /* Col1Ptr->Row == Col2Ptr->Row */
{ Row = Col1Ptr->Row;
Element1 = Col1Ptr;
Element2 = Col2Ptr;
Col1Ptr = Col1Ptr->NextInCol;
Col2Ptr = Col2Ptr->NextInCol;
}
ExchangeRowElements( Matrix, Col1, Element1, Col2, Element2, Row);
} /* end of while(Col1Ptr != NULL OR Col2Ptr != NULL) */
if (Matrix->InternalVectorsAllocated)
SWAP( int, Matrix->MarkowitzCol[Col1], Matrix->MarkowitzCol[Col2]);
SWAP( ElementPtr, Matrix->FirstInCol[Col1], Matrix->FirstInCol[Col2]);
SWAP( int, Matrix->IntToExtColMap[Col1], Matrix->IntToExtColMap[Col2]);
#if TRANSLATE
Matrix->ExtToIntColMap[ Matrix->IntToExtColMap[Col1] ] = Col1;
Matrix->ExtToIntColMap[ Matrix->IntToExtColMap[Col2] ] = Col2;
#endif
return;
}
/*
* EXCHANGE TWO ELEMENTS IN A COLUMN
*
* Performs all required operations to exchange two elements in a column.
* Those operations are: restring NextInCol pointers and swapping row indexes
* in the MatrixElements.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* Row1 (int)
* Row of top element to be exchanged.
* Element1 (ElementPtr)
* Pointer to top element to be exchanged.
* Row2 (int)
* Row of bottom element to be exchanged.
* Element2 (ElementPtr)
* Pointer to bottom element to be exchanged.
* Column (int)
* Column that exchange is to take place in.
*
* >>> Local variables:
* ElementAboveRow1 (ElementPtr *)
* Location of pointer which points to the element above Element1. This
* pointer is modified so that it points to correct element on exit.
* ElementAboveRow2 (ElementPtr *)
* Location of pointer which points to the element above Element2. This
* pointer is modified so that it points to correct element on exit.
* ElementBelowRow1 (ElementPtr)
* Pointer to element below Element1.
* ElementBelowRow2 (ElementPtr)
* Pointer to element below Element2.
* pElement (ElementPtr)
* Pointer used to traverse the column.
*/
static
ExchangeColElements( Matrix, Row1, Element1, Row2, Element2, Column )
MatrixPtr Matrix;
register ElementPtr Element1, Element2;
int Row1, Row2, Column;
{
ElementPtr *ElementAboveRow1, *ElementAboveRow2;
ElementPtr ElementBelowRow1, ElementBelowRow2;
register ElementPtr pElement;
/* Begin `ExchangeColElements'. */
/* Search to find the ElementAboveRow1. */
ElementAboveRow1 = &(Matrix->FirstInCol[Column]);
pElement = *ElementAboveRow1;
while (pElement->Row < Row1)
{ ElementAboveRow1 = &(pElement->NextInCol);
pElement = *ElementAboveRow1;
}
if (Element1 != NULL)
{ ElementBelowRow1 = Element1->NextInCol;
if (Element2 == NULL)
{
/* Element2 does not exist, move Element1 down to Row2. */
if ( ElementBelowRow1 != NULL AND ElementBelowRow1->Row < Row2 )
{
/* Element1 must be removed from linked list and moved. */
*ElementAboveRow1 = ElementBelowRow1;
/* Search column for Row2. */
pElement = ElementBelowRow1;
do
{ ElementAboveRow2 = &(pElement->NextInCol);
pElement = *ElementAboveRow2;
} while (pElement != NULL AND pElement->Row < Row2);
/* Place Element1 in Row2. */
*ElementAboveRow2 = Element1;
Element1->NextInCol = pElement;
*ElementAboveRow1 =ElementBelowRow1;
}
Element1->Row = Row2;
}
else
{
/* Element2 does exist, and the two elements must be exchanged. */
if ( ElementBelowRow1->Row == Row2)
{
/* Element2 is just below Element1, exchange them. */
Element1->NextInCol = Element2->NextInCol;
Element2->NextInCol = Element1;
*ElementAboveRow1 = Element2;
}
else
{
/* Element2 is not just below Element1 and must be searched for. */
pElement = ElementBelowRow1;
do
{ ElementAboveRow2 = &(pElement->NextInCol);
pElement = *ElementAboveRow2;
} while (pElement->Row < Row2);
ElementBelowRow2 = Element2->NextInCol;
/* Switch Element1 and Element2. */
*ElementAboveRow1 = Element2;
Element2->NextInCol = ElementBelowRow1;
*ElementAboveRow2 = Element1;
Element1->NextInCol = ElementBelowRow2;
}
Element1->Row = Row2;
Element2->Row = Row1;
}
}
else
{
/* Element1 does not exist. */
ElementBelowRow1 = pElement;
/* Find Element2. */
if (ElementBelowRow1->Row != Row2)
{ do
{ ElementAboveRow2 = &(pElement->NextInCol);
pElement = *ElementAboveRow2;
} while (pElement->Row < Row2);
ElementBelowRow2 = Element2->NextInCol;
/* Move Element2 to Row1. */
*ElementAboveRow2 = Element2->NextInCol;
*ElementAboveRow1 = Element2;
Element2->NextInCol = ElementBelowRow1;
}
Element2->Row = Row1;
}
return;
}
/*
* EXCHANGE TWO ELEMENTS IN A ROW
*
* Performs all required operations to exchange two elements in a row.
* Those operations are: restring NextInRow pointers and swapping column
* indexes in the MatrixElements.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* Col1 (int)
* Col of left-most element to be exchanged.
* Element1 (ElementPtr)
* Pointer to left-most element to be exchanged.
* Col2 (int)
* Col of right-most element to be exchanged.
* Element2 (ElementPtr)
* Pointer to right-most element to be exchanged.
* Row (int)
* Row that exchange is to take place in.
*
* >>> Local variables:
* ElementLeftOfCol1 (ElementPtr *)
* Location of pointer which points to the element to the left of
* Element1. This pointer is modified so that it points to correct
* element on exit.
* ElementLeftOfCol2 (ElementPtr *)
* Location of pointer which points to the element to the left of
* Element2. This pointer is modified so that it points to correct
* element on exit.
* ElementRightOfCol1 (ElementPtr)
* Pointer to element right of Element1.
* ElementRightOfCol2 (ElementPtr)
* Pointer to element right of Element2.
* pElement (ElementPtr)
* Pointer used to traverse the row.
*/
static
ExchangeRowElements( Matrix, Col1, Element1, Col2, Element2, Row )
MatrixPtr Matrix;
int Col1, Col2, Row;
register ElementPtr Element1, Element2;
{
ElementPtr *ElementLeftOfCol1, *ElementLeftOfCol2;
ElementPtr ElementRightOfCol1, ElementRightOfCol2;
register ElementPtr pElement;
/* Begin `ExchangeRowElements'. */
/* Search to find the ElementLeftOfCol1. */
ElementLeftOfCol1 = &(Matrix->FirstInRow[Row]);
pElement = *ElementLeftOfCol1;
while (pElement->Col < Col1)
{ ElementLeftOfCol1 = &(pElement->NextInRow);
pElement = *ElementLeftOfCol1;
}
if (Element1 != NULL)
{ ElementRightOfCol1 = Element1->NextInRow;
if (Element2 == NULL)
{
/* Element2 does not exist, move Element1 to right to Col2. */
if ( ElementRightOfCol1 != NULL AND ElementRightOfCol1->Col < Col2 )
{
/* Element1 must be removed from linked list and moved. */
*ElementLeftOfCol1 = ElementRightOfCol1;
/* Search Row for Col2. */
pElement = ElementRightOfCol1;
do
{ ElementLeftOfCol2 = &(pElement->NextInRow);
pElement = *ElementLeftOfCol2;
} while (pElement != NULL AND pElement->Col < Col2);
/* Place Element1 in Col2. */
*ElementLeftOfCol2 = Element1;
Element1->NextInRow = pElement;
*ElementLeftOfCol1 =ElementRightOfCol1;
}
Element1->Col = Col2;
}
else
{
/* Element2 does exist, and the two elements must be exchanged. */
if ( ElementRightOfCol1->Col == Col2)
{
/* Element2 is just right of Element1, exchange them. */
Element1->NextInRow = Element2->NextInRow;
Element2->NextInRow = Element1;
*ElementLeftOfCol1 = Element2;
}
else
{
/* Element2 is not just right of Element1 and must be searched for. */
pElement = ElementRightOfCol1;
do
{ ElementLeftOfCol2 = &(pElement->NextInRow);
pElement = *ElementLeftOfCol2;
} while (pElement->Col < Col2);
ElementRightOfCol2 = Element2->NextInRow;
/* Switch Element1 and Element2. */
*ElementLeftOfCol1 = Element2;
Element2->NextInRow = ElementRightOfCol1;
*ElementLeftOfCol2 = Element1;
Element1->NextInRow = ElementRightOfCol2;
}
Element1->Col = Col2;
Element2->Col = Col1;
}
}
else
{
/* Element1 does not exist. */
ElementRightOfCol1 = pElement;
/* Find Element2. */
if (ElementRightOfCol1->Col != Col2)
{ do
{ ElementLeftOfCol2 = &(pElement->NextInRow);
pElement = *ElementLeftOfCol2;
} while (pElement->Col < Col2);
ElementRightOfCol2 = Element2->NextInRow;
/* Move Element2 to Col1. */
*ElementLeftOfCol2 = Element2->NextInRow;
*ElementLeftOfCol1 = Element2;
Element2->NextInRow = ElementRightOfCol1;
}
Element2->Col = Col1;
}
return;
}
/*
* PERFORM ROW AND COLUMN ELIMINATION ON REAL MATRIX
*
* Eliminates a single row and column of the matrix and leaves single row of
* the upper triangular matrix and a single column of the lower triangular
* matrix in its wake. Uses Gauss's method.
*
* >>> Argument:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* pPivot (ElementPtr)
* Pointer to the current pivot.
*
* >>> Local variables:
* pLower (ElementPtr)
* Points to matrix element in lower triangular column.
* pSub (ElementPtr)
* Points to elements in the reduced submatrix.
* Row (int)
* Row index.
* pUpper (ElementPtr)
* Points to matrix element in upper triangular row.
*
* >>> Possible errors:
* spNO_MEMORY
*/
static
RealRowColElimination( Matrix, pPivot )
MatrixPtr Matrix;
register ElementPtr pPivot;
{
#if REAL
register ElementPtr pSub;
register int Row;
register ElementPtr pLower, pUpper;
extern ElementPtr CreateFillin();
/* Begin `RealRowColElimination'. */
/* Test for zero pivot. */
if (ABS(pPivot->Real) == 0.0)
{ (void)MatrixIsSingular( Matrix, pPivot->Row );
return;
}
pPivot->Real = 1.0 / pPivot->Real;
pUpper = pPivot->NextInRow;
while (pUpper != NULL)
{
/* Calculate upper triangular element. */
pUpper->Real *= pPivot->Real;
pSub = pUpper->NextInCol;
pLower = pPivot->NextInCol;
while (pLower != NULL)
{ Row = pLower->Row;
/* Find element in row that lines up with current lower triangular element. */
while (pSub != NULL AND pSub->Row < Row)
pSub = pSub->NextInCol;
/* Test to see if desired element was not found, if not, create fill-in. */
if (pSub == NULL OR pSub->Row > Row)
{ pSub = CreateFillin( Matrix, Row, pUpper->Col );
if (pSub == NULL)
{ Matrix->Error = spNO_MEMORY;
return;
}
}
pSub->Real -= pUpper->Real * pLower->Real;
pSub = pSub->NextInCol;
pLower = pLower->NextInCol;
}
pUpper = pUpper->NextInRow;
}
return;
#endif /* REAL */
}
/*
* PERFORM ROW AND COLUMN ELIMINATION ON COMPLEX MATRIX
*
* Eliminates a single row and column of the matrix and leaves single row of
* the upper triangular matrix and a single column of the lower triangular
* matrix in its wake. Uses Gauss's method.
*
* >>> Argument:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* pPivot (ElementPtr)
* Pointer to the current pivot.
*
* >>> Local variables:
* pLower (ElementPtr)
* Points to matrix element in lower triangular column.
* pSub (ElementPtr)
* Points to elements in the reduced submatrix.
* Row (int)
* Row index.
* pUpper (ElementPtr)
* Points to matrix element in upper triangular row.
*
* Possible errors:
* spNO_MEMORY
*/
static
ComplexRowColElimination( Matrix, pPivot )
MatrixPtr Matrix;
register ElementPtr pPivot;
{
#if spCOMPLEX
register ElementPtr pSub;
register int Row;
register ElementPtr pLower, pUpper;
ElementPtr CreateFillin();
/* Begin `ComplexRowColElimination'. */
/* Test for zero pivot. */
if (ELEMENT_MAG(pPivot) == 0.0)
{ (void)MatrixIsSingular( Matrix, pPivot->Row );
return;
}
CMPLX_RECIPROCAL(*pPivot, *pPivot);
pUpper = pPivot->NextInRow;
while (pUpper != NULL)
{
/* Calculate upper triangular element. */
/* Cmplx expr: *pUpper = *pUpper * (1.0 / *pPivot). */
CMPLX_MULT_ASSIGN(*pUpper, *pPivot);
pSub = pUpper->NextInCol;
pLower = pPivot->NextInCol;
while (pLower != NULL)
{ Row = pLower->Row;
/* Find element in row that lines up with current lower triangular element. */
while (pSub != NULL AND pSub->Row < Row)
pSub = pSub->NextInCol;
/* Test to see if desired element was not found, if not, create fill-in. */
if (pSub == NULL OR pSub->Row > Row)
{ pSub = CreateFillin( Matrix, Row, pUpper->Col );
if (pSub == NULL)
{ Matrix->Error = spNO_MEMORY;
return;
}
}
/* Cmplx expr: pElement -= *pUpper * pLower. */
CMPLX_MULT_SUBT_ASSIGN(*pSub, *pUpper, *pLower);
pSub = pSub->NextInCol;
pLower = pLower->NextInCol;
}
pUpper = pUpper->NextInRow;
}
return;
#endif /* spCOMPLEX */
}
/*
* UPDATE MARKOWITZ NUMBERS
*
* Updates the Markowitz numbers after a row and column have been eliminated.
* Also updates singleton count.
*
* >>> Argument:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* pPivot (ElementPtr)
* Pointer to the current pivot.
*
* >>> Local variables:
* Row (int)
* Row index.
* Col (int)
* Column index.
* ColPtr (ElementPtr)
* Points to matrix element in upper triangular column.
* RowPtr (ElementPtr)
* Points to matrix element in lower triangular row.
*/
static
UpdateMarkowitzNumbers( Matrix, pPivot )
MatrixPtr Matrix;
ElementPtr pPivot;
{
register int Row, Col;
register ElementPtr ColPtr, RowPtr;
register int *MarkoRow = Matrix->MarkowitzRow, *MarkoCol = Matrix->MarkowitzCol;
double Product;
/* Begin `UpdateMarkowitzNumbers'. */
/* Update Markowitz numbers. */
for (ColPtr = pPivot->NextInCol; ColPtr != NULL; ColPtr = ColPtr->NextInCol)
{ Row = ColPtr->Row;
--MarkoRow[Row];
/* Form Markowitz product while being cautious of overflows. */
if ((MarkoRow[Row] > LARGEST_SHORT_INTEGER AND MarkoCol[Row] != 0) OR
(MarkoCol[Row] > LARGEST_SHORT_INTEGER AND MarkoRow[Row] != 0))
{ Product = MarkoCol[Row] * MarkoRow[Row];
if (Product >= LARGEST_LONG_INTEGER)
Matrix->MarkowitzProd[Row] = LARGEST_LONG_INTEGER;
else
Matrix->MarkowitzProd[Row] = Product;
}
else Matrix->MarkowitzProd[Row] = MarkoRow[Row] * MarkoCol[Row];
if (MarkoRow[Row] == 0)
Matrix->Singletons++;
}
for (RowPtr = pPivot->NextInRow; RowPtr != NULL; RowPtr = RowPtr->NextInRow)
{ Col = RowPtr->Col;
--MarkoCol[Col];
/* Form Markowitz product while being cautious of overflows. */
if ((MarkoRow[Col] > LARGEST_SHORT_INTEGER AND MarkoCol[Col] != 0) OR
(MarkoCol[Col] > LARGEST_SHORT_INTEGER AND MarkoRow[Col] != 0))
{ Product = MarkoCol[Col] * MarkoRow[Col];
if (Product >= LARGEST_LONG_INTEGER)
Matrix->MarkowitzProd[Col] = LARGEST_LONG_INTEGER;
else
Matrix->MarkowitzProd[Col] = Product;
}
else Matrix->MarkowitzProd[Col] = MarkoRow[Col] * MarkoCol[Col];
if ((MarkoCol[Col] == 0) AND (MarkoRow[Col] != 0))
Matrix->Singletons++;
}
return;
}
/*
* CREATE FILL-IN
*
* This routine is used to create fill-ins and splice them into the
* matrix.
*
* >>> Returns:
* Pointer to fill-in.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to the matrix.
* Col (int)
* Column index for element.
* Row (int)
* Row index for element.
*
* >>> Local variables:
* pElement (ElementPtr)
* Pointer to an element in the matrix.
* ppElementAbove (ElementPtr *)
* This contains the address of the pointer to the element just above the
* one being created. It is used to speed the search and it is updated
* with address of the created element.
*
* >>> Possible errors:
* spNO_MEMORY
*/
static ElementPtr
CreateFillin( Matrix, Row, Col )
MatrixPtr Matrix;
register int Row;
int Col;
{
register ElementPtr pElement, *ppElementAbove;
ElementPtr spcCreateElement();
/* Begin `CreateFillin'. */
/* Find Element above fill-in. */
ppElementAbove = &Matrix->FirstInCol[Col];
pElement = *ppElementAbove;
while (pElement != NULL)
{ if (pElement->Row < Row)
{ ppElementAbove = &pElement->NextInCol;
pElement = *ppElementAbove;
}
else break; /* while loop */
}
/* End of search, create the element. */
pElement = spcCreateElement( Matrix, Row, Col, ppElementAbove, YES );
/* Update Markowitz counts and products. */
Matrix->MarkowitzProd[Row] = ++Matrix->MarkowitzRow[Row] *
Matrix->MarkowitzCol[Row];
if ((Matrix->MarkowitzRow[Row] == 1) AND (Matrix->MarkowitzCol[Row] != 0))
Matrix->Singletons--;
Matrix->MarkowitzProd[Col] = ++Matrix->MarkowitzCol[Col] *
Matrix->MarkowitzRow[Col];
if ((Matrix->MarkowitzRow[Col] != 0) AND (Matrix->MarkowitzCol[Col] == 1))
Matrix->Singletons--;
return pElement;
}
/*
* ZERO PIVOT ENCOUNTERED
*
* This routine is called when a singular matrix is found. It then
* records the current row and column and exits.
*
* >>> Returned:
* The error code spSINGULAR or spZERO_DIAG is returned.
*
* >>> Arguments:
* Matrix (MatrixPtr)
* Pointer to matrix.
* Step (int)
* Index of diagonal that is zero.
*/
static int
MatrixIsSingular( Matrix, Step )
MatrixPtr Matrix;
int Step;
{
/* Begin `MatrixIsSingular'. */
Matrix->SingularRow = Matrix->IntToExtRowMap[ Step ];
Matrix->SingularCol = Matrix->IntToExtColMap[ Step ];
return (Matrix->Error = spSINGULAR);
}
static int
ZeroPivot( Matrix, Step )
MatrixPtr Matrix;
int Step;
{
/* Begin `ZeroPivot'. */
Matrix->SingularRow = Matrix->IntToExtRowMap[ Step ];
Matrix->SingularCol = Matrix->IntToExtColMap[ Step ];
return (Matrix->Error = spZERO_DIAG);
}
#if ANNOTATE == FULL
/*
*
* WRITE STATUS
*
* Write a summary of important variables to standard output.
*/
static
WriteStatus( Matrix, Step )
MatrixPtr Matrix;
int Step;
{
int I;
/* Begin `WriteStatus'. */
printf("Step = %1d ", Step);
printf("Pivot found at %1d,%1d using ", Matrix->PivotsOriginalRow,
Matrix->PivotsOriginalCol);
switch(Matrix->PivotSelectionMethod)
{ case 's': printf("SearchForSingleton\n"); break;
case 'q': printf("QuicklySearchDiagonal\n"); break;
case 'd': printf("SearchDiagonal\n"); break;
case 'e': printf("SearchEntireMatrix\n"); break;
}
printf("MarkowitzRow = ");
for (I = 1; I <= Matrix->Size; I++)
printf("%2d ", Matrix->MarkowitzRow[I]);
printf("\n");
printf("MarkowitzCol = ");
for (I = 1; I <= Matrix->Size; I++)
printf("%2d ", Matrix->MarkowitzCol[I]);
printf("\n");
printf("MarkowitzProduct = ");
for (I = 1; I <= Matrix->Size; I++)
printf("%2d ", Matrix->MarkowitzProd[I]);
printf("\n");
printf("Singletons = %2d\n", Matrix->Singletons);
printf("IntToExtRowMap = ");
for (I = 1; I <= Matrix->Size; I++)
printf("%2d ", Matrix->IntToExtRowMap[I]);
printf("\n");
printf("IntToExtColMap = ");
for (I = 1; I <= Matrix->Size; I++)
printf("%2d ", Matrix->IntToExtColMap[I]);
printf("\n");
printf("ExtToIntRowMap = ");
for (I = 1; I <= Matrix->ExtSize; I++)
printf("%2d ", Matrix->ExtToIntRowMap[I]);
printf("\n");
printf("ExtToIntColMap = ");
for (I = 1; I <= Matrix->ExtSize; I++)
printf("%2d ", Matrix->ExtToIntColMap[I]);
printf("\n\n");
/* spPrint((char *)Matrix, NO, YES); */
return;
}
#endif /* ANNOTATE == FULL */
@EOF
chmod 644 spFactor.c
echo x - spMatrix.h
cat >spMatrix.h <<'@EOF'
/*
* EXPORTS for sparse matrix routines.
*
* Author: Advising professor:
* Kenneth S. Kundert Alberto Sangiovanni-Vincentelli
* UC Berkeley
*
* This file contains definitions that are useful to the calling
* program. In particular, this file contains error keyword
* definitions, some macro functions that are used to quickly enter
* data into the matrix and the type definition of a data structure
* that acts as a template for entering admittances into the matrix.
* Also included is the type definitions for the various functions
* available to the user.
*/
/*
* 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.
*
* $Date: 88/06/18 11:14:02 $
* $Revision: 1.2 $
*/
#ifndef spOKAY
/*
* IMPORTS
*
* >>> Import descriptions:
* spConfig.h
* Macros that customize the sparse matrix routines.
*/
#include "spConfig.h"
/*
* ERROR KEYWORDS
*
* The actual numbers used in the error codes are not sacred, they can be
* changed under the condition that the codes for the nonfatal errors are
* less than the code for spFATAL and similarly the codes for the fatal
* errors are greater than that for spFATAL.
*
* >>> Error descriptions:
* spOKAY
* No error has occurred.
* spSMALL_PIVOT
* When reordering the matrix, no element was found which satisfies the
* absolute threshold criteria. The largest element in the matrix was
* chosen as pivot. Non-fatal.
* spZERO_DIAG
* Fatal error. A zero was encountered on the diagonal the matrix. This
* does not necessarily imply that the matrix is singular. When this
* error occurs, the matrix should be reconstructed and factored using
* spOrderAndFactor().
* spSINGULAR
* Fatal error. Matrix is singular, so no unique solution exists.
* spNO_MEMORY
* Fatal error. Indicates that not enough memory is available to handle
* the matrix.
* spPANIC
* Fatal error indicating that the routines are not prepared to
* handle the matrix that has been requested. This may occur when
* the matrix is specified to be real and the routines are not
* compiled for real matrices, or when the matrix is specified to
* be complex and the routines are not compiled to handle complex
* matrices.
* spFATAL
* Not an error flag, but rather the dividing line between fatal errors
* and warnings.
*/
/* Begin error macros. */
#define spOKAY 0
#define spSMALL_PIVOT 1
#define spZERO_DIAG 2
#define spSINGULAR 3
#define spNO_MEMORY 4
#define spPANIC 5
#define spFATAL 2
/*
* KEYWORD DEFINITIONS
*
* Here we define what precision arithmetic Sparse will use. Double
* precision is suggested as being most appropriate for circuit
* simulation and for C. However, it is possible to change spREAL
* to a float for single precision arithmetic. Note that in C, single
* precision arithmetic is often slower than double precision. Sparse
* internally refers to spREALs as RealNumbers.
*
* Some C compilers, notably the old VMS compiler, do not handle the keyword
* "void" correctly. If this is true for your compiler, remove the
* comment delimiters from the redefinition of void to int below.
*/
#define spREAL double
/* #define void int */
/*
* PARTITION TYPES
*
* When factoring a previously ordered matrix using spFactor(), Sparse
* operates on a row-at-a-time basis. For speed, on each step, the row
* being updated is copied into a full vector and the operations are
* performed on that vector. This can be done one of two ways, either
* using direct addressing or indirect addressing. Direct addressing
* is fastest when the matrix is relatively dense and indirect addressing
* is quite sparse. The user can select which partitioning mode is used.
* The following keywords are passed to spPartition() and indicate that
* Sparse should use only direct addressing, only indirect addressing, or
* that it should choose the best mode on a row-by-row basis. The time
* required to choose a partition is of the same order of the cost to factor
* the matrix.
*
* If you plan to factor a large number of matrices with the same structure,
* it is best to let Sparse choose the partition. Otherwise, you should
* choose the partition based on the predicted density of the matrix.
*/
/* Begin partition keywords. */
#define spDEFAULT_PARTITION 0
#define spDIRECT_PARTITION 1
#define spINDIRECT_PARTITION 2
#define spAUTO_PARTITION 3
/*
* MACRO FUNCTION DEFINITIONS
*
* >>> Macro descriptions:
* spADD_REAL_ELEMENT
* Macro function that adds data to a real element in the matrix by a
* pointer.
* spADD_IMAG_ELEMENT
* Macro function that adds data to a imaginary element in the matrix by
* a pointer.
* spADD_COMPLEX_ELEMENT
* Macro function that adds data to a complex element in the matrix by a
* pointer.
* spADD_REAL_QUAD
* Macro function that adds data to each of the four real matrix elements
* specified by the given template.
* spADD_IMAG_QUAD
* Macro function that adds data to each of the four imaginary matrix
* elements specified by the given template.
* spADD_COMPLEX_QUAD
* Macro function that adds data to each of the four complex matrix
* elements specified by the given template.
*/
/* Begin Macros. */
#define spADD_REAL_ELEMENT(element,real) *(element) += real
#define spADD_IMAG_ELEMENT(element,imag) *(element+1) += imag
#define spADD_COMPLEX_ELEMENT(element,real,imag) \
{ *(element) += real; \
*(element+1) += imag; \
}
#define spADD_REAL_QUAD(template,real) \
{ *((template).Element1) += real; \
*((template).Element2) += real; \
*((template).Element3Negated) -= real; \
*((template).Element4Negated) -= real; \
}
#define spADD_IMAG_QUAD(template,imag) \
{ *((template).Element1+1) += imag; \
*((template).Element2+1) += imag; \
*((template).Element3Negated+1) -= imag; \
*((template).Element4Negated+1) -= imag; \
}
#define spADD_COMPLEX_QUAD(template,real,imag) \
{ *((template).Element1) += real; \
*((template).Element2) += real; \
*((template).Element3Negated) -= real; \
*((template).Element4Negated) -= real; \
*((template).Element1+1) += imag; \
*((template).Element2+1) += imag; \
*((template).Element3Negated+1) -= imag; \
*((template).Element4Negated+1) -= imag; \
}
/*
* TYPE DEFINITION FOR COMPONENT TEMPLATE
*
* This data structure is used to hold pointers to four related elements in
* matrix. It is used in conjunction with the routines
* spGetAdmittance
* spGetQuad
* spGetOnes
* These routines stuff the structure which is later used by the spADD_QUAD
* macro functions above. It is also possible for the user to collect four
* pointers returned by spGetElement and stuff them into the template.
* The spADD_QUAD routines stuff data into the matrix in locations specified
* by Element1 and Element2 without changing the data. The data is negated
* before being placed in Element3 and Element4.
*/
/* Begin `spTemplate'. */
struct spTemplate
{ spREAL *Element1 ;
spREAL *Element2 ;
spREAL *Element3Negated;
spREAL *Element4Negated;
};
/*
* FUNCTION TYPE DEFINITIONS
*
* The type of every user accessible function is declared here.
*/
/* Begin function declarations. */
#ifdef __STDC__
/* For compilers that understand function prototypes. */
extern void spClear( char* );
extern spREAL spCondition( char*, spREAL, int* );
extern char *spCreate( int, int, int* );
extern void spDeleteRowAndCol( char*, int, int );
extern void spDestroy( char* );
extern int spElementCount( char* );
extern int spError( char* );
extern int spFactor( char* );
extern int spFileMatrix( char*, char*, char*, int, int, int );
extern int spFileStats( char*, char*, char* );
extern int spFillinCount( char* );
extern int spGetAdmittance( char*, int, int, struct spTemplate* );
extern spREAL *spGetElement( char*, int, int );
extern char *spGetInitInfo( spREAL* );
extern int spGetOnes( char*, int, int, int, struct spTemplate* );
extern int spGetQuad( char*, int, int, int, int, struct spTemplate* );
extern int spGetSize( char*, int );
extern int spInitialize( char*, int (*)() );
extern void spInstallInitInfo( spREAL*, char* );
extern spREAL spLargestElement( char* );
extern void spMNA_Preorder( char* );
extern spREAL spNorm( char* );
extern int spOrderAndFactor( char*, spREAL[], spREAL, spREAL, int );
extern void spPartition( char*, int );
extern void spPrint( char*, int, int, int );
extern spREAL spPseudoCondition( char* );
extern spREAL spRoundoff( char*, spREAL );
extern void spScale( char*, spREAL[], spREAL[] );
extern void spSetComplex( char* );
extern void spSetReal( char* );
extern void spStripFills( char* );
extern void spWhereSingular( char*, int*, int* );
/* Functions with argument lists that are dependent on options. */
#if spCOMPLEX
extern void spDeterminant ( char*, int*, spREAL*, spREAL* );
#else /* NOT spCOMPLEX */
extern void spDeterminant ( char*, int*, spREAL* );
#endif /* NOT spCOMPLEX */
#if spCOMPLEX && spSEPARATED_COMPLEX_VECTORS
extern int spFileVector( char*, char* , spREAL[], spREAL[]);
extern void spMultiply( char*, spREAL[], spREAL[], spREAL[], spREAL[] );
extern void spMultTransposed(char*,spREAL[],spREAL[],spREAL[],spREAL[]);
extern void spSolve( char*, spREAL[], spREAL[], spREAL[], spREAL[] );
extern void spSolveTransposed(char*,spREAL[],spREAL[],spREAL[],spREAL[]);
#else /* NOT (spCOMPLEX && spSEPARATED_COMPLEX_VECTORS) */
extern int spFileVector( char*, char* , spREAL[] );
extern void spMultiply( char*, spREAL[], spREAL[] );
extern void spMultTransposed( char*, spREAL[], spREAL[] );
extern void spSolve( char*, spREAL[], spREAL[] );
extern void spSolveTransposed( char*, spREAL[], spREAL[] );
#endif /* NOT (spCOMPLEX && spSEPARATED_COMPLEX_VECTORS) */
#else /* NOT defined(__STDC__) */
/* For compilers that do not understand function prototypes. */
extern void spClear();
extern spREAL spCondition();
extern char *spCreate();
extern void spDeleteRowAndCol();
extern void spDestroy();
extern void spDeterminant ();
extern int spElementCount();
extern int spError();
extern int spFactor();
extern int spFileMatrix();
extern int spFileStats();
extern int spFileVector();
extern int spFillinCount();
extern int spGetAdmittance();
extern spREAL *spGetElement();
extern char *spGetInitInfo();
extern int spGetOnes();
extern int spGetQuad();
extern int spGetSize();
extern int spInitialize();
extern void spInstallInitInfo();
extern spREAL spLargestElement();
extern void spMNA_Preorder();
extern void spMultiply();
extern void spMultTransposed();
extern spREAL spNorm();
extern int spOrderAndFactor();
extern void spPartition();
extern void spPrint();
extern spREAL spPseudoCondition();
extern spREAL spRoundoff();
extern void spScale();
extern void spSetComplex();
extern void spSetReal();
extern void spSolve();
extern void spSolveTransposed();
extern void spStripFills();
extern void spWhereSingular();
#endif /* defined(__STDC__) */
#endif /* spOKAY */
@EOF
chmod 644 spMatrix.h
echo x - spSolve.c
cat >spSolve.c <<'@EOF'
/*
* MATRIX SOLVE MODULE
*
* Author: Advising professor:
* Kenneth S. Kundert Alberto Sangiovanni-Vincentelli
* UC Berkeley
*
* This file contains the forward and backward substitution routines for
* the sparse matrix routines.
*
* >>> User accessible functions contained in this file:
* spSolve
* spSolveTransposed
*
* >>> Other functions contained in this file:
* SolveComplexMatrix
* SolveComplexTransposedMatrix
*/
/*
* 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.
*/
#ifndef lint
static char copyright[] =
"Sparse1.3: Copyright (c) 1985,86,87,88 by Kenneth S. Kundert";
static char RCSid[] =
"@(#)$Header: spSolve.c,v 1.2 88/06/18 11:15:54 kundert Exp $";
#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 "spConfig.h"
#include "spMatrix.h"
#include "spDefs.h"
/*
* SOLVE MATRIX EQUATION
*
* Performs forward elimination and back substitution to find the
* unknown vector from the RHS vector and factored matrix. This
* routine assumes that the pivots are associated with the lower
* triangular (L) matrix and that the diagonal of the upper triangular
* (U) matrix consists of ones. This routine arranges the computation
* in different way than is traditionally used in order to exploit the
* sparsity of the right-hand side. See the reference in spRevision.
*
* >>> Arguments:
* Matrix (char *)
* Pointer to matrix.
* RHS (RealVector)
* RHS is the input data array, the right hand side. This data is
* undisturbed and may be reused for other solves.
* Solution (RealVector)
* Solution is the output data array. This routine is constructed such that
* RHS and Solution can be the same array.
* iRHS (RealVector)
* iRHS is the imaginary portion of the input data array, the right
* hand side. This data is undisturbed and may be reused for other solves.
* This argument is only necessary if matrix is complex and if
* spSEPARATED_COMPLEX_VECTOR is set true.
* iSolution (RealVector)
* iSolution is the imaginary portion of the output data array. This
* routine is constructed such that iRHS and iSolution can be
* the same array. This argument is only necessary if matrix is complex
* and if spSEPARATED_COMPLEX_VECTOR is set true.
*
* >>> Local variables:
* Intermediate (RealVector)
* Temporary storage for use in forward elimination and backward
* substitution. Commonly referred to as c, when the LU factorization
* equations are given as Ax = b, Lc = b, Ux = c Local version of
* Matrix->Intermediate, which was created during the initial
* factorization in function CreateInternalVectors() in the matrix
* factorization module.
* pElement (ElementPtr)
* Pointer used to address elements in both the lower and upper triangle
* matrices.
* pExtOrder (int *)
* Pointer used to sequentially access each entry in IntToExtRowMap
* and IntToExtColMap arrays. Used to quickly scramble and unscramble
* RHS and Solution to account for row and column interchanges.
* pPivot (ElementPtr)
* Pointer that points to current pivot or diagonal element.
* Size (int)
* Size of matrix. Made local to reduce indirection.
* Temp (RealNumber)
* Temporary storage for entries in arrays.
*
* >>> 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.
*/
/*VARARGS3*/
void
spSolve( eMatrix, RHS, Solution IMAG_VECTORS )
char *eMatrix;
RealVector RHS, Solution IMAG_VECTORS;
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
register ElementPtr pElement;
register RealVector Intermediate;
register RealNumber Temp;
register int I, *pExtOrder, Size;
ElementPtr pPivot;
void SolveComplexMatrix();
/* Begin `spSolve'. */
ASSERT( IS_VALID(Matrix) AND IS_FACTORED(Matrix) );
#if spCOMPLEX
if (Matrix->Complex)
{ SolveComplexMatrix( Matrix, RHS, Solution IMAG_VECTORS );
return;
}
#endif
#if REAL
Intermediate = Matrix->Intermediate;
Size = Matrix->Size;
/* Correct array pointers for ARRAY_OFFSET. */
#if NOT ARRAY_OFFSET
--RHS;
--Solution;
#endif
/* Initialize Intermediate vector. */
pExtOrder = &Matrix->IntToExtRowMap[Size];
for (I = Size; I > 0; I--)
Intermediate[I] = RHS[*(pExtOrder--)];
/* Forward elimination. Solves Lc = b.*/
for (I = 1; I <= Size; I++)
{
/* This step of the elimination is skipped if Temp equals zero. */
if ((Temp = Intermediate[I]) != 0.0)
{ pPivot = Matrix->Diag[I];
Intermediate[I] = (Temp *= pPivot->Real);
pElement = pPivot->NextInCol;
while (pElement != NULL)
{ Intermediate[pElement->Row] -= Temp * pElement->Real;
pElement = pElement->NextInCol;
}
}
}
/* Backward Substitution. Solves Ux = c.*/
for (I = Size; I > 0; I--)
{ Temp = Intermediate[I];
pElement = Matrix->Diag[I]->NextInRow;
while (pElement != NULL)
{ Temp -= pElement->Real * Intermediate[pElement->Col];
pElement = pElement->NextInRow;
}
Intermediate[I] = Temp;
}
/* Unscramble Intermediate vector while placing data in to Solution vector. */
pExtOrder = &Matrix->IntToExtColMap[Size];
for (I = Size; I > 0; I--)
Solution[*(pExtOrder--)] = Intermediate[I];
return;
#endif /* REAL */
}
#if spCOMPLEX
/*
* SOLVE COMPLEX MATRIX EQUATION
*
* Performs forward elimination and back substitution to find the
* unknown vector from the RHS vector and factored matrix. This
* routine assumes that the pivots are associated with the lower
* triangular (L) matrix and that the diagonal of the upper triangular
* (U) matrix consists of ones. This routine arranges the computation
* in different way than is traditionally used in order to exploit the
* sparsity of the right-hand side. See the reference in spRevision.
*
* >>> Arguments:
* Matrix (char *)
* Pointer to matrix.
* RHS (RealVector)
* RHS is the real portion of the input data array, the right hand
* side. This data is undisturbed and may be reused for other solves.
* Solution (RealVector)
* Solution is the real portion of the output data array. This routine
* is constructed such that RHS and Solution can be the same
* array.
* iRHS (RealVector)
* iRHS is the imaginary portion of the input data array, the right
* hand side. This data is undisturbed and may be reused for other solves.
* If spSEPARATED_COMPLEX_VECTOR is set false, there is no need to
* supply this array.
* iSolution (RealVector)
* iSolution is the imaginary portion of the output data array. This
* routine is constructed such that iRHS and iSolution can be
* the same array. If spSEPARATED_COMPLEX_VECTOR is set false, there is no
* need to supply this array.
*
* >>> Local variables:
* Intermediate (ComplexVector)
* Temporary storage for use in forward elimination and backward
* substitution. Commonly referred to as c, when the LU factorization
* equations are given as Ax = b, Lc = b, Ux = c.
* Local version of Matrix->Intermediate, which was created during
* the initial factorization in function CreateInternalVectors() in the
* matrix factorization module.
* pElement (ElementPtr)
* Pointer used to address elements in both the lower and upper triangle
* matrices.
* pExtOrder (int *)
* Pointer used to sequentially access each entry in IntToExtRowMap
* and IntToExtColMap arrays. Used to quickly scramble and unscramble
* RHS and Solution to account for row and column interchanges.
* pPivot (ElementPtr)
* Pointer that points to current pivot or diagonal element.
* Size (int)
* Size of matrix. Made local to reduce indirection.
* Temp (ComplexNumber)
* Temporary storage for entries in arrays.
*
* >>> 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
SolveComplexMatrix( Matrix, RHS, Solution IMAG_VECTORS )
MatrixPtr Matrix;
RealVector RHS, Solution IMAG_VECTORS;
{
register ElementPtr pElement;
register ComplexVector Intermediate;
register int I, *pExtOrder, Size;
ElementPtr pPivot;
register ComplexVector ExtVector;
ComplexNumber Temp;
/* Begin `SolveComplexMatrix'. */
Size = Matrix->Size;
Intermediate = (ComplexVector)Matrix->Intermediate;
/* 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. */
pExtOrder = &Matrix->IntToExtRowMap[Size];
#if spSEPARATED_COMPLEX_VECTORS
for (I = Size; I > 0; I--)
{ Intermediate[I].Real = RHS[*(pExtOrder)];
Intermediate[I].Imag = iRHS[*(pExtOrder--)];
}
#else
ExtVector = (ComplexVector)RHS;
for (I = Size; I > 0; I--)
Intermediate[I] = ExtVector[*(pExtOrder--)];
#endif
/* Forward substitution. Solves Lc = b.*/
for (I = 1; I <= Size; I++)
{ Temp = Intermediate[I];
/* This step of the substitution is skipped if Temp equals zero. */
if ((Temp.Real != 0.0) OR (Temp.Imag != 0.0))
{ pPivot = Matrix->Diag[I];
/* Cmplx expr: Temp *= (1.0 / Pivot). */
CMPLX_MULT_ASSIGN(Temp, *pPivot);
Intermediate[I] = Temp;
pElement = pPivot->NextInCol;
while (pElement != NULL)
{
/* Cmplx expr: Intermediate[Element->Row] -= Temp * *Element. */
CMPLX_MULT_SUBT_ASSIGN(Intermediate[pElement->Row],
Temp, *pElement);
pElement = pElement->NextInCol;
}
}
}
/* Backward Substitution. Solves Ux = c.*/
for (I = Size; I > 0; I--)
{ Temp = Intermediate[I];
pElement = Matrix->Diag[I]->NextInRow;
while (pElement != NULL)
{
/* Cmplx expr: Temp -= *Element * Intermediate[Element->Col]. */
CMPLX_MULT_SUBT_ASSIGN(Temp, *pElement,Intermediate[pElement->Col]);
pElement = pElement->NextInRow;
}
Intermediate[I] = Temp;
}
/* Unscramble Intermediate vector while placing data in to Solution vector. */
pExtOrder = &Matrix->IntToExtColMap[Size];
#if spSEPARATED_COMPLEX_VECTORS
for (I = Size; I > 0; I--)
{ Solution[*(pExtOrder)] = Intermediate[I].Real;
iSolution[*(pExtOrder--)] = Intermediate[I].Imag;
}
#else
ExtVector = (ComplexVector)Solution;
for (I = Size; I > 0; I--)
ExtVector[*(pExtOrder--)] = Intermediate[I];
#endif
return;
}
#endif /* spCOMPLEX */
#if TRANSPOSE
/*
* SOLVE TRANSPOSED MATRIX EQUATION
*
* Performs forward elimination and back substitution to find the
* unknown vector from the RHS vector and transposed factored
* matrix. This routine is useful when performing sensitivity analysis
* on a circuit using the adjoint method. This routine assumes that
* the pivots are associated with the untransposed lower triangular
* (L) matrix and that the diagonal of the untransposed upper
* triangular (U) matrix consists of ones.
*
* >>> Arguments:
* Matrix (char *)
* Pointer to matrix.
* RHS (RealVector)
* RHS is the input data array, the right hand side. This data is
* undisturbed and may be reused for other solves.
* Solution (RealVector)
* Solution is the output data array. This routine is constructed such that
* RHS and Solution can be the same array.
* iRHS (RealVector)
* iRHS is the imaginary portion of the input data array, the right
* hand side. This data is undisturbed and may be reused for other solves.
* If spSEPARATED_COMPLEX_VECTOR is set false, or if matrix is real, there
* is no need to supply this array.
* iSolution (RealVector)
* iSolution is the imaginary portion of the output data array. This
* routine is constructed such that iRHS and iSolution can be
* the same array. If spSEPARATED_COMPLEX_VECTOR is set false, or if
* matrix is real, there is no need to supply this array.
*
* >>> Local variables:
* Intermediate (RealVector)
* Temporary storage for use in forward elimination and backward
* substitution. Commonly referred to as c, when the LU factorization
* equations are given as Ax = b, Lc = b, Ux = c. Local version of
* Matrix->Intermediate, which was created during the initial
* factorization in function CreateInternalVectors() in the matrix
* factorization module.
* pElement (ElementPtr)
* Pointer used to address elements in both the lower and upper triangle
* matrices.
* pExtOrder (int *)
* Pointer used to sequentially access each entry in IntToExtRowMap
* and IntToExtRowMap arrays. Used to quickly scramble and unscramble
* RHS and Solution to account for row and column interchanges.
* pPivot (ElementPtr)
* Pointer that points to current pivot or diagonal element.
* Size (int)
* Size of matrix. Made local to reduce indirection.
* Temp (RealNumber)
* Temporary storage for entries in arrays.
*
* >>> 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.
*/
/*VARARGS3*/
void
spSolveTransposed( eMatrix, RHS, Solution IMAG_VECTORS )
char *eMatrix;
RealVector RHS, Solution IMAG_VECTORS;
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
register ElementPtr pElement;
register RealVector Intermediate;
register int I, *pExtOrder, Size;
ElementPtr pPivot;
RealNumber Temp;
void SolveComplexTransposedMatrix();
/* Begin `spSolveTransposed'. */
ASSERT( IS_VALID(Matrix) AND IS_FACTORED(Matrix) );
#if spCOMPLEX
if (Matrix->Complex)
{ SolveComplexTransposedMatrix( Matrix, RHS, Solution IMAG_VECTORS );
return;
}
#endif
#if REAL
Size = Matrix->Size;
Intermediate = Matrix->Intermediate;
/* Correct array pointers for ARRAY_OFFSET. */
#if NOT ARRAY_OFFSET
--RHS;
--Solution;
#endif
/* Initialize Intermediate vector. */
pExtOrder = &Matrix->IntToExtColMap[Size];
for (I = Size; I > 0; I--)
Intermediate[I] = RHS[*(pExtOrder--)];
/* Forward elimination. */
for (I = 1; I <= Size; I++)
{
/* This step of the elimination is skipped if Temp equals zero. */
if ((Temp = Intermediate[I]) != 0.0)
{ pElement = Matrix->Diag[I]->NextInRow;
while (pElement != NULL)
{ Intermediate[pElement->Col] -= Temp * pElement->Real;
pElement = pElement->NextInRow;
}
}
}
/* Backward Substitution. */
for (I = Size; I > 0; I--)
{ pPivot = Matrix->Diag[I];
Temp = Intermediate[I];
pElement = pPivot->NextInCol;
while (pElement != NULL)
{ Temp -= pElement->Real * Intermediate[pElement->Row];
pElement = pElement->NextInCol;
}
Intermediate[I] = Temp * pPivot->Real;
}
/* Unscramble Intermediate vector while placing data in to Solution vector. */
pExtOrder = &Matrix->IntToExtRowMap[Size];
for (I = Size; I > 0; I--)
Solution[*(pExtOrder--)] = Intermediate[I];
return;
#endif /* REAL */
}
#endif /* TRANSPOSE */
#if TRANSPOSE AND spCOMPLEX
/*
* SOLVE COMPLEX TRANSPOSED MATRIX EQUATION
*
* Performs forward elimination and back substitution to find the
* unknown vector from the RHS vector and transposed factored
* matrix. This routine is useful when performing sensitivity analysis
* on a circuit using the adjoint method. This routine assumes that
* the pivots are associated with the untransposed lower triangular
* (L) matrix and that the diagonal of the untransposed upper
* triangular (U) matrix consists of ones.
*
* >>> Arguments:
* Matrix (char *)
* Pointer to matrix.
* RHS (RealVector)
* RHS is the input data array, the right hand
* side. This data is undisturbed and may be reused for other solves.
* This vector is only the real portion if the matrix is complex and
* spSEPARATED_COMPLEX_VECTORS is set true.
* Solution (RealVector)
* Solution is the real portion of the output data array. This routine
* is constructed such that RHS and Solution can be the same array.
* This vector is only the real portion if the matrix is complex and
* spSEPARATED_COMPLEX_VECTORS is set true.
* iRHS (RealVector)
* iRHS is the imaginary portion of the input data array, the right
* hand side. This data is undisturbed and may be reused for other solves.
* If either spCOMPLEX or spSEPARATED_COMPLEX_VECTOR is set false, there
* is no need to supply this array.
* iSolution (RealVector)
* iSolution is the imaginary portion of the output data array. This
* routine is constructed such that iRHS and iSolution can be
* the same array. If spCOMPLEX or spSEPARATED_COMPLEX_VECTOR is set
* false, there is no need to supply this array.
*
* >>> Local variables:
* Intermediate (ComplexVector)
* Temporary storage for use in forward elimination and backward
* substitution. Commonly referred to as c, when the LU factorization
* equations are given as Ax = b, Lc = b, Ux = c. Local version of
* Matrix->Intermediate, which was created during
* the initial factorization in function CreateInternalVectors() in the
* matrix factorization module.
* pElement (ElementPtr)
* Pointer used to address elements in both the lower and upper triangle
* matrices.
* pExtOrder (int *)
* Pointer used to sequentially access each entry in IntToExtRowMap
* and IntToExtColMap arrays. Used to quickly scramble and unscramble
* RHS and Solution to account for row and column interchanges.
* pPivot (ElementPtr)
* Pointer that points to current pivot or diagonal element.
* Size (int)
* Size of matrix. Made local to reduce indirection.
* Temp (ComplexNumber)
* Temporary storage for entries in arrays.
*
* >>> 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
SolveComplexTransposedMatrix(Matrix, RHS, Solution IMAG_VECTORS )
MatrixPtr Matrix;
RealVector RHS, Solution IMAG_VECTORS;
{
register ElementPtr pElement;
register ComplexVector Intermediate;
register int I, *pExtOrder, Size;
register ComplexVector ExtVector;
ElementPtr pPivot;
ComplexNumber Temp;
/* Begin `SolveComplexTransposedMatrix'. */
Size = Matrix->Size;
Intermediate = (ComplexVector)Matrix->Intermediate;
/* 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. */
pExtOrder = &Matrix->IntToExtColMap[Size];
#if spSEPARATED_COMPLEX_VECTORS
for (I = Size; I > 0; I--)
{ Intermediate[I].Real = RHS[*(pExtOrder)];
Intermediate[I].Imag = iRHS[*(pExtOrder--)];
}
#else
ExtVector = (ComplexVector)RHS;
for (I = Size; I > 0; I--)
Intermediate[I] = ExtVector[*(pExtOrder--)];
#endif
/* Forward elimination. */
for (I = 1; I <= Size; I++)
{ Temp = Intermediate[I];
/* This step of the elimination is skipped if Temp equals zero. */
if ((Temp.Real != 0.0) OR (Temp.Imag != 0.0))
{ pElement = Matrix->Diag[I]->NextInRow;
while (pElement != NULL)
{
/* Cmplx expr: Intermediate[Element->Col] -= Temp * *Element. */
CMPLX_MULT_SUBT_ASSIGN( Intermediate[pElement->Col],
Temp, *pElement);
pElement = pElement->NextInRow;
}
}
}
/* Backward Substitution. */
for (I = Size; I > 0; I--)
{ pPivot = Matrix->Diag[I];
Temp = Intermediate[I];
pElement = pPivot->NextInCol;
while (pElement != NULL)
{
/* Cmplx expr: Temp -= Intermediate[Element->Row] * *Element. */
CMPLX_MULT_SUBT_ASSIGN(Temp,Intermediate[pElement->Row],*pElement);
pElement = pElement->NextInCol;
}
/* Cmplx expr: Intermediate = Temp * (1.0 / *pPivot). */
CMPLX_MULT(Intermediate[I], Temp, *pPivot);
}
/* Unscramble Intermediate vector while placing data in to Solution vector. */
pExtOrder = &Matrix->IntToExtRowMap[Size];
#if spSEPARATED_COMPLEX_VECTORS
for (I = Size; I > 0; I--)
{ Solution[*(pExtOrder)] = Intermediate[I].Real;
iSolution[*(pExtOrder--)] = Intermediate[I].Imag;
}
#else
ExtVector = (ComplexVector)Solution;
for (I = Size; I > 0; I--)
ExtVector[*(pExtOrder--)] = Intermediate[I];
#endif
return;
}
#endif /* TRANSPOSE AND spCOMPLEX */
@EOF
chmod 644 spSolve.c
exit 0
.