Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

760 lines
16 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1997
  6. //
  7. // File: vrmatrx.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include <float.h>
  11. #include <math.h>
  12. #include <bitset>
  13. #include "vrmatrx.h"
  14. VRMATRIX VRMATRIX :: VrmatrixProject ( const VIMD & vimdRowColumnRetain ) const
  15. {
  16. // Returns the projection of this matrix defined by the rows and columns
  17. // in vimdRowColumnRetain.
  18. #define BSETSIZE 100
  19. size_t cDimMax = _cpp_max(CCol(),CRow());
  20. assert( cDimMax < BSETSIZE );
  21. // Build a bitset that keeps track of the rows and columns we're retaining
  22. bitset<BSETSIZE> bset;
  23. for ( int iRowCol = 0; iRowCol < vimdRowColumnRetain.size(); ++iRowCol)
  24. {
  25. bset[ vimdRowColumnRetain[iRowCol] ] = true;
  26. }
  27. int cCol = 0;
  28. int cRow = 0;
  29. for ( iRowCol = 0; iRowCol < cDimMax; iRowCol++ )
  30. {
  31. bool bKeep = bset[iRowCol];
  32. if ( cDimMax >= CCol() && bKeep )
  33. cCol++;
  34. if ( cDimMax >= CRow() && bKeep )
  35. cRow++;
  36. }
  37. // Make sure that a least one row and column are being retained
  38. if ( cCol == 0 || cRow == 0 )
  39. throw GMException(EC_MDVECT_MISUSE,"null matrix projection");
  40. // Construct the projection matrix
  41. VRMATRIX vrmatrix(cRow,cCol);
  42. int iRowProjection = 0;
  43. // Step through every element in this matrix, and insert into the
  44. // projection if the element is to be retained
  45. for ( int iRow = 0; iRow < CRow(); ++iRow )
  46. {
  47. if ( ! bset[iRow] )
  48. {
  49. // This row is excluded from the projection
  50. continue;
  51. }
  52. int iColProjection = 0;
  53. // This row is included... insert the members
  54. // of the row for every column in the projection
  55. for (int iCol = 0; iCol < CCol(); ++iCol )
  56. {
  57. if ( bset[iCol] )
  58. {
  59. vrmatrix(iRowProjection, iColProjection) = self(iRow,iCol);
  60. ++iColProjection;
  61. }
  62. }
  63. ++iRowProjection;
  64. }
  65. return vrmatrix;
  66. }
  67. VRMATRIXSQ VRMATRIXSQ :: VrmatrixProject ( const VIMD & vimdRowColumnRetain ) const
  68. {
  69. // Returns the projection of this matrix defined by the rows and columns
  70. // in vimdRowColumnRetain.
  71. #define BSETSIZE 100
  72. size_t cDimMax = _cpp_max(CCol(),CRow());
  73. assert( cDimMax < BSETSIZE );
  74. // Build a bitset that keeps track of the rows and columns we're retaining
  75. bitset<BSETSIZE> bset;
  76. for ( int iRowCol = 0; iRowCol < vimdRowColumnRetain.size(); ++iRowCol)
  77. {
  78. bset[ vimdRowColumnRetain[iRowCol] ] = true;
  79. }
  80. int cCol = 0;
  81. int cRow = 0;
  82. for ( iRowCol = 0; iRowCol < cDimMax; iRowCol++ )
  83. {
  84. bool bKeep = bset[iRowCol];
  85. if ( cDimMax >= CCol() && bKeep )
  86. cCol++;
  87. if ( cDimMax >= CRow() && bKeep )
  88. cRow++;
  89. }
  90. VRMATRIXSQ vrmatrix;
  91. // Make sure that a least one row and column are being retained
  92. if ( cCol > 0 && cRow > 0 )
  93. {
  94. // Initialize the projection matrix
  95. vrmatrix.Init(cRow,cCol);
  96. int iRowProjection = 0;
  97. // Step through every element in this matrix, and insert into the
  98. // projection if the element is to be retained
  99. for ( int iRow = 0; iRow < CRow(); ++iRow )
  100. {
  101. if ( ! bset[iRow] )
  102. {
  103. // This row is excluded from the projection
  104. continue;
  105. }
  106. int iColProjection = 0;
  107. // This row is included... insert the members
  108. // of the row for every column in the projection
  109. for (int iCol = 0; iCol < CCol(); ++iCol )
  110. {
  111. if ( bset[iCol] )
  112. {
  113. vrmatrix(iRowProjection, iColProjection) = self(iRow,iCol);
  114. ++iColProjection;
  115. }
  116. }
  117. ++iRowProjection;
  118. }
  119. }
  120. else
  121. {
  122. vrmatrix.Init(0,0);
  123. }
  124. return vrmatrix;
  125. }
  126. VLREAL VRMATRIX :: VectorRow ( int iRow ) const
  127. {
  128. // Return a copy of the iRow'th row vector of the matrix
  129. if ( iRow >= CRow() )
  130. throw GMException(EC_MDVECT_MISUSE,"invalid matrix projection");
  131. VLREAL vectorRowReturn;
  132. int cCol = CCol();
  133. vectorRowReturn.resize(cCol);
  134. const REAL* rgrealRowMatrix = & self(iRow,0);
  135. for ( int iCol = 0; iCol < cCol; cCol++ )
  136. {
  137. vectorRowReturn[iCol] = rgrealRowMatrix[iCol];
  138. }
  139. // *prv++ = *prm++;
  140. return vectorRowReturn;
  141. }
  142. VLREAL VRMATRIX :: VectorColumn ( int iCol ) const
  143. {
  144. // Return a copy of the iCol'th column vector of the matrix
  145. if ( iCol >= CCol() )
  146. throw GMException(EC_MDVECT_MISUSE,"invalid matrix projection");
  147. VLREAL vectorColReturn;
  148. int cRow = CRow();
  149. vectorColReturn.resize(cRow);
  150. const REAL* rgrealColMatrix = & self(0, iCol);
  151. for ( int iRow = 0; iRow < cRow; iRow++ )
  152. {
  153. vectorColReturn[iRow] = rgrealColMatrix[iRow];
  154. }
  155. return vectorColReturn;
  156. }
  157. VRMATRIX VRMATRIX :: VrmatrixTranspose () const
  158. {
  159. // Return the transpose of this matrix
  160. VRMATRIX vrmatrixTranspose( CCol(), CRow() );
  161. for ( int iRow = 0 ; iRow < CRow() ; iRow++ )
  162. {
  163. for ( int iCol = 0; iCol < CCol(); iCol++ )
  164. {
  165. vrmatrixTranspose(iCol,iRow) = self(iRow,iCol);
  166. }
  167. }
  168. return vrmatrixTranspose;
  169. }
  170. VRMATRIX VRMATRIX::operator * ( const VRMATRIX & matrix ) const
  171. {
  172. if ( ! BCanMultiply( matrix ) )
  173. throw GMException(EC_MDVECT_MISUSE,"invalid matrix multiplication");
  174. // Result matrix
  175. VRMATRIX mat( CRow(), matrix.CCol() );
  176. // Compute distance in flat array between adjacent
  177. // column items in secondary
  178. int icolInc = matrix.second.stride()[0];
  179. const REAL * prrow = & self(0,0);
  180. REAL * prmat = & mat(0,0);
  181. for (int irow = 0; irow < CRow(); irow++)
  182. {
  183. const REAL * prrowt;
  184. for ( int icol = 0; icol < matrix.CCol(); icol++ )
  185. {
  186. prrowt = prrow;
  187. assert( prrowt == & self(irow,0) );
  188. // First column element in "matrix"
  189. const REAL * prcol = & matrix(0,icol);
  190. // Compute the new element
  191. REAL r = 0.0;
  192. for (int i = 0; i < CCol(); i++)
  193. {
  194. assert( prcol == & matrix(i,icol) );
  195. r += *prcol * *prrowt++;
  196. prcol += icolInc;
  197. }
  198. // Store it
  199. *prmat++ = r;
  200. }
  201. prrow = prrowt;
  202. }
  203. return mat;
  204. }
  205. VRMATRIX & VRMATRIX::operator += ( const VRMATRIX & vrmatrixAdd )
  206. {
  207. // Add vrmatrixAdd to this matrix
  208. // Make sure the matrices are of the same dimension
  209. if (! BSameDimension(vrmatrixAdd) )
  210. throw GMException(EC_MDVECT_MISUSE,"inapplicable matrix operator");
  211. // Perform a flat add between all the elements in the matricies
  212. int crealTotal = second._Totlen();
  213. REAL* rgrealSelf = &self(0,0);
  214. const REAL* rgrealMatrixAdd = &vrmatrixAdd(0,0);
  215. for ( int ireal = 0 ; ireal < crealTotal ; ireal++ )
  216. {
  217. rgrealSelf[ireal] += rgrealMatrixAdd[ireal];
  218. }
  219. return self;
  220. }
  221. VRMATRIX & VRMATRIX::operator -= ( const VRMATRIX & vrmatrixMatrixSubtract )
  222. {
  223. // Subtract vrmatrixAdd from this matrix
  224. // Make sure the matrices are of the same dimension
  225. if ( ! BSameDimension( vrmatrixMatrixSubtract ) )
  226. throw GMException(EC_MDVECT_MISUSE,"inapplicable matrix operator");
  227. // Perform a flat subtration between all the elements in the matricies
  228. int crealTotal = second._Totlen();
  229. REAL* rgrealSelf = &self(0,0);
  230. const REAL* rgrealMatrixSubtract = &vrmatrixMatrixSubtract(0,0);
  231. for ( int ireal = 0 ; ireal < crealTotal ; ireal++ )
  232. {
  233. rgrealSelf[ireal] -= rgrealMatrixSubtract[ireal];
  234. }
  235. return self;
  236. }
  237. VRMATRIX & VRMATRIX::operator *= ( REAL rScalar )
  238. {
  239. // Multiply each element in the matrix by rScalar
  240. int crealTotal = second._Totlen();
  241. REAL* rgrealSelf = &self(0,0);
  242. for ( int ireal = 0 ; ireal < crealTotal ; ireal++ )
  243. {
  244. rgrealSelf[ireal] *= rScalar;
  245. }
  246. return self;
  247. }
  248. VRMATRIX & VRMATRIX::operator += ( REAL rScalar )
  249. {
  250. // Add rScalar to each element in the matrix
  251. int crealTotal = second._Totlen();
  252. REAL* rgrealSelf = &self(0,0);
  253. for ( int ireal = 0 ; ireal < crealTotal ; ireal++ )
  254. {
  255. rgrealSelf[ireal] += rScalar;
  256. }
  257. return self;
  258. }
  259. VRMATRIX & VRMATRIX::operator -= ( REAL rScalar )
  260. {
  261. // Subtract rScalar from each element in the matrix
  262. int crealTotal = second._Totlen();
  263. REAL* rgrealSelf = &self(0,0);
  264. for ( int ireal = 0 ; ireal < crealTotal ; ireal++ )
  265. {
  266. rgrealSelf[ireal] -= rScalar;
  267. }
  268. return self;
  269. }
  270. VRMATRIX & VRMATRIX::operator /= ( REAL rScalar )
  271. {
  272. // Divide each element in the matrix by rScalar
  273. int crealTotal = second._Totlen();
  274. REAL* rgrealSelf = &self(0,0);
  275. for ( int ireal = 0 ; ireal < crealTotal ; ireal++ )
  276. {
  277. rgrealSelf[ireal] /= rScalar;
  278. }
  279. return self;
  280. }
  281. VRMATRIXSQ :: VRMATRIXSQ ( const VLREAL & vrColumn, const VLREAL & vrRow )
  282. {
  283. // Constructor for square matrices that takes a column and row vector.
  284. // The initial state of this matrix is the product of the input
  285. // vectors.
  286. // Make sure the vectors are of the same length
  287. if ( vrColumn.size() != vrRow.size() )
  288. throw GMException(EC_MDVECT_MISUSE,"invalid matrix multiplication");
  289. Init( vrColumn.size() );
  290. REAL * prm = & self(0,0);
  291. for ( int iRow = 0; iRow < CRow(); iRow++ )
  292. {
  293. for ( int iCol = 0; iCol < CCol(); iCol++ )
  294. {
  295. *prm++ = vrColumn[iCol] * vrRow[iRow];
  296. }
  297. }
  298. }
  299. VRMATRIXSQ & VRMATRIXSQ::operator *= (const VRMATRIXSQ& matrix)
  300. {
  301. if ( matrix.CRow() != CRow()
  302. || matrix.CCol() != CRow() )
  303. throw GMException(EC_MDVECT_MISUSE,"invalid matrix multiplication");
  304. // Temporary row for partial result
  305. VLREAL vrrow;
  306. vrrow.resize(CCol());
  307. // Compute distance in flat array between rows
  308. int icolInc = matrix.second.stride()[0];
  309. REAL * prrow = & self(0,0);
  310. const REAL * prmat = & matrix(0,0);
  311. REAL * prtemp0 = & vrrow[0];
  312. for (int irow = 0; irow < CRow(); irow++)
  313. {
  314. REAL * prtemp = prtemp0;
  315. for ( int icol = 0; icol < matrix.CCol(); icol++ )
  316. {
  317. const REAL * prrowt = prrow;
  318. assert( prrowt == & self(irow,0) );
  319. // First column element in "matrix"
  320. const REAL * prcol = & matrix(0,icol);
  321. // Compute the new element
  322. REAL r = 0.0;
  323. for (int i = 0; i < CCol(); i++)
  324. {
  325. assert( prcol == & matrix(i,icol) );
  326. r += *prcol * *prrowt++;
  327. prcol += icolInc;
  328. }
  329. // Store it temporary row vector
  330. *prtemp++ = r;
  331. }
  332. // Update row in self
  333. prtemp = prtemp0;
  334. for ( int icol2 = 0; icol2 < CCol(); icol2++ )
  335. {
  336. *prrow++ = *prtemp++;
  337. }
  338. }
  339. return self;
  340. }
  341. void VRMATRIXSQ::LUDBackSub (const VRMATRIXSQ& matrix)
  342. {
  343. if ( ! matrix.BIsLUDecomposed() )
  344. throw GMException(EC_MDVECT_MISUSE,"matrix not in L-U decomposed form");
  345. for (int icol = 0; icol < CCol(); icol++)
  346. {
  347. int irowNZ = -1;
  348. for (int irow = 0; irow < CRow(); irow++)
  349. {
  350. int irowMax = matrix._vimdRow[irow];
  351. REAL probSum = self(irowMax,icol);
  352. self(irowMax,icol) = self(irow,icol);
  353. if (irowNZ != -1)
  354. {
  355. for (int iMul = irowNZ; iMul < irow; iMul++)
  356. probSum -= matrix(irow,iMul) * self(iMul,icol);
  357. }
  358. else if (probSum != 0.0)
  359. irowNZ = irow;
  360. self(irow,icol) = probSum;
  361. }
  362. for ( irow = CRow(); irow-- > 0; )
  363. {
  364. REAL probSum = self(irow,icol);
  365. for (int iMul = irow + 1; iMul < CRow(); iMul++)
  366. probSum -= matrix(irow,iMul) * self(iMul,icol);
  367. self(irow,icol) = probSum / matrix(irow,irow);
  368. }
  369. }
  370. }
  371. void VRMATRIXSQ::LUDecompose( bool bUseTinyIfSingular )
  372. {
  373. // Perform L-U decomposition; throw exception if singular
  374. // If "use tiny" is set, pivots at zero are replaced with
  375. // RTINY value (1.0e-20)
  376. // Check that this matrix is not already LU decomposed
  377. if ( BIsLUDecomposed() )
  378. throw GMException(EC_MDVECT_MISUSE,"matrix is already in L-U decomposed form");
  379. if (CRow() == 0)
  380. return; // trivial case
  381. int cDim = CRow();
  382. _vimdRow.resize(cDim);
  383. VLREAL vlrealOverMax;
  384. vlrealOverMax.resize(cDim);
  385. _iSign = 1;
  386. for (int iRow = 0; iRow < cDim; iRow++)
  387. {
  388. REAL realMax = 0.0;
  389. for (int iCol = 0; iCol < cDim; iCol++)
  390. {
  391. REAL realAbs = fabs(self(iRow,iCol));
  392. if (realAbs > realMax)
  393. realMax = realAbs;
  394. }
  395. if (realMax == 0.0)
  396. {
  397. // Every element in the row is zero: this is a singular matrix
  398. throw GMException(EC_MDVECT_MISUSE,"matrix is singular");
  399. }
  400. vlrealOverMax[iRow] = 1.0 / realMax;
  401. }
  402. for (int iCol = 0; iCol < cDim; iCol++)
  403. {
  404. for (int iRow = 0; iRow < iCol; iRow++)
  405. {
  406. REAL realSum = self(iRow,iCol);
  407. for (int iMul = 0; iMul < iRow; iMul++)
  408. realSum -= self(iRow,iMul) * self(iMul,iCol);
  409. self(iRow,iCol) = realSum;
  410. }
  411. REAL realMax = 0.0;
  412. int iRowMax = 0;
  413. for ( iRow = iCol; iRow < cDim; iRow++)
  414. {
  415. REAL realSum = self(iRow,iCol);
  416. for (int iMul = 0; iMul < iCol; iMul++)
  417. realSum -= self(iRow,iMul) * self(iMul,iCol);
  418. self(iRow,iCol) = realSum;
  419. REAL realAbs = vlrealOverMax[iRow] * fabs(realSum);
  420. if (realAbs >= realMax)
  421. {
  422. realMax = realAbs;
  423. iRowMax = iRow;
  424. }
  425. }
  426. if (iRowMax != iCol)
  427. {
  428. // we need to interchange rows
  429. _iSign *= -1;
  430. vlrealOverMax[iRowMax] = vlrealOverMax[iCol];
  431. InterchangeRows(iRowMax,iCol);
  432. }
  433. _vimdRow[iCol] = iRowMax;
  434. REAL & rPivot = self(iCol,iCol);
  435. if ( rPivot == 0.0 )
  436. {
  437. if ( ! bUseTinyIfSingular )
  438. {
  439. // This is a singular matrix: throw exceptioin
  440. throw GMException(EC_MDVECT_MISUSE,"matrix is singular");
  441. }
  442. rPivot = RTINY;
  443. }
  444. REAL rScale = 1.0 / rPivot;
  445. for ( iRow = iCol + 1; iRow < cDim; iRow++)
  446. self(iRow,iCol) *= rScale;
  447. }
  448. }
  449. void VRMATRIXSQ::Invert( bool bUseTinyIfSingular )
  450. {
  451. // Invert; throw exception if singular. If not in L-U form,
  452. // L-U Decomp is called.
  453. if ( ! BIsLUDecomposed() )
  454. {
  455. LUDecompose( bUseTinyIfSingular );
  456. }
  457. VRMATRIXSQ matrixOne(CRow());
  458. // Create the identity matrix
  459. for (int iDim1 = 0; iDim1 < CRow(); iDim1++)
  460. {
  461. for (int iDim2 = 0; iDim2 < CRow(); iDim2++)
  462. matrixOne(iDim1, iDim2) = iDim1 == iDim2 ? 1.0 : 0.0;
  463. }
  464. matrixOne.LUDBackSub(self);
  465. for ( iDim1 = 0; iDim1 < CRow(); iDim1++)
  466. {
  467. for (int iDim2 = 0; iDim2 < CRow(); iDim2++)
  468. self(iDim1, iDim2) = matrixOne(iDim1, iDim2);
  469. }
  470. // Clear l-u decomp values
  471. _vimdRow.resize(0);
  472. }
  473. DBL VRMATRIXSQ::DblDeterminant()
  474. {
  475. DBL dblDet = _iSign;
  476. if ( CRow() > 0 && ! BIsLUDecomposed() )
  477. LUDecompose();
  478. // Once the matrix has been LU decomposed, the determinant can be
  479. // obtained by simply multiplying the elements of the diagonal
  480. for (int iRow = 0; iRow < CRow(); iRow++)
  481. {
  482. dblDet *= self(iRow,iRow);
  483. }
  484. return dblDet;
  485. }
  486. DBL VRMATRIXSQ :: DblAddLogDiagonal() const
  487. // Adds the log of each element in the diagonal and returns the sum.
  488. {
  489. DBL dblLogDiag = 0;
  490. // bool bPositive = _iSign == 1;
  491. bool bPositive = 1;
  492. for (int iRow = 0; iRow < CRow(); iRow++)
  493. {
  494. if (self(iRow,iRow) < 0)
  495. bPositive = !bPositive;
  496. // Assert that the element is not zero. We should probably
  497. // throw an exception here instead.
  498. assert(self(iRow,iRow) != 0);
  499. dblLogDiag += log (fabs(self(iRow,iRow)));
  500. }
  501. if (!bPositive)
  502. {
  503. // Got a negative determinant, so we can't take the log... throw
  504. // an exception
  505. return false;
  506. }
  507. return dblLogDiag;
  508. }
  509. DBL VRMATRIXSQ :: DblLogDeterminant()
  510. {
  511. // Return the log of the determinant. If not in L-U form,
  512. // L-U Decomp is called. Throws exception if negative.
  513. if ( CRow() > 0 && ! BIsLUDecomposed() )
  514. LUDecompose();
  515. DBL dblLogDet = 0;
  516. bool bPositive = _iSign == 1;
  517. for (int iRow = 0; iRow < CRow(); iRow++)
  518. {
  519. if (self(iRow,iRow) < 0)
  520. bPositive = !bPositive;
  521. // Assert that the deterninant is not zero. We should probably
  522. // throw an exception here instead.
  523. assert(self(iRow,iRow) != 0);
  524. dblLogDet += log (fabs(self(iRow,iRow)));
  525. }
  526. if (!bPositive)
  527. {
  528. // Got a negative determinant, so we can't take the log... throw
  529. // an exception
  530. return false;
  531. }
  532. return dblLogDet;
  533. }
  534. void VRMATRIXSQ :: GetLUDecompose( VRMATRIXSQ & vmatrixResult, bool bUseTinyIfSingular ) const
  535. {
  536. // Set vrmatResult to be the result of performing an L-U
  537. // decomposition on the matrix. Will throw exception if
  538. // the matrix is singular
  539. // If "use tiny" is set, pivots at zero are replaced with
  540. // RTINY value (1.0e-20)
  541. // Copy this matrix into vmatrixResult...
  542. vmatrixResult = self;
  543. // .. and perform the decomposition
  544. vmatrixResult.LUDecompose( bUseTinyIfSingular );
  545. }
  546. void VRMATRIXSQ :: GetInverse( VRMATRIXSQ & vmatrixResult, bool bUseTinyIfSingular ) const
  547. {
  548. // Set vrmatResult to the inverse of the matrix.
  549. // Will throw an exception if the matrix is singular.
  550. // Copy this matrix into vmatrixResult...
  551. vmatrixResult = self;
  552. /// ...and invert
  553. vmatrixResult.Invert( bUseTinyIfSingular );
  554. }
  555. void VRMATRIXSQ :: GetDblDeterminant( DBL& dblDeterminant, VRMATRIXSQ & vmatrixResult ) const
  556. {
  557. // Get the determinant without modifying (LU decomposing) the matrix.
  558. // vmatrixResult will contain the LU decomposed version of the matrix.
  559. // Copy this matrix into vmatrixResult...
  560. vmatrixResult = self;
  561. dblDeterminant = vmatrixResult.DblDeterminant();
  562. }
  563. void VRMATRIXSQ :: GetDblLogDeterminant( DBL& dblLogDeterminant, VRMATRIXSQ & vmatrixResult ) const
  564. {
  565. // Get the log of determinant without modifying (LU decomposing) the matrix.
  566. // vmatrixResult will contain the LU decomposed version of the matrix.
  567. vmatrixResult = self;
  568. dblLogDeterminant = vmatrixResult.DblLogDeterminant();
  569. }