PageRenderTime 63ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/src/lib/numerics/sym.cpp

https://gitlab.com/philipclaude/avro2
C++ | 412 lines | 324 code | 58 blank | 30 comment | 62 complexity | 358c5b3984d661ac94e8eb38a280dbfd MD5 | raw file
  1. //
  2. // avro - Adaptive Voronoi Remesher
  3. //
  4. // Copyright 2017-2022, Philip Claude Caplan
  5. // All rights reserved
  6. //
  7. // Licensed under The GNU Lesser General Public License, version 2.1
  8. // See http://www.opensource.org/licenses/lgpl-2.1.php
  9. //
  10. #include "numerics/mat.h"
  11. #include "numerics/dual.h"
  12. #include "numerics/linear_algebra.h"
  13. #include "numerics/sym.h"
  14. #include "numerics/surreal/config.h"
  15. #include "numerics/surreal/SurrealD.h"
  16. #include "numerics/surreal/SurrealS.h"
  17. #include <stdio.h>
  18. #include <math.h>
  19. namespace avro
  20. {
  21. template<typename type>
  22. symd<type>::symd( const coord_t _n ) :
  23. n_(_n) {
  24. // constructor from size
  25. data_.resize( nb() );
  26. std::fill( data_.begin() , data_.end() , 0. );
  27. }
  28. template<typename type>
  29. symd<type>::symd( const std::vector<type>& _data ) :
  30. data_(_data) {
  31. // constructor from matrix data itself
  32. index_t m = data_.size();
  33. n_ = ( -1 +::sqrt( 8*m +1 ) )/2;
  34. }
  35. template<typename type>
  36. symd<type>::symd( const vecd<type>& lambda , const matd<type>& q ) :
  37. n_( lambda.m() ) {
  38. from_eig(lambda,q);
  39. }
  40. template<typename type>
  41. symd<type>::symd( const std::pair< vecd<type> , matd<type> >& decomp ) :
  42. symd( decomp.first , decomp.second )
  43. {}
  44. template<typename type>
  45. symd<type>::symd( const matd<type>& M ) :
  46. n_(M.n()) {
  47. avro_assert( M.m() == M.n() );
  48. data_.resize(nb());
  49. for (index_t i = 0; i < n_; i++)
  50. for (index_t j = 0; j < n_; j++)
  51. operator()(i,j) = M(i,j);
  52. }
  53. template<typename type>
  54. template<typename typeR>
  55. symd<type>::symd( const symd<typeR>& A ) :
  56. n_(A.n()) {
  57. data_.resize(nb());
  58. for (index_t i=0;i<n_;i++)
  59. for (index_t j=0;j<n_;j++)
  60. operator()(i,j) = type(A(i,j));
  61. }
  62. template<typename type>
  63. void
  64. symd<type>::set( const matd<type>& M ) {
  65. n_ = M.m();
  66. avro_assert( n_ == M.n() );
  67. for (index_t i = 0; i < n_; i++)
  68. for (index_t j = 0; j < n_; j++)
  69. (*this)(i,j) = M(i,j);
  70. }
  71. template<typename type>
  72. void
  73. symd<type>::from_eig(const vecd<type>& lambda , const matd<type>& q ) {
  74. // constructor from eigendecomposition
  75. n_ = lambda.m();
  76. data_.resize( nb() );
  77. matd<type> M = q * (numerics::diag(lambda) * numerics::transpose(q));
  78. set(M);
  79. }
  80. template<typename type>
  81. symd<type>
  82. symd<type>::sandwich( const symd<type>& B ) const {
  83. avro_assert( n_>=2 && n_<=4 );
  84. symd<type> C(n_);
  85. type A1_1 = operator()(0,0); type A1_2 = operator()(0,1);
  86. type A2_1 = operator()(1,0); type A2_2 = operator()(1,1);
  87. type B1_1 = B(0,0); type B1_2 = B(0,1);
  88. type B2_1 = B(1,0); type B2_2 = B(1,1);
  89. if (n_ == 2)
  90. {
  91. C(0,0) = B1_1*(A1_1*B1_1+A2_1*B1_2)+B2_1*(A1_2*B1_1+A2_2*B1_2);
  92. C(0,1) = B1_2*(A1_1*B1_1+A2_1*B1_2)+B2_2*(A1_2*B1_1+A2_2*B1_2);
  93. C(1,1) = B1_2*(A1_1*B2_1+A2_1*B2_2)+B2_2*(A1_2*B2_1+A2_2*B2_2);
  94. }
  95. else if (n_ == 3)
  96. {
  97. type A1_3 = operator()(0,2); type A2_3 = operator()(1,2); type A3_3 = operator()(2,2);
  98. type A3_1 = A1_3; type A3_2 = A2_3;
  99. type B1_3 = B(0,2); type B2_3 = B(1,2); type B3_3 = B(2,2);
  100. type B3_1 = B1_3; type B3_2 = B2_3;
  101. C(0,0) = B1_1*(A1_1*B1_1+A2_1*B1_2+A3_1*B1_3)+B2_1*(A1_2*B1_1+A2_2*B1_2+A3_2*B1_3)+B3_1*(A1_3*B1_1+A2_3*B1_2+A3_3*B1_3);
  102. C(0,1) = B1_2*(A1_1*B1_1+A2_1*B1_2+A3_1*B1_3)+B2_2*(A1_2*B1_1+A2_2*B1_2+A3_2*B1_3)+B3_2*(A1_3*B1_1+A2_3*B1_2+A3_3*B1_3);
  103. C(0,2) = B1_3*(A1_1*B1_1+A2_1*B1_2+A3_1*B1_3)+B2_3*(A1_2*B1_1+A2_2*B1_2+A3_2*B1_3)+B3_3*(A1_3*B1_1+A2_3*B1_2+A3_3*B1_3);
  104. C(1,1) = B1_2*(A1_1*B2_1+A2_1*B2_2+A3_1*B2_3)+B2_2*(A1_2*B2_1+A2_2*B2_2+A3_2*B2_3)+B3_2*(A1_3*B2_1+A2_3*B2_2+A3_3*B2_3);
  105. C(1,2) = B1_3*(A1_1*B2_1+A2_1*B2_2+A3_1*B2_3)+B2_3*(A1_2*B2_1+A2_2*B2_2+A3_2*B2_3)+B3_3*(A1_3*B2_1+A2_3*B2_2+A3_3*B2_3);
  106. C(2,2) = B1_3*(A1_1*B3_1+A2_1*B3_2+A3_1*B3_3)+B2_3*(A1_2*B3_1+A2_2*B3_2+A3_2*B3_3)+B3_3*(A1_3*B3_1+A2_3*B3_2+A3_3*B3_3);
  107. }
  108. else if (n_ == 4)
  109. {
  110. type A1_3 = operator()(0,2); type A2_3 = operator()(1,2); type A3_3 = operator()(2,2);
  111. type A3_1 = A1_3; type A3_2 = A2_3;
  112. type B1_3 = B(0,2); type B2_3 = B(1,2); type B3_3 = B(2,2);
  113. type B3_1 = B1_3; type B3_2 = B2_3;
  114. type A1_4 = operator()(0,3); type A2_4 = operator()(1,3); type A3_4 = operator()(2,3); type A4_4 = operator()(3,3);
  115. type A4_1 = A1_4; type A4_2 = A2_4; type A4_3 = A3_4;
  116. type B1_4 = B(0,3); type B2_4 = B(1,3); type B3_4 = B(2,3); type B4_4 = B(3,3);
  117. type B4_1 = B1_4; type B4_2 = B2_4; type B4_3 = B3_4;
  118. C(0,0) = B1_1*(A1_1*B1_1+A2_1*B1_2+A3_1*B1_3+A4_1*B1_4)+B2_1*(A1_2*B1_1+A2_2*B1_2+A3_2*B1_3+A4_2*B1_4)+B3_1*(A1_3*B1_1+A2_3*B1_2+A3_3*B1_3+A4_3*B1_4)+B4_1*(A1_4*B1_1+A2_4*B1_2+A3_4*B1_3+A4_4*B1_4);
  119. C(0,1) = B1_2*(A1_1*B1_1+A2_1*B1_2+A3_1*B1_3+A4_1*B1_4)+B2_2*(A1_2*B1_1+A2_2*B1_2+A3_2*B1_3+A4_2*B1_4)+B3_2*(A1_3*B1_1+A2_3*B1_2+A3_3*B1_3+A4_3*B1_4)+B4_2*(A1_4*B1_1+A2_4*B1_2+A3_4*B1_3+A4_4*B1_4);
  120. C(0,2) = B1_3*(A1_1*B1_1+A2_1*B1_2+A3_1*B1_3+A4_1*B1_4)+B2_3*(A1_2*B1_1+A2_2*B1_2+A3_2*B1_3+A4_2*B1_4)+B3_3*(A1_3*B1_1+A2_3*B1_2+A3_3*B1_3+A4_3*B1_4)+B4_3*(A1_4*B1_1+A2_4*B1_2+A3_4*B1_3+A4_4*B1_4);
  121. C(0,3) = B1_4*(A1_1*B1_1+A2_1*B1_2+A3_1*B1_3+A4_1*B1_4)+B2_4*(A1_2*B1_1+A2_2*B1_2+A3_2*B1_3+A4_2*B1_4)+B3_4*(A1_3*B1_1+A2_3*B1_2+A3_3*B1_3+A4_3*B1_4)+B4_4*(A1_4*B1_1+A2_4*B1_2+A3_4*B1_3+A4_4*B1_4);
  122. C(1,1) = B1_2*(A1_1*B2_1+A2_1*B2_2+A3_1*B2_3+A4_1*B2_4)+B2_2*(A1_2*B2_1+A2_2*B2_2+A3_2*B2_3+A4_2*B2_4)+B3_2*(A1_3*B2_1+A2_3*B2_2+A3_3*B2_3+A4_3*B2_4)+B4_2*(A1_4*B2_1+A2_4*B2_2+A3_4*B2_3+A4_4*B2_4);
  123. C(1,2) = B1_3*(A1_1*B2_1+A2_1*B2_2+A3_1*B2_3+A4_1*B2_4)+B2_3*(A1_2*B2_1+A2_2*B2_2+A3_2*B2_3+A4_2*B2_4)+B3_3*(A1_3*B2_1+A2_3*B2_2+A3_3*B2_3+A4_3*B2_4)+B4_3*(A1_4*B2_1+A2_4*B2_2+A3_4*B2_3+A4_4*B2_4);
  124. C(1,3) = B1_4*(A1_1*B2_1+A2_1*B2_2+A3_1*B2_3+A4_1*B2_4)+B2_4*(A1_2*B2_1+A2_2*B2_2+A3_2*B2_3+A4_2*B2_4)+B3_4*(A1_3*B2_1+A2_3*B2_2+A3_3*B2_3+A4_3*B2_4)+B4_4*(A1_4*B2_1+A2_4*B2_2+A3_4*B2_3+A4_4*B2_4);
  125. C(2,2) = B1_3*(A1_1*B3_1+A2_1*B3_2+A3_1*B3_3+A4_1*B3_4)+B2_3*(A1_2*B3_1+A2_2*B3_2+A3_2*B3_3+A4_2*B3_4)+B3_3*(A1_3*B3_1+A2_3*B3_2+A3_3*B3_3+A4_3*B3_4)+B4_3*(A1_4*B3_1+A2_4*B3_2+A3_4*B3_3+A4_4*B3_4);
  126. C(2,3) = B1_4*(A1_1*B3_1+A2_1*B3_2+A3_1*B3_3+A4_1*B3_4)+B2_4*(A1_2*B3_1+A2_2*B3_2+A3_2*B3_3+A4_2*B3_4)+B3_4*(A1_3*B3_1+A2_3*B3_2+A3_3*B3_3+A4_3*B3_4)+B4_4*(A1_4*B3_1+A2_4*B3_2+A3_4*B3_3+A4_4*B3_4);
  127. C(3,3) = B1_4*(A1_1*B4_1+A2_1*B4_2+A3_1*B4_3+A4_1*B4_4)+B2_4*(A1_2*B4_1+A2_2*B4_2+A3_2*B4_3+A4_2*B4_4)+B3_4*(A1_3*B4_1+A2_3*B4_2+A3_3*B4_3+A4_3*B4_4)+B4_4*(A1_4*B4_1+A2_4*B4_2+A3_4*B4_3+A4_4*B4_4);
  128. }
  129. else
  130. avro_implement;
  131. return C;
  132. }
  133. // eigenvalues and eigenvectors
  134. template<typename type>
  135. std::pair< vecd<type>,matd<type> >
  136. symd<type>::eig() const {
  137. std::pair< vecd<type>,matd<type> > decomp = __eigivens__();
  138. #if 0
  139. // bound the result? this can be dangerous
  140. type lim = 1e30;
  141. for (coord_t i = 0; i < decomp.first.m(); i++) {
  142. if (decomp.first[i] > lim) decomp.first[i] = lim;
  143. if (decomp.first[i] < -lim) decomp.first[i] = -lim;
  144. }
  145. #endif
  146. return decomp;
  147. }
  148. // eigenvalues and eigenvectors
  149. template<>
  150. std::pair< vecd<real_t>,matd<real_t> >
  151. symd<real_t>::eig() const {
  152. // go for numerical stability with lapack when using real matrices
  153. //return numerics::eign(*this);
  154. std::pair< vecd<real_t>,matd<real_t> > decomp = __eigivens__();
  155. return decomp;
  156. }
  157. template<typename type>
  158. std::pair< vecd<type>,matd<type> >
  159. symd<type>::__eig2__() const {
  160. vecd<type> d(n_);
  161. matd<type> Q(n_,n_);
  162. type sqDelta,dd,trm,vnorm;
  163. dd = data_[0] -data_[2];
  164. trm = data_[0] +data_[2];
  165. sqDelta = ::sqrt( dd*dd +4.0*data_[1]*data_[1]);
  166. d[0] = 0.5*(trm -sqDelta);
  167. real_t tol = 1e-12;
  168. if (sqDelta < tol) {
  169. d[1] = d[0];
  170. Q(0,0) = 1.;
  171. Q(0,1) = 0.;
  172. Q(1,0) = 0.;
  173. Q(1,1) = 1.;
  174. return std::make_pair(d,Q);
  175. }
  176. Q(0,0) = data_[1];
  177. Q(0,1) = (d[0] -data_[0]);
  178. vnorm = ::sqrt( Q(0,0)*Q(0,0) +Q(0,1)*Q(0,1) );
  179. if (vnorm < tol) {
  180. Q(0,0) = (d[0] -data_[2]);
  181. Q(0,1) = data_[1];
  182. vnorm = ::sqrt( Q(0,0)*Q(0,0) +Q(0,1)*Q(0,1) );
  183. }
  184. avro_assert( vnorm > tol );
  185. vnorm = 1./vnorm;
  186. Q(0,0) *= vnorm;
  187. Q(0,1) *= vnorm;
  188. Q(1,0) = -Q(0,1);
  189. Q(1,1) = Q(0,0);
  190. d[1] = data_[0]*Q(1,0)*Q(1,0)
  191. +2.*data_[1]*Q(1,0)*Q(1,1)
  192. + data_[2]*Q(1,1)*Q(1,1);
  193. return std::make_pair(d,Q);
  194. }
  195. // please see the following paper:
  196. // "Efficient numerical diagonalization of hermitian 3x3 matrices"
  197. // by Joachim Kopp
  198. // Int. J. Mod. Phys. C 19 (2008) 523-548
  199. // arXiv.org: physics/0610206
  200. // (https://www.mpi-hd.mpg.de/personalhomes/globes/3x3/)
  201. template<typename type>
  202. std::pair< vecd<type>,matd<type> >
  203. symd<type>::__eigivens__() const {
  204. vecd<type> L(n_);
  205. matd<type> E(n_,n_);
  206. type sd,so;
  207. type s,c,t;
  208. type g,h,z,theta;
  209. type thresh;
  210. E.eye();
  211. symd A(n_);
  212. A.copy(*this);
  213. for (coord_t i=0;i<n_;i++)
  214. L[i] = A(i,i);
  215. // calculate sq(tr(A))
  216. sd = 0.;
  217. for (coord_t i=0;i<n_;i++)
  218. sd += fabs(L[i]);
  219. sd = sd*sd;
  220. for (index_t iter = 0; iter < 50; iter++) {
  221. // test for convergence
  222. so = 0.;
  223. for (coord_t p = 0; p < n_; p++)
  224. for (coord_t q = p+1; q < n_; q++)
  225. so += fabs(A(p,q));
  226. if (so == 0.0)
  227. return std::make_pair(L,E);
  228. if (iter < 4)
  229. thresh = 0.2*so/type(n_*n_);
  230. else
  231. thresh = 0.;
  232. // sweep
  233. for (coord_t p = 0; p < n_; p++) {
  234. for (int q = p+1; q < n_; q++) {
  235. g = 100.*fabs(A(p,q));
  236. if (iter>4 && fabs(L[p])+g == fabs(L[p]) && fabs(L[q])+g==fabs(L[q]))
  237. A(p,q) = 0.;
  238. else if (fabs(A(p,q)) > thresh) {
  239. // calculate Jacobi transformation
  240. h = L[q] -L[p];
  241. if (fabs(h)+g == fabs(h)) {
  242. t = A(p,q)/h;
  243. }
  244. else {
  245. theta = 0.5*h/A(p,q);
  246. if (theta < 0.0)
  247. t = -1./( ::sqrt(1. +theta*theta) -theta );
  248. else
  249. t = 1./( ::sqrt(1. +theta*theta) +theta );
  250. }
  251. c = 1./::sqrt(1. +t*t);
  252. s = t*c;
  253. z = t*A(p,q);
  254. // apply Jacobi transformation
  255. A(p,q) = 0.;
  256. L[p] -= z;
  257. L[q] += z;
  258. for (coord_t r = 0; r < p; r++) {
  259. t = A(r,p);
  260. A(r,p) = c*t -s*A(r,q);
  261. A(r,q) = s*t +c*A(r,q);
  262. }
  263. for (coord_t r = p+1 ; r < q; r++) {
  264. t = A(p,r);
  265. A(p,r) = c*t -s*A(r,q);
  266. A(r,q) = s*t +c*A(r,q);
  267. }
  268. for (coord_t r = q+1; r < n_; r++) {
  269. t = A(p,r);
  270. A(p,r) = c*t -s*A(q,r);
  271. A(q,r) = s*t +c*A(q,r);
  272. }
  273. // update eigenvectors
  274. for (coord_t r = 0; r < n_; r++) {
  275. t = E(r,p);
  276. E(r,p) = c*t -s*E(r,q);
  277. E(r,q) = s*t +c*E(r,q);
  278. }
  279. }
  280. }
  281. }
  282. } // iteration loop
  283. printf("givens rotation failed :(\n");
  284. display();
  285. avro_assert_not_reached;
  286. return std::make_pair(L,E);
  287. }
  288. template<typename type>
  289. void
  290. symd<type>::display( const std::string& title ) const {
  291. if (!title.empty()) printf("%s\n",title.c_str());
  292. printf("%s:\n",__PRETTY_FUNCTION__);
  293. for (index_t i = 0; i < n_; i++)
  294. for (index_t j = 0; j < n_; j++)
  295. std::cout << "(" + std::to_string(i) + "," + std::to_string(j) + "): " << (*this)(i,j) << std::endl;
  296. }
  297. template<typename type>
  298. void
  299. symd<type>::for_matlab( const std::string& title ) const {
  300. avro_assert_not_reached;
  301. }
  302. template<typename type>
  303. void
  304. symd<type>::for_unit( const std::string& title ) const {
  305. avro_assert_not_reached;
  306. }
  307. template<>
  308. void
  309. symd<real_t>::display( const std::string& title ) const {
  310. if (!title.empty()) printf("%s\n",title.c_str());
  311. else printf("SPT:\n");
  312. for (index_t i = 0; i < n_; i++) {
  313. printf("[ ");
  314. for (index_t j = 0; j < n_;j++)
  315. printf("%.5e ",operator()(i,j));
  316. printf("]\n");
  317. }
  318. }
  319. template<>
  320. void
  321. symd<real_t>::for_matlab( const std::string& title ) const {
  322. if (!title.empty()) printf("%s = [",title.c_str());
  323. else printf("A = [");
  324. for (index_t i = 0; i < n_; i++) {
  325. for (index_t j = 0; j < n_; j++) {
  326. printf("%.8e",operator()(i,j));
  327. if (int(j)<n_-1) printf(",");
  328. else {
  329. if (int(i)<n_-1)
  330. printf(";");
  331. }
  332. }
  333. }
  334. printf("];\n");
  335. }
  336. template<>
  337. void
  338. symd<real_t>::for_unit( const std::string& title ) const {
  339. avro_assert_msg( !title.empty() , "provide a variable name!" );
  340. for (index_t i = 0; i < n_; i++)
  341. for (index_t j = 0; j <= i; j++)
  342. printf("%s(%lu,%lu) = %.16e;\n",title.c_str(),i,j,operator()(i,j));
  343. }
  344. template class symd<real_t>;
  345. template class symd<SurrealS<1>>;
  346. template class symd<SurrealS<3>>;
  347. template class symd<SurrealS<6>>;
  348. template class symd<SurrealS<10>>;
  349. // constructors of surreal symmetric matrices from real ones
  350. template symd<SurrealS<1>>::symd( const symd<real_t>& A );
  351. template symd<SurrealS<3>>::symd( const symd<real_t>& A );
  352. template symd<SurrealS<6>>::symd( const symd<real_t>& A );
  353. template symd<SurrealS<10>>::symd( const symd<real_t>& A );
  354. } // avro