quadrule.hpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. #ifndef _SCTL_QUADRULE_HPP_
  2. #define _SCTL_QUADRULE_HPP_
  3. #include <sctl/common.hpp>
  4. #include SCTL_INCLUDE(matrix.hpp)
  5. #include SCTL_INCLUDE(math_utils.hpp)
  6. #include SCTL_INCLUDE(legendre_rule.hpp)
  7. #include <algorithm>
  8. namespace SCTL_NAMESPACE {
  9. template <class Real> class ChebQuadRule { // p(x)
  10. public:
  11. template <Integer MAX_ORDER=50> static const Vector<Real>& nds(Integer ChebOrder) {
  12. SCTL_ASSERT(ChebOrder < MAX_ORDER);
  13. auto compute_all = [](){
  14. Vector<Vector<Real>> nds(MAX_ORDER);
  15. for (Long i = 0; i < MAX_ORDER; i++) {
  16. nds[i] = ComputeNds(i);
  17. }
  18. return nds;
  19. };
  20. static const Vector<Vector<Real>> all_nds = compute_all();
  21. return all_nds[ChebOrder];
  22. }
  23. template <Integer MAX_ORDER=50> static const Vector<Real>& wts(Integer ChebOrder) {
  24. SCTL_ASSERT(ChebOrder < MAX_ORDER);
  25. auto compute_all = [](){
  26. Vector<Vector<Real>> wts(MAX_ORDER);
  27. for (Long i = 0; i < MAX_ORDER; i++) {
  28. wts[i] = ComputeWts(i);
  29. }
  30. return wts;
  31. };
  32. static const Vector<Vector<Real>> all_wts = compute_all();
  33. return all_wts[ChebOrder];
  34. }
  35. template <Integer ChebOrder> static const Vector<Real>& nds() {
  36. static Vector<Real> nds = ComputeNds(ChebOrder);
  37. return nds;
  38. }
  39. template <Integer ChebOrder> static const Vector<Real>& wts() {
  40. static const Vector<Real> wts = ComputeWts(ChebOrder);
  41. return wts;
  42. }
  43. static Vector<Real> ComputeNds(Integer ChebOrder){
  44. Vector<Real> nds(ChebOrder);
  45. for (Long i = 0; i < ChebOrder; i++) {
  46. nds[i] = 0.5 - cos<Real>((2*i+1)*const_pi<Real>()/(2*ChebOrder)) * 0.5;
  47. }
  48. return nds;
  49. }
  50. static Vector<Real> ComputeWts(Integer ChebOrder){
  51. Matrix<Real> M_cheb(ChebOrder, ChebOrder);
  52. { // Set M_cheb
  53. for (Long i = 0; i < ChebOrder; i++) {
  54. Real theta = (2*i+1)*const_pi<Real>()/(2*ChebOrder);
  55. for (Long j = 0; j < ChebOrder; j++) {
  56. M_cheb[j][i] = cos<Real>(j*theta);
  57. }
  58. }
  59. M_cheb = M_cheb.pinv(machine_eps<Real>());
  60. }
  61. Vector<Real> w_sample(ChebOrder);
  62. for (Integer i = 0; i < ChebOrder; i++) {
  63. w_sample[i] = (i % 2 ? 0 : -(ChebOrder/(Real)(i*i-1)));
  64. }
  65. Vector<Real> wts(ChebOrder);
  66. for (Integer j = 0; j < ChebOrder; j++) {
  67. wts[j] = 0;
  68. for (Integer i = 0; i < ChebOrder; i++) {
  69. wts[j] += M_cheb[j][i] * w_sample[i] / ChebOrder;
  70. }
  71. }
  72. return wts;
  73. }
  74. };
  75. template <class Real> class LegQuadRule {
  76. public:
  77. template <Integer MAX_ORDER=25> static const Vector<Real>& nds(Integer Order) {
  78. SCTL_ASSERT(Order < MAX_ORDER);
  79. auto compute_all = [](){
  80. Vector<Vector<Real>> nds(MAX_ORDER);
  81. for (Long i = 1; i < MAX_ORDER; i++) {
  82. nds[i] = ComputeNds<MAX_ORDER>(i);
  83. }
  84. return nds;
  85. };
  86. static const Vector<Vector<Real>> all_nds = compute_all();
  87. return all_nds[Order];
  88. }
  89. template <Integer MAX_ORDER=25> static const Vector<Real>& wts(Integer Order) {
  90. SCTL_ASSERT(Order < MAX_ORDER);
  91. auto compute_all = [](){
  92. Vector<Vector<Real>> wts(MAX_ORDER);
  93. for (Long i = 1; i < MAX_ORDER; i++) {
  94. wts[i] = ComputeWts<MAX_ORDER>(nds(i));
  95. }
  96. return wts;
  97. };
  98. static const Vector<Vector<Real>> all_wts = compute_all();
  99. return all_wts[Order];
  100. }
  101. template <Integer Order> static const Vector<Real>& nds() {
  102. static Vector<Real> nds = ComputeNds(Order);
  103. return nds;
  104. }
  105. template <Integer Order> static const Vector<Real>& wts() {
  106. static const Vector<Real> wts = ComputeWts(nds<Order>());
  107. return wts;
  108. }
  109. static Vector<Real> ComputeNds(Integer order) {
  110. Vector<Real> nds, wts;
  111. gauss_legendre_approx(nds, wts, order);
  112. nds = nds*2-1;
  113. auto EvalLegPoly = [](Vector<Real>& Pn, Vector<Real>& dPn, const Vector<Real>& nds, Long n) {
  114. Vector<Real> P, dP;
  115. LegPoly(P, nds, n);
  116. LegPolyDeriv(dP, nds, n);
  117. const Long M = nds.Dim();
  118. if (Pn.Dim() != M) Pn.ReInit(M);
  119. if (dPn.Dim() != M) dPn.ReInit(M);
  120. Pn = Vector<Real>(M, P.begin() + n*M, false);
  121. dPn = Vector<Real>(M, dP.begin() + n*M, false);
  122. for (Long i = 0; i < M; i++) dPn[i] = -dPn[i] / sqrt<Real>(1-nds[i]*nds[i]);
  123. };
  124. Vector<Real> Pn, dPn; // Newton iterations
  125. EvalLegPoly(Pn, dPn, nds, order); nds -= Pn/dPn;
  126. EvalLegPoly(Pn, dPn, nds, order); nds -= Pn/dPn;
  127. EvalLegPoly(Pn, dPn, nds, order); nds -= Pn/dPn;
  128. return (nds+1)/2;
  129. }
  130. static Vector<Real> ComputeWts(const Vector<Real>& nds) {
  131. const Long order = nds.Dim();
  132. Vector<Real> cheb_nds = ChebQuadRule<Real>::ComputeNds(2*order-1)*2-1;
  133. Vector<Real> cheb_wts = ChebQuadRule<Real>::ComputeWts(2*order-1)*2;
  134. auto EvalLegPoly = [](const Vector<Real>& nds, Long n) {
  135. Vector<Real> P;
  136. LegPoly(P, nds, n);
  137. const Long M = nds.Dim();
  138. return Matrix<Real>(n, M, P.begin());
  139. };
  140. Matrix<Real> b = EvalLegPoly(cheb_nds, 2*order-1) * Matrix<Real>(cheb_wts.Dim(), 1, cheb_wts.begin(), false);
  141. Matrix<Real> M = EvalLegPoly(nds*2-1, 2*order-1);
  142. Matrix<Real> wts = Matrix<Real>(M).pinv() * b;
  143. return Vector<Real>(wts.Dim(0), wts.begin())/2;
  144. }
  145. private:
  146. static void LegPoly(Vector<Real>& poly_val, const Vector<Real>& X, Long degree){
  147. Vector<Real> theta(X.Dim());
  148. for (Long i = 0; i < X.Dim(); i++) theta[i] = acos<Real>(X[i]);
  149. LegPoly_(poly_val, theta, degree);
  150. }
  151. static void LegPoly_(Vector<Real>& poly_val, const Vector<Real>& theta, Long degree){
  152. Long N = theta.Dim();
  153. Long Npoly = (degree + 1) * (degree + 2) / 2;
  154. if (poly_val.Dim() != Npoly * N) poly_val.ReInit(Npoly * N);
  155. Real fact = 1 / sqrt<Real>(4 * const_pi<Real>());
  156. Vector<Real> cos_theta(N), sin_theta(N);
  157. for (Long n = 0; n < N; n++) {
  158. cos_theta[n] = cos(theta[n]);
  159. sin_theta[n] = sin(theta[n]);
  160. poly_val[n] = fact;
  161. }
  162. Long idx = 0;
  163. Long idx_nxt = 0;
  164. for (Long i = 1; i <= degree; i++) {
  165. idx_nxt += N*(degree-i+2);
  166. Real c = sqrt<Real>((2*i+1)/(Real)(2*i));
  167. for (Long n = 0; n < N; n++) {
  168. poly_val[idx_nxt+n] = -poly_val[idx+n] * sin_theta[n] * c;
  169. }
  170. idx = idx_nxt;
  171. }
  172. idx = 0;
  173. for (Long m = 0; m < degree; m++) {
  174. for (Long n = 0; n < N; n++) {
  175. Real pmm = 0;
  176. Real pmmp1 = poly_val[idx+n];
  177. for (Long ll = m + 1; ll <= degree; ll++) {
  178. Real a = sqrt<Real>(((2*ll-1)*(2*ll+1) ) / (Real)((ll-m)*(ll+m) ));
  179. Real b = sqrt<Real>(((2*ll+1)*(ll+m-1)*(ll-m-1)) / (Real)((ll-m)*(ll+m)*(2*ll-3)));
  180. Real pll = cos_theta[n]*a*pmmp1 - b*pmm;
  181. pmm = pmmp1;
  182. pmmp1 = pll;
  183. poly_val[idx + N*(ll-m) + n] = pll;
  184. }
  185. }
  186. idx += N * (degree - m + 1);
  187. }
  188. }
  189. static void LegPolyDeriv(Vector<Real>& poly_val, const Vector<Real>& X, Long degree){
  190. Vector<Real> theta(X.Dim());
  191. for (Long i = 0; i < X.Dim(); i++) theta[i] = acos<Real>(X[i]);
  192. LegPolyDeriv_(poly_val, theta, degree);
  193. }
  194. static void LegPolyDeriv_(Vector<Real>& poly_val, const Vector<Real>& theta, Long degree){
  195. Long N = theta.Dim();
  196. Long Npoly = (degree + 1) * (degree + 2) / 2;
  197. if (poly_val.Dim() != N * Npoly) poly_val.ReInit(N * Npoly);
  198. Vector<Real> cos_theta(N), sin_theta(N);
  199. for (Long i = 0; i < N; i++) {
  200. cos_theta[i] = cos(theta[i]);
  201. sin_theta[i] = sin(theta[i]);
  202. }
  203. Vector<Real> leg_poly(Npoly * N);
  204. LegPoly_(leg_poly, theta, degree);
  205. for (Long m = 0; m <= degree; m++) {
  206. for (Long n = m; n <= degree; n++) {
  207. ConstIterator<Real> Pn = leg_poly.begin() + N * ((degree * 2 - m + 1) * (m + 0) / 2 + n);
  208. ConstIterator<Real> Pn_ = leg_poly.begin() + N * ((degree * 2 - m + 0) * (m + 1) / 2 + n) * (m < n);
  209. Iterator <Real> Hn = poly_val.begin() + N * ((degree * 2 - m + 1) * (m + 0) / 2 + n);
  210. Real c2 = sqrt<Real>(m<n ? (n+m+1)*(n-m) : 0);
  211. for (Long i = 0; i < N; i++) {
  212. Real c1 = (sin_theta[i]>0 ? m/sin_theta[i] : 0);
  213. Hn[i] = c1*cos_theta[i]*Pn[i] + c2*Pn_[i];
  214. }
  215. }
  216. }
  217. }
  218. static void gauss_legendre_approx(Vector<Real>& nds, Vector<Real>& wts, Integer order) {
  219. Vector<double> xd(order), wd(order);
  220. int kind = 1;
  221. double alpha = 0.0, beta = 0.0, a = 0.0, b = 1.0;
  222. cgqf(order, kind, (double)alpha, (double)beta, (double)a, (double)b, &xd[0], &wd[0]);
  223. if (nds.Dim()!=order) nds.ReInit(order);
  224. if (wts.Dim()!=order) wts.ReInit(order);
  225. for (Long i = 0; i < order; i++) {
  226. nds[i] = (Real)xd[i];
  227. wts[i] = (Real)wd[i];
  228. }
  229. }
  230. };
  231. template <class Real, bool UseGramSchmidtOrtho=false> class InterpQuadRule {
  232. public:
  233. template <class BasisObj> static Real Build(Vector<Real>& quad_nds, Vector<Real>& quad_wts, const BasisObj& integrands, bool symmetric = false, Real eps = 1e-16, Long ORDER = 0, Real nds_start = -1, Real nds_end = 1) {
  234. Vector<Real> nds, wts;
  235. adap_quad_rule(nds, wts, integrands, 0, 1, eps);
  236. Matrix<Real> M = integrands(nds);
  237. return Build(quad_nds, quad_wts, M, nds, wts, symmetric, eps, ORDER, nds_start, nds_end);
  238. }
  239. static Real Build(Vector<Real>& quad_nds, Vector<Real>& quad_wts, Matrix<Real> M, const Vector<Real>& nds, const Vector<Real>& wts, bool symmetric = false, Real eps = 1e-16, Long ORDER = 0, Real nds_start = -1, Real nds_end = 1) {
  240. Vector<Real> eps_vec;
  241. Vector<Long> ORDER_vec;
  242. if (ORDER) {
  243. ORDER_vec.PushBack(ORDER);
  244. } else {
  245. eps_vec.PushBack(eps);
  246. }
  247. Vector<Vector<Real>> quad_nds_;
  248. Vector<Vector<Real>> quad_wts_;
  249. Vector<Real> cond_num_vec = Build(quad_nds_, quad_wts_, M, nds, wts, symmetric, eps_vec, ORDER_vec, nds_start, nds_end);
  250. if (quad_nds_.Dim() && quad_wts_.Dim()) {
  251. quad_nds = quad_nds_[0];
  252. quad_wts = quad_wts_[0];
  253. return cond_num_vec[0];
  254. }
  255. return -1;
  256. }
  257. static Vector<Real> Build(Vector<Vector<Real>>& quad_nds, Vector<Vector<Real>>& quad_wts, const Matrix<Real>& M, const Vector<Real>& nds, const Vector<Real>& wts, bool symmetric = false, const Vector<Real>& eps_vec = Vector<Real>(), const Vector<Long>& ORDER_vec = Vector<Long>(), Real nds_start = -1, Real nds_end = 1) {
  258. Vector<Real> ret_vec;
  259. if (symmetric) {
  260. const Long N0 = M.Dim(0);
  261. const Long N1 = M.Dim(1);
  262. Matrix<Real> M_;
  263. M_.ReInit(N0, N1);
  264. for (Long i = 0; i < N0; i++) {
  265. for (Long j = 0; j < N1; j++) {
  266. M_[i][j] = (M[i][j] + M[N0-1-i][j])/2;
  267. }
  268. }
  269. Vector<Long> ORDER_vec_;
  270. for (const auto& x : ORDER_vec) ORDER_vec_.PushBack((x+1)/2);
  271. Vector<Vector<Real>> quad_nds_, quad_wts_;
  272. Real nds_mid_point = (nds_end+nds_start)/2;
  273. ret_vec = Build_helper(quad_nds_, quad_wts_, M_, nds, wts, eps_vec, ORDER_vec_, nds_start, nds_mid_point);
  274. const Long Nrules = quad_nds_.Dim();
  275. quad_nds.ReInit(Nrules);
  276. quad_wts.ReInit(Nrules);
  277. for (Long i = 0; i < Nrules; i++) {
  278. const Long N = quad_nds_[i].Dim();
  279. quad_nds[i].ReInit(2*N);
  280. quad_wts[i].ReInit(2*N);
  281. for (Long j = 0; j < N; j++) {
  282. quad_nds[i][j] = quad_nds_[i][j];
  283. quad_wts[i][j] = quad_wts_[i][j] / 2;
  284. quad_nds[i][2*N-1-j] = 2*nds_mid_point - quad_nds_[i][j];
  285. quad_wts[i][2*N-1-j] = quad_wts_[i][j] / 2;
  286. }
  287. }
  288. } else {
  289. ret_vec = Build_helper(quad_nds, quad_wts, M, nds, wts, eps_vec, ORDER_vec, nds_start, nds_end);
  290. }
  291. return ret_vec;
  292. }
  293. static void test() {
  294. const Integer ORDER = 28;
  295. auto integrands = [ORDER](const Vector<Real>& nds) {
  296. Integer K = ORDER;
  297. Long N = nds.Dim();
  298. Matrix<Real> M(N,K);
  299. for (Long j = 0; j < N; j++) {
  300. //for (Long i = 0; i < K; i++) {
  301. // M[j][i] = pow<Real>(nds[j],i);
  302. //}
  303. for (Long i = 0; i < K/2; i++) {
  304. M[j][i] = pow<Real>(nds[j],i);
  305. }
  306. for (Long i = K/2; i < K; i++) {
  307. M[j][i] = pow<Real>(nds[j],K-i-1) * log<Real>(nds[j]);
  308. }
  309. }
  310. return M;
  311. };
  312. Vector<Real> nds, wts;
  313. Real cond_num = InterpQuadRule::Build(nds, wts, integrands);
  314. std::cout<<cond_num<<'\n';
  315. }
  316. private:
  317. static Vector<Real> Build_helper(Vector<Vector<Real>>& quad_nds, Vector<Vector<Real>>& quad_wts, Matrix<Real> M, Vector<Real> nds, Vector<Real> wts, Vector<Real> eps_vec = Vector<Real>(), Vector<Long> ORDER_vec = Vector<Long>(), Real nds_start = 0, Real nds_end = 1) {
  318. if (M.Dim(0) * M.Dim(1) == 0) return Vector<Real>();
  319. Vector<Real> sqrt_wts(wts.Dim());
  320. for (Long i = 0; i < sqrt_wts.Dim(); i++) { // Set sqrt_wts
  321. SCTL_ASSERT(wts[i] > 0);
  322. sqrt_wts[i] = sqrt<Real>(wts[i]);
  323. }
  324. for (Long i = 0; i < M.Dim(0); i++) { // M <-- diag(sqrt_wts) * M
  325. Real sqrt_wts_ = sqrt_wts[i];
  326. for (Long j = 0; j < M.Dim(1); j++) {
  327. M[i][j] *= sqrt_wts_;
  328. }
  329. }
  330. Vector<Real> S_vec;
  331. auto modified_gram_schmidt = [](Matrix<Real>& Q, Vector<Real>& S, Vector<Long>& pivot, Matrix<Real> M, Real tol, Long max_rows, bool verbose) { // orthogonalize rows
  332. const Long N0 = M.Dim(0), N1 = M.Dim(1);
  333. if (N0*N1 == 0) return;
  334. Vector<Real> row_norm(N0);
  335. S.ReInit(max_rows); S.SetZero();
  336. pivot.ReInit(max_rows); pivot = -1;
  337. Q.ReInit(max_rows, N1); Q.SetZero();
  338. for (Long i = 0; i < max_rows; i++) {
  339. #pragma omp parallel for schedule(static)
  340. for (Long j = 0; j < N0; j++) { // compute row_norm
  341. Real row_norm2 = 0;
  342. for (Long k = 0; k < N1; k++) {
  343. row_norm2 += M[j][k]*M[j][k];
  344. }
  345. row_norm[j] = sqrt<Real>(row_norm2);
  346. }
  347. Long pivot_idx = 0;
  348. Real pivot_norm = 0;
  349. for (Long j = 0; j < N0; j++) { // determine pivot
  350. if (row_norm[j] > pivot_norm) {
  351. pivot_norm = row_norm[j];
  352. pivot_idx = j;
  353. }
  354. }
  355. pivot[i] = pivot_idx;
  356. S[i] = pivot_norm;
  357. #pragma omp parallel for schedule(static)
  358. for (Long k = 0; k < N1; k++) Q[i][k] = M[pivot_idx][k] / pivot_norm;
  359. #pragma omp parallel for schedule(static)
  360. for (Long j = 0; j < N0; j++) { // orthonormalize
  361. Real dot_prod = 0;
  362. for (Long k = 0; k < N1; k++) dot_prod += M[j][k] * Q[i][k];
  363. for (Long k = 0; k < N1; k++) M[j][k] -= Q[i][k] * dot_prod;
  364. }
  365. if (verbose) std::cout<<pivot_norm/S[0]<<'\n';
  366. if (pivot_norm/S[0] < tol) break;
  367. }
  368. };
  369. if (UseGramSchmidtOrtho) { // orthonormalize M and get truncation errors S_vec (using modified Gradm-Schmidt)
  370. Matrix<Real> Q;
  371. Vector<Long> pivot;
  372. Real eps = (eps_vec.Dim() ? eps_vec[eps_vec.Dim()-1] : machine_eps<Real>());
  373. modified_gram_schmidt(Q, S_vec, pivot, M.Transpose(), eps, M.Dim(1), false);
  374. if (1) {
  375. M = Q.Transpose();
  376. } else {
  377. M.ReInit(Q.Dim(1), Q.Dim(0));
  378. for (Long i = 0; i < Q.Dim(1); i++) {
  379. for (Long j = 0; j < Q.Dim(0); j++) {
  380. M[i][j] = Q[j][i] * S_vec[j];
  381. }
  382. }
  383. }
  384. } else { // using SVD
  385. // TODO: try M = W * M where W is a random matrix to reduce number of rows in M
  386. Matrix<Real> U, S, Vt;
  387. M.SVD(U,S,Vt);
  388. Long N = S.Dim(0);
  389. S_vec.ReInit(N);
  390. Vector<std::pair<Real,Long>> S_idx_lst(N);
  391. for (Long i = 0; i < N; i++) {
  392. S_idx_lst[i] = std::pair<Real,Long>(S[i][i],i);
  393. }
  394. std::sort(S_idx_lst.begin(), S_idx_lst.end(), std::greater<std::pair<Real,Long>>());
  395. for (Long i = 0; i < N; i++) {
  396. S_vec[i] = S_idx_lst[i].first;
  397. }
  398. Matrix<Real> UU(nds.Dim(),N);
  399. for (Long i = 0; i < nds.Dim(); i++) {
  400. for (Long j = 0; j < N; j++) {
  401. UU[i][j] = U[i][S_idx_lst[j].second];
  402. }
  403. }
  404. M = UU;
  405. }
  406. if (eps_vec.Dim()) { // Set ORDER_vec
  407. SCTL_ASSERT(!ORDER_vec.Dim());
  408. ORDER_vec.ReInit(eps_vec.Dim());
  409. for (Long i = 0; i < eps_vec.Dim(); i++) {
  410. ORDER_vec[i] = std::lower_bound(S_vec.begin(), S_vec.end(), eps_vec[i]*S_vec[0], std::greater<Real>()) - S_vec.begin();
  411. ORDER_vec[i] = std::min(std::max<Long>(ORDER_vec[i],1), S_vec.Dim());
  412. }
  413. }
  414. Vector<Real> cond_num_vec;
  415. quad_nds.ReInit(ORDER_vec.Dim());
  416. quad_wts.ReInit(ORDER_vec.Dim());
  417. auto build_quad_rule = [&nds_start, &nds_end, &nds, &modified_gram_schmidt](Vector<Real>& quad_nds, Vector<Real>& quad_wts, Matrix<Real> M, const Vector<Real>& sqrt_wts) {
  418. const Long idx0 = std::lower_bound(nds.begin(), nds.end(), nds_start) - nds.begin();
  419. const Long idx1 = std::lower_bound(nds.begin(), nds.end(), nds_end ) - nds.begin();
  420. const Long N = M.Dim(0), ORDER = M.Dim(1);
  421. { // Set quad_nds
  422. Matrix<Real> M_(N, ORDER);
  423. for (Long i = 0; i < idx0*ORDER; i++) M_[0][i] = 0;
  424. for (Long i = idx1*ORDER; i < N*ORDER; i++) M_[0][i] = 0;
  425. for (Long i = idx0; i < idx1; i++) {
  426. for (Long j = 0; j < ORDER; j++) {
  427. M_[i][j] = M[i][j] / sqrt_wts[i];
  428. }
  429. }
  430. Matrix<Real> Q;
  431. Vector<Real> S;
  432. Vector<Long> pivot_rows;
  433. modified_gram_schmidt(Q, S, pivot_rows, M_, machine_eps<Real>(), ORDER, false);
  434. quad_nds.ReInit(ORDER);
  435. for (Long i = 0; i < ORDER; i++) {
  436. SCTL_ASSERT(0<=pivot_rows[i] && pivot_rows[i]<N);
  437. quad_nds[i] = nds[pivot_rows[i]];
  438. }
  439. std::sort(quad_nds.begin(), quad_nds.end());
  440. if (0) { // print spectrum of the sub-matrix
  441. Matrix<Real> MM(ORDER,ORDER);
  442. for (Long i = 0; i < ORDER; i++) {
  443. for (Long j = 0; j < ORDER; j++) {
  444. MM[i][j] = M[pivot_rows[i]][j];
  445. }
  446. }
  447. Matrix<Real> U, S, Vt;
  448. MM.SVD(U,S,Vt);
  449. std::cout<<S<<'\n';
  450. }
  451. }
  452. Real cond_num, smallest_wt = 1;
  453. { // Set quad_wts, cond_num
  454. const Matrix<Real> b = Matrix<Real>(1, sqrt_wts.Dim(), (Iterator<Real>)sqrt_wts.begin()) * M;
  455. Matrix<Real> MM(ORDER,ORDER);
  456. { // Set MM <-- M[quad_nds][:] / sqrt_wts
  457. Vector<std::pair<Real,Long>> sorted_nds(nds.Dim());
  458. for (Long i = 0; i < nds.Dim(); i++) {
  459. sorted_nds[i].first = nds[i];
  460. sorted_nds[i].second = i;
  461. }
  462. std::sort(sorted_nds.begin(), sorted_nds.end());
  463. for (Long i = 0; i < ORDER; i++) { // Set MM <-- M[quad_nds][:] / sqrt_wts
  464. Long row_id = std::lower_bound(sorted_nds.begin(), sorted_nds.end(), std::pair<Real,Long>(quad_nds[i],0))->second;
  465. Real inv_sqrt_wts = 1/sqrt_wts[row_id];
  466. for (Long j = 0; j < ORDER; j++) {
  467. MM[i][j] = M[row_id][j] * inv_sqrt_wts;
  468. }
  469. }
  470. }
  471. { // set quad_wts <-- b * MM.pinv()
  472. Matrix<Real> U, S, Vt;
  473. MM.SVD(U,S,Vt);
  474. Real Smax = S[0][0], Smin = S[0][0];
  475. for (Long i = 0; i < ORDER; i++) {
  476. Smin = std::min<Real>(Smin, fabs<Real>(S[i][i]));
  477. Smax = std::max<Real>(Smax, fabs<Real>(S[i][i]));
  478. }
  479. cond_num = Smax / Smin;
  480. auto quad_wts_ = (b * Vt.Transpose()) * S.pinv(machine_eps<Real>()) * U.Transpose();
  481. quad_wts = Vector<Real>(ORDER, quad_wts_.begin(), false);
  482. for (const auto& a : quad_wts) smallest_wt = std::min<Real>(smallest_wt, a);
  483. }
  484. //std::cout<<(Matrix<Real>(1,ORDER,quad_wts.begin())*(Matrix<Real>(ORDER,1)*0+1))[0][0]-1<<'\n';
  485. }
  486. std::cout<<"condition number = "<<cond_num<<" nodes = "<<ORDER<<" smallest_wt = "<<smallest_wt<<'\n';
  487. return cond_num;
  488. };
  489. for (Long i = 0; i < ORDER_vec.Dim(); i++) {
  490. const Long N0 = M.Dim(0);
  491. const Long N1 = ORDER_vec[i];
  492. const Long N1_ = std::min(ORDER_vec[i],M.Dim(1));
  493. Matrix<Real> MM(N0, N1);
  494. for (Long j0 = 0; j0 < N0; j0++) {
  495. for (Long j1 = 0; j1 < N1_; j1++) MM[j0][j1] = M[j0][j1];
  496. for (Long j1 = N1_; j1 < N1 ; j1++) MM[j0][j1] = 0;
  497. }
  498. Real cond_num = build_quad_rule(quad_nds[i], quad_wts[i], MM, sqrt_wts);
  499. cond_num_vec.PushBack(cond_num);
  500. }
  501. return cond_num_vec;
  502. }
  503. template <class FnObj> static void adap_quad_rule(Vector<Real>& nds, Vector<Real>& wts, const FnObj& fn, Real a, Real b, Real tol) {
  504. const auto& nds0 = LegQuadRule<Real>::template nds<25>();
  505. const auto& wts0 = LegQuadRule<Real>::template wts<25>();
  506. const auto& nds1 = LegQuadRule<Real>::template nds<50>();
  507. const auto& wts1 = LegQuadRule<Real>::template wts<50>();
  508. auto concat_vec = [](const Vector<Real>& v0, const Vector<Real>& v1) {
  509. Long N0 = v0.Dim();
  510. Long N1 = v1.Dim();
  511. Vector<Real> v(N0 + N1);
  512. for (Long i = 0; i < N0; i++) v[ i] = v0[i];
  513. for (Long i = 0; i < N1; i++) v[N0+i] = v1[i];
  514. return v;
  515. };
  516. auto integration_error = [&fn,&nds0,&wts0,&nds1,&wts1](Real a, Real b) {
  517. const Matrix<Real> M0 = fn(nds0*(b-a)+a);
  518. const Matrix<Real> M1 = fn(nds1*(b-a)+a);
  519. const Long dof = M0.Dim(1);
  520. SCTL_ASSERT(M0.Dim(0) == nds0.Dim());
  521. SCTL_ASSERT(M1.Dim(0) == nds1.Dim());
  522. SCTL_ASSERT(M1.Dim(1) == dof);
  523. Real max_err = 0;
  524. for (Long i = 0; i < dof; i++) {
  525. Real I0 = 0, I1 = 0;
  526. for (Long j = 0; j < nds0.Dim(); j++) {
  527. I0 += M0[j][i] * wts0[j] * (b-a);
  528. }
  529. for (Long j = 0; j < nds1.Dim(); j++) {
  530. I1 += M1[j][i] * wts1[j] * (b-a);
  531. }
  532. max_err = std::max(max_err, fabs(I1-I0));
  533. }
  534. return max_err;
  535. };
  536. Real err = integration_error(a, b);
  537. if (err < tol) {
  538. //std::cout<<a<<" "<<b<<" "<<err<<'\n';
  539. nds = nds0 * (b-a) + a;
  540. wts = wts0 * (b-a);
  541. } else {
  542. Vector<Real> nds0_, wts0_, nds1_, wts1_;
  543. adap_quad_rule(nds0_, wts0_, fn, a, (a+b)/2, tol);
  544. adap_quad_rule(nds1_, wts1_, fn, (a+b)/2, b, tol);
  545. nds = concat_vec(nds0_, nds1_);
  546. wts = concat_vec(wts0_, wts1_);
  547. }
  548. };
  549. };
  550. }
  551. #endif // _SCTL_QUADRULE_HPP_