GMatrix.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/GVector.h>
  9. #include <Mathematics/GaussianElimination.h>
  10. #include <algorithm>
  11. namespace WwiseGTE
  12. {
  13. template <typename Real>
  14. class GMatrix
  15. {
  16. public:
  17. // The table is length zero and mNumRows and mNumCols are set to zero.
  18. GMatrix()
  19. :
  20. mNumRows(0),
  21. mNumCols(0)
  22. {
  23. }
  24. // The table is length numRows*numCols and the elements are
  25. // initialized to zero.
  26. GMatrix(int numRows, int numCols)
  27. {
  28. SetSize(numRows, numCols);
  29. std::fill(mElements.begin(), mElements.end(), (Real)0);
  30. }
  31. // For 0 <= r < numRows and 0 <= c < numCols, element (r,c) is 1 and
  32. // all others are 0. If either of r or c is invalid, the zero matrix
  33. // is created. This is a convenience for creating the standard
  34. // Euclidean basis matrices; see also MakeUnit(int,int) and
  35. // Unit(int,int).
  36. GMatrix(int numRows, int numCols, int r, int c)
  37. {
  38. SetSize(numRows, numCols);
  39. MakeUnit(r, c);
  40. }
  41. // The copy constructor, destructor, and assignment operator are
  42. // generated by the compiler.
  43. // Member access for which the storage representation is transparent.
  44. // The matrix entry in row r and column c is A(r,c). The first
  45. // operator() returns a const reference rather than a Real value.
  46. // This supports writing via standard file operations that require a
  47. // const pointer to data.
  48. void SetSize(int numRows, int numCols)
  49. {
  50. if (numRows > 0 && numCols > 0)
  51. {
  52. mNumRows = numRows;
  53. mNumCols = numCols;
  54. mElements.resize(mNumRows * mNumCols);
  55. }
  56. else
  57. {
  58. mNumRows = 0;
  59. mNumCols = 0;
  60. mElements.clear();
  61. }
  62. }
  63. inline void GetSize(int& numRows, int& numCols) const
  64. {
  65. numRows = mNumRows;
  66. numCols = mNumCols;
  67. }
  68. inline int GetNumRows() const
  69. {
  70. return mNumRows;
  71. }
  72. inline int GetNumCols() const
  73. {
  74. return mNumCols;
  75. }
  76. inline int GetNumElements() const
  77. {
  78. return static_cast<int>(mElements.size());
  79. }
  80. inline Real const& operator()(int r, int c) const
  81. {
  82. if (0 <= r && r < GetNumRows() && 0 <= c && c < GetNumCols())
  83. {
  84. #if defined(GTE_USE_ROW_MAJOR)
  85. return mElements[c + mNumCols * r];
  86. #else
  87. return mElements[r + mNumRows * c];
  88. #endif
  89. }
  90. LogError("Invalid index.");
  91. }
  92. inline Real& operator()(int r, int c)
  93. {
  94. if (0 <= r && r < GetNumRows() && 0 <= c && c < GetNumCols())
  95. {
  96. #if defined(GTE_USE_ROW_MAJOR)
  97. return mElements[c + mNumCols * r];
  98. #else
  99. return mElements[r + mNumRows * c];
  100. #endif
  101. }
  102. LogError("Invalid index.");
  103. }
  104. // Member access by rows or by columns. The input vectors must have
  105. // the correct number of elements for the matrix size.
  106. void SetRow(int r, GVector<Real> const& vec)
  107. {
  108. if (0 <= r && r < mNumRows)
  109. {
  110. if (vec.GetSize() == GetNumCols())
  111. {
  112. for (int c = 0; c < mNumCols; ++c)
  113. {
  114. operator()(r, c) = vec[c];
  115. }
  116. }
  117. LogError("Mismatched sizes.");
  118. }
  119. LogError("Invalid index.");
  120. }
  121. void SetCol(int c, GVector<Real> const& vec)
  122. {
  123. if (0 <= c && c < mNumCols)
  124. {
  125. if (vec.GetSize() == GetNumRows())
  126. {
  127. for (int r = 0; r < mNumRows; ++r)
  128. {
  129. operator()(r, c) = vec[r];
  130. }
  131. return;
  132. }
  133. LogError("Mismatched sizes.");
  134. }
  135. LogError("Invalid index.");
  136. }
  137. GVector<Real> GetRow(int r) const
  138. {
  139. if (0 <= r && r < mNumRows)
  140. {
  141. GVector<Real> vec(mNumCols);
  142. for (int c = 0; c < mNumCols; ++c)
  143. {
  144. vec[c] = operator()(r, c);
  145. }
  146. return vec;
  147. }
  148. LogError("Invalid index.");
  149. }
  150. GVector<Real> GetCol(int c) const
  151. {
  152. if (0 <= c && c < mNumCols)
  153. {
  154. GVector<Real> vec(mNumRows);
  155. for (int r = 0; r < mNumRows; ++r)
  156. {
  157. vec[r] = operator()(r, c);
  158. }
  159. return vec;
  160. }
  161. LogError("Invalid index.");
  162. }
  163. // Member access by 1-dimensional index. NOTE: These accessors are
  164. // useful for the manipulation of matrix entries when it does not
  165. // matter whether storage is row-major or column-major. Do not use
  166. // constructs such as M[c+NumCols*r] or M[r+NumRows*c] that expose the
  167. // storage convention.
  168. inline Real const& operator[](int i) const
  169. {
  170. return mElements[i];
  171. }
  172. inline Real& operator[](int i)
  173. {
  174. return mElements[i];
  175. }
  176. // Comparisons for sorted containers and geometric ordering.
  177. inline bool operator==(GMatrix const& mat) const
  178. {
  179. return mNumRows == mat.mNumRows && mNumCols == mat.mNumCols
  180. && mElements == mat.mElements;
  181. }
  182. inline bool operator!=(GMatrix const& mat) const
  183. {
  184. return mNumRows == mat.mNumRows && mNumCols == mat.mNumCols
  185. && mElements != mat.mElements;
  186. }
  187. inline bool operator< (GMatrix const& mat) const
  188. {
  189. return mNumRows == mat.mNumRows && mNumCols == mat.mNumCols
  190. && mElements < mat.mElements;
  191. }
  192. inline bool operator<=(GMatrix const& mat) const
  193. {
  194. return mNumRows == mat.mNumRows && mNumCols == mat.mNumCols
  195. && mElements <= mat.mElements;
  196. }
  197. inline bool operator> (GMatrix const& mat) const
  198. {
  199. return mNumRows == mat.mNumRows && mNumCols == mat.mNumCols
  200. && mElements > mat.mElements;
  201. }
  202. inline bool operator>=(GMatrix const& mat) const
  203. {
  204. return mNumRows == mat.mNumRows && mNumCols == mat.mNumCols
  205. && mElements >= mat.mElements;
  206. }
  207. // Special matrices.
  208. // All components are 0.
  209. void MakeZero()
  210. {
  211. std::fill(mElements.begin(), mElements.end(), (Real)0);
  212. }
  213. // Component (r,c) is 1, all others zero.
  214. void MakeUnit(int r, int c)
  215. {
  216. if (0 <= r && r < mNumRows && 0 <= c && c < mNumCols)
  217. {
  218. MakeZero();
  219. operator()(r, c) = (Real)1;
  220. return;
  221. }
  222. LogError("Invalid index.");
  223. }
  224. // Diagonal entries 1, others 0, even when nonsquare.
  225. void MakeIdentity()
  226. {
  227. MakeZero();
  228. int const numDiagonal = (mNumRows <= mNumCols ? mNumRows : mNumCols);
  229. for (int i = 0; i < numDiagonal; ++i)
  230. {
  231. operator()(i, i) = (Real)1;
  232. }
  233. }
  234. static GMatrix Zero(int numRows, int numCols)
  235. {
  236. GMatrix<Real> M(numRows, numCols);
  237. M.MakeZero();
  238. return M;
  239. }
  240. static GMatrix Unit(int numRows, int numCols, int r, int c)
  241. {
  242. GMatrix<Real> M(numRows, numCols);
  243. M.MakeUnit(r, c);
  244. return M;
  245. }
  246. static GMatrix Identity(int numRows, int numCols)
  247. {
  248. GMatrix<Real> M(numRows, numCols);
  249. M.MakeIdentity();
  250. return M;
  251. }
  252. protected:
  253. // The matrix is stored as a 1-dimensional array. The convention of
  254. // row-major or column-major is your choice.
  255. int mNumRows, mNumCols;
  256. std::vector<Real> mElements;
  257. };
  258. // Unary operations.
  259. template <typename Real>
  260. GMatrix<Real> operator+(GMatrix<Real> const& M)
  261. {
  262. return M;
  263. }
  264. template <typename Real>
  265. GMatrix<Real> operator-(GMatrix<Real> const& M)
  266. {
  267. GMatrix<Real> result(M.GetNumRows(), M.GetNumCols());
  268. for (int i = 0; i < M.GetNumElements(); ++i)
  269. {
  270. result[i] = -M[i];
  271. }
  272. return result;
  273. }
  274. // Linear-algebraic operations.
  275. template <typename Real>
  276. GMatrix<Real> operator+(GMatrix<Real> const& M0, GMatrix<Real> const& M1)
  277. {
  278. GMatrix<Real> result = M0;
  279. return result += M1;
  280. }
  281. template <typename Real>
  282. GMatrix<Real> operator-(GMatrix<Real> const& M0, GMatrix<Real> const& M1)
  283. {
  284. GMatrix<Real> result = M0;
  285. return result -= M1;
  286. }
  287. template <typename Real>
  288. GMatrix<Real> operator*(GMatrix<Real> const& M, Real scalar)
  289. {
  290. GMatrix<Real> result = M;
  291. return result *= scalar;
  292. }
  293. template <typename Real>
  294. GMatrix<Real> operator*(Real scalar, GMatrix<Real> const& M)
  295. {
  296. GMatrix<Real> result = M;
  297. return result *= scalar;
  298. }
  299. template <typename Real>
  300. GMatrix<Real> operator/(GMatrix<Real> const& M, Real scalar)
  301. {
  302. GMatrix<Real> result = M;
  303. return result /= scalar;
  304. }
  305. template <typename Real>
  306. GMatrix<Real>& operator+=(GMatrix<Real>& M0, GMatrix<Real> const& M1)
  307. {
  308. if (M0.GetNumRows() == M1.GetNumRows() && M0.GetNumCols() == M1.GetNumCols())
  309. {
  310. for (int i = 0; i < M0.GetNumElements(); ++i)
  311. {
  312. M0[i] += M1[i];
  313. }
  314. return M0;
  315. }
  316. LogError("Mismatched sizes");
  317. }
  318. template <typename Real>
  319. GMatrix<Real>& operator-=(GMatrix<Real>& M0, GMatrix<Real> const& M1)
  320. {
  321. if (M0.GetNumRows() == M1.GetNumRows() && M0.GetNumCols() == M1.GetNumCols())
  322. {
  323. for (int i = 0; i < M0.GetNumElements(); ++i)
  324. {
  325. M0[i] -= M1[i];
  326. }
  327. return M0;
  328. }
  329. LogError("Mismatched sizes");
  330. }
  331. template <typename Real>
  332. GMatrix<Real>& operator*=(GMatrix<Real>& M, Real scalar)
  333. {
  334. for (int i = 0; i < M.GetNumElements(); ++i)
  335. {
  336. M[i] *= scalar;
  337. }
  338. return M;
  339. }
  340. template <typename Real>
  341. GMatrix<Real>& operator/=(GMatrix<Real>& M, Real scalar)
  342. {
  343. if (scalar != (Real)0)
  344. {
  345. Real invScalar = ((Real)1) / scalar;
  346. for (int i = 0; i < M.GetNumElements(); ++i)
  347. {
  348. M[i] *= invScalar;
  349. }
  350. return M;
  351. }
  352. LogError("Division by zero.");
  353. }
  354. // Geometric operations.
  355. template <typename Real>
  356. Real L1Norm(GMatrix<Real> const& M)
  357. {
  358. Real sum(0);
  359. for (int i = 0; i < M.GetNumElements(); ++i)
  360. {
  361. sum += std::fabs(M[i]);
  362. }
  363. return sum;
  364. }
  365. template <typename Real>
  366. Real L2Norm(GMatrix<Real> const& M)
  367. {
  368. Real sum(0);
  369. for (int i = 0; i < M.GetNumElements(); ++i)
  370. {
  371. sum += M[i] * M[i];
  372. }
  373. return std::sqrt(sum);
  374. }
  375. template <typename Real>
  376. Real LInfinityNorm(GMatrix<Real> const& M)
  377. {
  378. Real maxAbsElement(0);
  379. for (int i = 0; i < M.GetNumElements(); ++i)
  380. {
  381. Real absElement = std::fabs(M[i]);
  382. if (absElement > maxAbsElement)
  383. {
  384. maxAbsElement = absElement;
  385. }
  386. }
  387. return maxAbsElement;
  388. }
  389. template <typename Real>
  390. GMatrix<Real> Inverse(GMatrix<Real> const& M, bool* reportInvertibility = nullptr)
  391. {
  392. if (M.GetNumRows() == M.GetNumCols())
  393. {
  394. GMatrix<Real> invM(M.GetNumRows(), M.GetNumCols());
  395. Real determinant;
  396. bool invertible = GaussianElimination<Real>()(M.GetNumRows(), &M[0],
  397. &invM[0], determinant, nullptr, nullptr, nullptr, 0, nullptr);
  398. if (reportInvertibility)
  399. {
  400. *reportInvertibility = invertible;
  401. }
  402. return invM;
  403. }
  404. LogError("Matrix must be square.");
  405. }
  406. template <typename Real>
  407. Real Determinant(GMatrix<Real> const& M)
  408. {
  409. if (M.GetNumRows() == M.GetNumCols())
  410. {
  411. Real determinant;
  412. GaussianElimination<Real>()(M.GetNumRows(), &M[0], nullptr,
  413. determinant, nullptr, nullptr, nullptr, 0, nullptr);
  414. return determinant;
  415. }
  416. LogError("Matrix must be square.");
  417. }
  418. // M^T
  419. template <typename Real>
  420. GMatrix<Real> Transpose(GMatrix<Real> const& M)
  421. {
  422. GMatrix<Real> result(M.GetNumCols(), M.GetNumRows());
  423. for (int r = 0; r < M.GetNumRows(); ++r)
  424. {
  425. for (int c = 0; c < M.GetNumCols(); ++c)
  426. {
  427. result(c, r) = M(r, c);
  428. }
  429. }
  430. return result;
  431. }
  432. // M*V
  433. template <typename Real>
  434. GVector<Real> operator*(GMatrix<Real> const& M, GVector<Real> const& V)
  435. {
  436. if (V.GetSize() == M.GetNumCols())
  437. {
  438. GVector<Real> result(M.GetNumRows());
  439. for (int r = 0; r < M.GetNumRows(); ++r)
  440. {
  441. result[r] = (Real)0;
  442. for (int c = 0; c < M.GetNumCols(); ++c)
  443. {
  444. result[r] += M(r, c) * V[c];
  445. }
  446. }
  447. return result;
  448. }
  449. LogError("Mismatched sizes.");
  450. }
  451. // V^T*M
  452. template <typename Real>
  453. GVector<Real> operator*(GVector<Real> const& V, GMatrix<Real> const& M)
  454. {
  455. if (V.GetSize() == M.GetNumRows())
  456. {
  457. GVector<Real> result(M.GetNumCols());
  458. for (int c = 0; c < M.GetNumCols(); ++c)
  459. {
  460. result[c] = (Real)0;
  461. for (int r = 0; r < M.GetNumRows(); ++r)
  462. {
  463. result[c] += V[r] * M(r, c);
  464. }
  465. }
  466. return result;
  467. }
  468. LogError("Mismatched sizes.");
  469. }
  470. // A*B
  471. template <typename Real>
  472. GMatrix<Real> operator*(GMatrix<Real> const& A, GMatrix<Real> const& B)
  473. {
  474. return MultiplyAB(A, B);
  475. }
  476. template <typename Real>
  477. GMatrix<Real> MultiplyAB(GMatrix<Real> const& A, GMatrix<Real> const& B)
  478. {
  479. if (A.GetNumCols() == B.GetNumRows())
  480. {
  481. GMatrix<Real> result(A.GetNumRows(), B.GetNumCols());
  482. int const numCommon = A.GetNumCols();
  483. for (int r = 0; r < result.GetNumRows(); ++r)
  484. {
  485. for (int c = 0; c < result.GetNumCols(); ++c)
  486. {
  487. result(r, c) = (Real)0;
  488. for (int i = 0; i < numCommon; ++i)
  489. {
  490. result(r, c) += A(r, i) * B(i, c);
  491. }
  492. }
  493. }
  494. return result;
  495. }
  496. LogError("Mismatched sizes.");
  497. }
  498. // A*B^T
  499. template <typename Real>
  500. GMatrix<Real> MultiplyABT(GMatrix<Real> const& A, GMatrix<Real> const& B)
  501. {
  502. if (A.GetNumCols() == B.GetNumCols())
  503. {
  504. GMatrix<Real> result(A.GetNumRows(), B.GetNumRows());
  505. int const numCommon = A.GetNumCols();
  506. for (int r = 0; r < result.GetNumRows(); ++r)
  507. {
  508. for (int c = 0; c < result.GetNumCols(); ++c)
  509. {
  510. result(r, c) = (Real)0;
  511. for (int i = 0; i < numCommon; ++i)
  512. {
  513. result(r, c) += A(r, i) * B(c, i);
  514. }
  515. }
  516. }
  517. return result;
  518. }
  519. LogError("Mismatched sizes.");
  520. }
  521. // A^T*B
  522. template <typename Real>
  523. GMatrix<Real> MultiplyATB(GMatrix<Real> const& A, GMatrix<Real> const& B)
  524. {
  525. if (A.GetNumRows() == B.GetNumRows())
  526. {
  527. GMatrix<Real> result(A.GetNumCols(), B.GetNumCols());
  528. int const numCommon = A.GetNumRows();
  529. for (int r = 0; r < result.GetNumRows(); ++r)
  530. {
  531. for (int c = 0; c < result.GetNumCols(); ++c)
  532. {
  533. result(r, c) = (Real)0;
  534. for (int i = 0; i < numCommon; ++i)
  535. {
  536. result(r, c) += A(i, r) * B(i, c);
  537. }
  538. }
  539. }
  540. return result;
  541. }
  542. LogError("Mismatched sizes.");
  543. }
  544. // A^T*B^T
  545. template <typename Real>
  546. GMatrix<Real> MultiplyATBT(GMatrix<Real> const& A, GMatrix<Real> const& B)
  547. {
  548. if (A.GetNumRows() == B.GetNumCols())
  549. {
  550. GMatrix<Real> result(A.GetNumCols(), B.GetNumRows());
  551. int const numCommon = A.GetNumRows();
  552. for (int r = 0; r < result.GetNumRows(); ++r)
  553. {
  554. for (int c = 0; c < result.GetNumCols(); ++c)
  555. {
  556. result(r, c) = (Real)0;
  557. for (int i = 0; i < numCommon; ++i)
  558. {
  559. result(r, c) += A(i, r) * B(c, i);
  560. }
  561. }
  562. }
  563. return result;
  564. }
  565. LogError("Mismatched sizes.");
  566. }
  567. // M*D, D is square diagonal (stored as vector)
  568. template <typename Real>
  569. GMatrix<Real> MultiplyMD(GMatrix<Real> const& M, GVector<Real> const& D)
  570. {
  571. if (D.GetSize() == M.GetNumCols())
  572. {
  573. GMatrix<Real> result(M.GetNumRows(), M.GetNumCols());
  574. for (int r = 0; r < result.GetNumRows(); ++r)
  575. {
  576. for (int c = 0; c < result.GetNumCols(); ++c)
  577. {
  578. result(r, c) = M(r, c) * D[c];
  579. }
  580. }
  581. return result;
  582. }
  583. LogError("Mismatched sizes.");
  584. }
  585. // D*M, D is square diagonal (stored as vector)
  586. template <typename Real>
  587. GMatrix<Real> MultiplyDM(GVector<Real> const& D, GMatrix<Real> const& M)
  588. {
  589. if (D.GetSize() == M.GetNumRows())
  590. {
  591. GMatrix<Real> result(M.GetNumRows(), M.GetNumCols());
  592. for (int r = 0; r < result.GetNumRows(); ++r)
  593. {
  594. for (int c = 0; c < result.GetNumCols(); ++c)
  595. {
  596. result(r, c) = D[r] * M(r, c);
  597. }
  598. }
  599. return result;
  600. }
  601. LogError("Mismatched sizes.");
  602. }
  603. // U*V^T, U is N-by-1, V is M-by-1, result is N-by-M.
  604. template <typename Real>
  605. GMatrix<Real> OuterProduct(GVector<Real> const& U, GVector<Real> const& V)
  606. {
  607. GMatrix<Real> result(U.GetSize(), V.GetSize());
  608. for (int r = 0; r < result.GetNumRows(); ++r)
  609. {
  610. for (int c = 0; c < result.GetNumCols(); ++c)
  611. {
  612. result(r, c) = U[r] * V[c];
  613. }
  614. }
  615. return result;
  616. }
  617. // Initialization to a diagonal matrix whose diagonal entries are the
  618. // components of D, even when nonsquare.
  619. template <typename Real>
  620. void MakeDiagonal(GVector<Real> const& D, GMatrix<Real>& M)
  621. {
  622. int const numRows = M.GetNumRows();
  623. int const numCols = M.GetNumCols();
  624. int const numDiagonal = (numRows <= numCols ? numRows : numCols);
  625. M.MakeZero();
  626. for (int i = 0; i < numDiagonal; ++i)
  627. {
  628. M(i, i) = D[i];
  629. }
  630. }
  631. }