//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1997 // // File: vrmatrx.cpp // //-------------------------------------------------------------------------- #include #include #include #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 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 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(); }