windows-nt/Source/XPSP1/NT/enduser/troubleshoot/bn/vrmatrx.cpp

761 lines
16 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 1997
//
// File: vrmatrx.cpp
//
//--------------------------------------------------------------------------
#include <float.h>
#include <math.h>
#include <bitset>
#include "vrmatrx.h"
VRMATRIX VRMATRIX :: VrmatrixProject ( const VIMD & vimdRowColumnRetain ) const
{
// Returns the projection of this matrix defined by the rows and columns
// in vimdRowColumnRetain.
#define BSETSIZE 100
size_t cDimMax = _cpp_max(CCol(),CRow());
assert( cDimMax < BSETSIZE );
// Build a bitset that keeps track of the rows and columns we're retaining
bitset<BSETSIZE> bset;
for ( int iRowCol = 0; iRowCol < vimdRowColumnRetain.size(); ++iRowCol)
{
bset[ vimdRowColumnRetain[iRowCol] ] = true;
}
int cCol = 0;
int cRow = 0;
for ( iRowCol = 0; iRowCol < cDimMax; iRowCol++ )
{
bool bKeep = bset[iRowCol];
if ( cDimMax >= CCol() && bKeep )
cCol++;
if ( cDimMax >= CRow() && bKeep )
cRow++;
}
// Make sure that a least one row and column are being retained
if ( cCol == 0 || cRow == 0 )
throw GMException(EC_MDVECT_MISUSE,"null matrix projection");
// Construct the projection matrix
VRMATRIX vrmatrix(cRow,cCol);
int iRowProjection = 0;
// Step through every element in this matrix, and insert into the
// projection if the element is to be retained
for ( int iRow = 0; iRow < CRow(); ++iRow )
{
if ( ! bset[iRow] )
{
// This row is excluded from the projection
continue;
}
int iColProjection = 0;
// This row is included... insert the members
// of the row for every column in the projection
for (int iCol = 0; iCol < CCol(); ++iCol )
{
if ( bset[iCol] )
{
vrmatrix(iRowProjection, iColProjection) = self(iRow,iCol);
++iColProjection;
}
}
++iRowProjection;
}
return vrmatrix;
}
VRMATRIXSQ VRMATRIXSQ :: VrmatrixProject ( const VIMD & vimdRowColumnRetain ) const
{
// Returns the projection of this matrix defined by the rows and columns
// in vimdRowColumnRetain.
#define BSETSIZE 100
size_t cDimMax = _cpp_max(CCol(),CRow());
assert( cDimMax < BSETSIZE );
// Build a bitset that keeps track of the rows and columns we're retaining
bitset<BSETSIZE> bset;
for ( int iRowCol = 0; iRowCol < vimdRowColumnRetain.size(); ++iRowCol)
{
bset[ vimdRowColumnRetain[iRowCol] ] = true;
}
int cCol = 0;
int cRow = 0;
for ( iRowCol = 0; iRowCol < cDimMax; iRowCol++ )
{
bool bKeep = bset[iRowCol];
if ( cDimMax >= CCol() && bKeep )
cCol++;
if ( cDimMax >= CRow() && bKeep )
cRow++;
}
VRMATRIXSQ vrmatrix;
// Make sure that a least one row and column are being retained
if ( cCol > 0 && cRow > 0 )
{
// Initialize the projection matrix
vrmatrix.Init(cRow,cCol);
int iRowProjection = 0;
// Step through every element in this matrix, and insert into the
// projection if the element is to be retained
for ( int iRow = 0; iRow < CRow(); ++iRow )
{
if ( ! bset[iRow] )
{
// This row is excluded from the projection
continue;
}
int iColProjection = 0;
// This row is included... insert the members
// of the row for every column in the projection
for (int iCol = 0; iCol < CCol(); ++iCol )
{
if ( bset[iCol] )
{
vrmatrix(iRowProjection, iColProjection) = self(iRow,iCol);
++iColProjection;
}
}
++iRowProjection;
}
}
else
{
vrmatrix.Init(0,0);
}
return vrmatrix;
}
VLREAL VRMATRIX :: VectorRow ( int iRow ) const
{
// Return a copy of the iRow'th row vector of the matrix
if ( iRow >= CRow() )
throw GMException(EC_MDVECT_MISUSE,"invalid matrix projection");
VLREAL vectorRowReturn;
int cCol = CCol();
vectorRowReturn.resize(cCol);
const REAL* rgrealRowMatrix = & self(iRow,0);
for ( int iCol = 0; iCol < cCol; cCol++ )
{
vectorRowReturn[iCol] = rgrealRowMatrix[iCol];
}
// *prv++ = *prm++;
return vectorRowReturn;
}
VLREAL VRMATRIX :: VectorColumn ( int iCol ) const
{
// Return a copy of the iCol'th column vector of the matrix
if ( iCol >= CCol() )
throw GMException(EC_MDVECT_MISUSE,"invalid matrix projection");
VLREAL vectorColReturn;
int cRow = CRow();
vectorColReturn.resize(cRow);
const REAL* rgrealColMatrix = & self(0, iCol);
for ( int iRow = 0; iRow < cRow; iRow++ )
{
vectorColReturn[iRow] = rgrealColMatrix[iRow];
}
return vectorColReturn;
}
VRMATRIX VRMATRIX :: VrmatrixTranspose () const
{
// Return the transpose of this matrix
VRMATRIX vrmatrixTranspose( CCol(), CRow() );
for ( int iRow = 0 ; iRow < CRow() ; iRow++ )
{
for ( int iCol = 0; iCol < CCol(); iCol++ )
{
vrmatrixTranspose(iCol,iRow) = self(iRow,iCol);
}
}
return vrmatrixTranspose;
}
VRMATRIX VRMATRIX::operator * ( const VRMATRIX & matrix ) const
{
if ( ! BCanMultiply( matrix ) )
throw GMException(EC_MDVECT_MISUSE,"invalid matrix multiplication");
// Result matrix
VRMATRIX mat( CRow(), matrix.CCol() );
// Compute distance in flat array between adjacent
// column items in secondary
int icolInc = matrix.second.stride()[0];
const REAL * prrow = & self(0,0);
REAL * prmat = & mat(0,0);
for (int irow = 0; irow < CRow(); irow++)
{
const REAL * prrowt;
for ( int icol = 0; icol < matrix.CCol(); icol++ )
{
prrowt = prrow;
assert( prrowt == & self(irow,0) );
// First column element in "matrix"
const REAL * prcol = & matrix(0,icol);
// Compute the new element
REAL r = 0.0;
for (int i = 0; i < CCol(); i++)
{
assert( prcol == & matrix(i,icol) );
r += *prcol * *prrowt++;
prcol += icolInc;
}
// Store it
*prmat++ = r;
}
prrow = prrowt;
}
return mat;
}
VRMATRIX & VRMATRIX::operator += ( const VRMATRIX & vrmatrixAdd )
{
// Add vrmatrixAdd to this matrix
// Make sure the matrices are of the same dimension
if (! BSameDimension(vrmatrixAdd) )
throw GMException(EC_MDVECT_MISUSE,"inapplicable matrix operator");
// Perform a flat add between all the elements in the matricies
int crealTotal = second._Totlen();
REAL* rgrealSelf = &self(0,0);
const REAL* rgrealMatrixAdd = &vrmatrixAdd(0,0);
for ( int ireal = 0 ; ireal < crealTotal ; ireal++ )
{
rgrealSelf[ireal] += rgrealMatrixAdd[ireal];
}
return self;
}
VRMATRIX & VRMATRIX::operator -= ( const VRMATRIX & vrmatrixMatrixSubtract )
{
// Subtract vrmatrixAdd from this matrix
// Make sure the matrices are of the same dimension
if ( ! BSameDimension( vrmatrixMatrixSubtract ) )
throw GMException(EC_MDVECT_MISUSE,"inapplicable matrix operator");
// Perform a flat subtration between all the elements in the matricies
int crealTotal = second._Totlen();
REAL* rgrealSelf = &self(0,0);
const REAL* rgrealMatrixSubtract = &vrmatrixMatrixSubtract(0,0);
for ( int ireal = 0 ; ireal < crealTotal ; ireal++ )
{
rgrealSelf[ireal] -= rgrealMatrixSubtract[ireal];
}
return self;
}
VRMATRIX & VRMATRIX::operator *= ( REAL rScalar )
{
// Multiply each element in the matrix by rScalar
int crealTotal = second._Totlen();
REAL* rgrealSelf = &self(0,0);
for ( int ireal = 0 ; ireal < crealTotal ; ireal++ )
{
rgrealSelf[ireal] *= rScalar;
}
return self;
}
VRMATRIX & VRMATRIX::operator += ( REAL rScalar )
{
// Add rScalar to each element in the matrix
int crealTotal = second._Totlen();
REAL* rgrealSelf = &self(0,0);
for ( int ireal = 0 ; ireal < crealTotal ; ireal++ )
{
rgrealSelf[ireal] += rScalar;
}
return self;
}
VRMATRIX & VRMATRIX::operator -= ( REAL rScalar )
{
// Subtract rScalar from each element in the matrix
int crealTotal = second._Totlen();
REAL* rgrealSelf = &self(0,0);
for ( int ireal = 0 ; ireal < crealTotal ; ireal++ )
{
rgrealSelf[ireal] -= rScalar;
}
return self;
}
VRMATRIX & VRMATRIX::operator /= ( REAL rScalar )
{
// Divide each element in the matrix by rScalar
int crealTotal = second._Totlen();
REAL* rgrealSelf = &self(0,0);
for ( int ireal = 0 ; ireal < crealTotal ; ireal++ )
{
rgrealSelf[ireal] /= rScalar;
}
return self;
}
VRMATRIXSQ :: VRMATRIXSQ ( const VLREAL & vrColumn, const VLREAL & vrRow )
{
// Constructor for square matrices that takes a column and row vector.
// The initial state of this matrix is the product of the input
// vectors.
// Make sure the vectors are of the same length
if ( vrColumn.size() != vrRow.size() )
throw GMException(EC_MDVECT_MISUSE,"invalid matrix multiplication");
Init( vrColumn.size() );
REAL * prm = & self(0,0);
for ( int iRow = 0; iRow < CRow(); iRow++ )
{
for ( int iCol = 0; iCol < CCol(); iCol++ )
{
*prm++ = vrColumn[iCol] * vrRow[iRow];
}
}
}
VRMATRIXSQ & VRMATRIXSQ::operator *= (const VRMATRIXSQ& matrix)
{
if ( matrix.CRow() != CRow()
|| matrix.CCol() != CRow() )
throw GMException(EC_MDVECT_MISUSE,"invalid matrix multiplication");
// Temporary row for partial result
VLREAL vrrow;
vrrow.resize(CCol());
// Compute distance in flat array between rows
int icolInc = matrix.second.stride()[0];
REAL * prrow = & self(0,0);
const REAL * prmat = & matrix(0,0);
REAL * prtemp0 = & vrrow[0];
for (int irow = 0; irow < CRow(); irow++)
{
REAL * prtemp = prtemp0;
for ( int icol = 0; icol < matrix.CCol(); icol++ )
{
const REAL * prrowt = prrow;
assert( prrowt == & self(irow,0) );
// First column element in "matrix"
const REAL * prcol = & matrix(0,icol);
// Compute the new element
REAL r = 0.0;
for (int i = 0; i < CCol(); i++)
{
assert( prcol == & matrix(i,icol) );
r += *prcol * *prrowt++;
prcol += icolInc;
}
// Store it temporary row vector
*prtemp++ = r;
}
// Update row in self
prtemp = prtemp0;
for ( int icol2 = 0; icol2 < CCol(); icol2++ )
{
*prrow++ = *prtemp++;
}
}
return self;
}
void VRMATRIXSQ::LUDBackSub (const VRMATRIXSQ& matrix)
{
if ( ! matrix.BIsLUDecomposed() )
throw GMException(EC_MDVECT_MISUSE,"matrix not in L-U decomposed form");
for (int icol = 0; icol < CCol(); icol++)
{
int irowNZ = -1;
for (int irow = 0; irow < CRow(); irow++)
{
int irowMax = matrix._vimdRow[irow];
REAL probSum = self(irowMax,icol);
self(irowMax,icol) = self(irow,icol);
if (irowNZ != -1)
{
for (int iMul = irowNZ; iMul < irow; iMul++)
probSum -= matrix(irow,iMul) * self(iMul,icol);
}
else if (probSum != 0.0)
irowNZ = irow;
self(irow,icol) = probSum;
}
for ( irow = CRow(); irow-- > 0; )
{
REAL probSum = self(irow,icol);
for (int iMul = irow + 1; iMul < CRow(); iMul++)
probSum -= matrix(irow,iMul) * self(iMul,icol);
self(irow,icol) = probSum / matrix(irow,irow);
}
}
}
void VRMATRIXSQ::LUDecompose( bool bUseTinyIfSingular )
{
// Perform L-U decomposition; throw exception if singular
// If "use tiny" is set, pivots at zero are replaced with
// RTINY value (1.0e-20)
// Check that this matrix is not already LU decomposed
if ( BIsLUDecomposed() )
throw GMException(EC_MDVECT_MISUSE,"matrix is already in L-U decomposed form");
if (CRow() == 0)
return; // trivial case
int cDim = CRow();
_vimdRow.resize(cDim);
VLREAL vlrealOverMax;
vlrealOverMax.resize(cDim);
_iSign = 1;
for (int iRow = 0; iRow < cDim; iRow++)
{
REAL realMax = 0.0;
for (int iCol = 0; iCol < cDim; iCol++)
{
REAL realAbs = fabs(self(iRow,iCol));
if (realAbs > realMax)
realMax = realAbs;
}
if (realMax == 0.0)
{
// Every element in the row is zero: this is a singular matrix
throw GMException(EC_MDVECT_MISUSE,"matrix is singular");
}
vlrealOverMax[iRow] = 1.0 / realMax;
}
for (int iCol = 0; iCol < cDim; iCol++)
{
for (int iRow = 0; iRow < iCol; iRow++)
{
REAL realSum = self(iRow,iCol);
for (int iMul = 0; iMul < iRow; iMul++)
realSum -= self(iRow,iMul) * self(iMul,iCol);
self(iRow,iCol) = realSum;
}
REAL realMax = 0.0;
int iRowMax = 0;
for ( iRow = iCol; iRow < cDim; iRow++)
{
REAL realSum = self(iRow,iCol);
for (int iMul = 0; iMul < iCol; iMul++)
realSum -= self(iRow,iMul) * self(iMul,iCol);
self(iRow,iCol) = realSum;
REAL realAbs = vlrealOverMax[iRow] * fabs(realSum);
if (realAbs >= realMax)
{
realMax = realAbs;
iRowMax = iRow;
}
}
if (iRowMax != iCol)
{
// we need to interchange rows
_iSign *= -1;
vlrealOverMax[iRowMax] = vlrealOverMax[iCol];
InterchangeRows(iRowMax,iCol);
}
_vimdRow[iCol] = iRowMax;
REAL & rPivot = self(iCol,iCol);
if ( rPivot == 0.0 )
{
if ( ! bUseTinyIfSingular )
{
// This is a singular matrix: throw exceptioin
throw GMException(EC_MDVECT_MISUSE,"matrix is singular");
}
rPivot = RTINY;
}
REAL rScale = 1.0 / rPivot;
for ( iRow = iCol + 1; iRow < cDim; iRow++)
self(iRow,iCol) *= rScale;
}
}
void VRMATRIXSQ::Invert( bool bUseTinyIfSingular )
{
// Invert; throw exception if singular. If not in L-U form,
// L-U Decomp is called.
if ( ! BIsLUDecomposed() )
{
LUDecompose( bUseTinyIfSingular );
}
VRMATRIXSQ matrixOne(CRow());
// Create the identity matrix
for (int iDim1 = 0; iDim1 < CRow(); iDim1++)
{
for (int iDim2 = 0; iDim2 < CRow(); iDim2++)
matrixOne(iDim1, iDim2) = iDim1 == iDim2 ? 1.0 : 0.0;
}
matrixOne.LUDBackSub(self);
for ( iDim1 = 0; iDim1 < CRow(); iDim1++)
{
for (int iDim2 = 0; iDim2 < CRow(); iDim2++)
self(iDim1, iDim2) = matrixOne(iDim1, iDim2);
}
// Clear l-u decomp values
_vimdRow.resize(0);
}
DBL VRMATRIXSQ::DblDeterminant()
{
DBL dblDet = _iSign;
if ( CRow() > 0 && ! BIsLUDecomposed() )
LUDecompose();
// Once the matrix has been LU decomposed, the determinant can be
// obtained by simply multiplying the elements of the diagonal
for (int iRow = 0; iRow < CRow(); iRow++)
{
dblDet *= self(iRow,iRow);
}
return dblDet;
}
DBL VRMATRIXSQ :: DblAddLogDiagonal() const
// Adds the log of each element in the diagonal and returns the sum.
{
DBL dblLogDiag = 0;
// bool bPositive = _iSign == 1;
bool bPositive = 1;
for (int iRow = 0; iRow < CRow(); iRow++)
{
if (self(iRow,iRow) < 0)
bPositive = !bPositive;
// Assert that the element is not zero. We should probably
// throw an exception here instead.
assert(self(iRow,iRow) != 0);
dblLogDiag += log (fabs(self(iRow,iRow)));
}
if (!bPositive)
{
// Got a negative determinant, so we can't take the log... throw
// an exception
return false;
}
return dblLogDiag;
}
DBL VRMATRIXSQ :: DblLogDeterminant()
{
// Return the log of the determinant. If not in L-U form,
// L-U Decomp is called. Throws exception if negative.
if ( CRow() > 0 && ! BIsLUDecomposed() )
LUDecompose();
DBL dblLogDet = 0;
bool bPositive = _iSign == 1;
for (int iRow = 0; iRow < CRow(); iRow++)
{
if (self(iRow,iRow) < 0)
bPositive = !bPositive;
// Assert that the deterninant is not zero. We should probably
// throw an exception here instead.
assert(self(iRow,iRow) != 0);
dblLogDet += log (fabs(self(iRow,iRow)));
}
if (!bPositive)
{
// Got a negative determinant, so we can't take the log... throw
// an exception
return false;
}
return dblLogDet;
}
void VRMATRIXSQ :: GetLUDecompose( VRMATRIXSQ & vmatrixResult, bool bUseTinyIfSingular ) const
{
// Set vrmatResult to be the result of performing an L-U
// decomposition on the matrix. Will throw exception if
// the matrix is singular
// If "use tiny" is set, pivots at zero are replaced with
// RTINY value (1.0e-20)
// Copy this matrix into vmatrixResult...
vmatrixResult = self;
// .. and perform the decomposition
vmatrixResult.LUDecompose( bUseTinyIfSingular );
}
void VRMATRIXSQ :: GetInverse( VRMATRIXSQ & vmatrixResult, bool bUseTinyIfSingular ) const
{
// Set vrmatResult to the inverse of the matrix.
// Will throw an exception if the matrix is singular.
// Copy this matrix into vmatrixResult...
vmatrixResult = self;
/// ...and invert
vmatrixResult.Invert( bUseTinyIfSingular );
}
void VRMATRIXSQ :: GetDblDeterminant( DBL& dblDeterminant, VRMATRIXSQ & vmatrixResult ) const
{
// Get the determinant without modifying (LU decomposing) the matrix.
// vmatrixResult will contain the LU decomposed version of the matrix.
// Copy this matrix into vmatrixResult...
vmatrixResult = self;
dblDeterminant = vmatrixResult.DblDeterminant();
}
void VRMATRIXSQ :: GetDblLogDeterminant( DBL& dblLogDeterminant, VRMATRIXSQ & vmatrixResult ) const
{
// Get the log of determinant without modifying (LU decomposing) the matrix.
// vmatrixResult will contain the LU decomposed version of the matrix.
vmatrixResult = self;
dblLogDeterminant = vmatrixResult.DblLogDeterminant();
}