#ifndef _SCTL_QUADRULE_HPP_ #define _SCTL_QUADRULE_HPP_ #include #include SCTL_INCLUDE(matrix.hpp) #include SCTL_INCLUDE(math_utils.hpp) #include SCTL_INCLUDE(legendre_rule.hpp) #include namespace SCTL_NAMESPACE { template class ChebQuadRule { // p(x) public: template static const Vector& nds(Integer ChebOrder) { SCTL_ASSERT(ChebOrder < MAX_ORDER); auto compute_all = [](){ Vector> nds(MAX_ORDER); for (Long i = 0; i < MAX_ORDER; i++) { nds[i] = ComputeNds(i); } return nds; }; static const Vector> all_nds = compute_all(); return all_nds[ChebOrder]; } template static const Vector& wts(Integer ChebOrder) { SCTL_ASSERT(ChebOrder < MAX_ORDER); auto compute_all = [](){ Vector> wts(MAX_ORDER); for (Long i = 0; i < MAX_ORDER; i++) { wts[i] = ComputeWts(i); } return wts; }; static const Vector> all_wts = compute_all(); return all_wts[ChebOrder]; } template static const Vector& nds() { static Vector nds = ComputeNds(ChebOrder); return nds; } template static const Vector& wts() { static const Vector wts = ComputeWts(ChebOrder); return wts; } static Vector ComputeNds(Integer ChebOrder){ Vector nds(ChebOrder); for (Long i = 0; i < ChebOrder; i++) { nds[i] = 0.5 - cos((2*i+1)*const_pi()/(2*ChebOrder)) * 0.5; } return nds; } static Vector ComputeWts(Integer ChebOrder){ Matrix M_cheb(ChebOrder, ChebOrder); { // Set M_cheb for (Long i = 0; i < ChebOrder; i++) { Real theta = (2*i+1)*const_pi()/(2*ChebOrder); for (Long j = 0; j < ChebOrder; j++) { M_cheb[j][i] = cos(j*theta); } } M_cheb = M_cheb.pinv(machine_eps()); } Vector w_sample(ChebOrder); for (Integer i = 0; i < ChebOrder; i++) { w_sample[i] = (i % 2 ? 0 : -(ChebOrder/(Real)(i*i-1))); } Vector wts(ChebOrder); for (Integer j = 0; j < ChebOrder; j++) { wts[j] = 0; for (Integer i = 0; i < ChebOrder; i++) { wts[j] += M_cheb[j][i] * w_sample[i] / ChebOrder; } } return wts; } }; template class LegQuadRule { public: template static const Vector& nds(Integer Order) { SCTL_ASSERT(Order < MAX_ORDER); auto compute_all = [](){ Vector> nds(MAX_ORDER); for (Long i = 1; i < MAX_ORDER; i++) { nds[i] = ComputeNds(i); } return nds; }; static const Vector> all_nds = compute_all(); return all_nds[Order]; } template static const Vector& wts(Integer Order) { SCTL_ASSERT(Order < MAX_ORDER); auto compute_all = [](){ Vector> wts(MAX_ORDER); for (Long i = 1; i < MAX_ORDER; i++) { wts[i] = ComputeWts(nds(i)); } return wts; }; static const Vector> all_wts = compute_all(); return all_wts[Order]; } template static const Vector& nds() { static Vector nds = ComputeNds(Order); return nds; } template static const Vector& wts() { static const Vector wts = ComputeWts(nds()); return wts; } static Vector ComputeNds(Integer order) { Vector nds, wts; gauss_legendre_approx(nds, wts, order); nds = nds*2-1; auto EvalLegPoly = [](Vector& Pn, Vector& dPn, const Vector& nds, Long n) { Vector P, dP; LegPoly(P, nds, n); LegPolyDeriv(dP, nds, n); const Long M = nds.Dim(); if (Pn.Dim() != M) Pn.ReInit(M); if (dPn.Dim() != M) dPn.ReInit(M); Pn = Vector(M, P.begin() + n*M, false); dPn = Vector(M, dP.begin() + n*M, false); for (Long i = 0; i < M; i++) dPn[i] = -dPn[i] / sqrt(1-nds[i]*nds[i]); }; Vector Pn, dPn; // Newton iterations EvalLegPoly(Pn, dPn, nds, order); nds -= Pn/dPn; EvalLegPoly(Pn, dPn, nds, order); nds -= Pn/dPn; EvalLegPoly(Pn, dPn, nds, order); nds -= Pn/dPn; return (nds+1)/2; } static Vector ComputeWts(const Vector& nds) { const Long order = nds.Dim(); Vector cheb_nds = ChebQuadRule::ComputeNds(2*order-1)*2-1; Vector cheb_wts = ChebQuadRule::ComputeWts(2*order-1)*2; auto EvalLegPoly = [](const Vector& nds, Long n) { Vector P; LegPoly(P, nds, n); const Long M = nds.Dim(); return Matrix(n, M, P.begin()); }; Matrix b = EvalLegPoly(cheb_nds, 2*order-1) * Matrix(cheb_wts.Dim(), 1, cheb_wts.begin(), false); Matrix M = EvalLegPoly(nds*2-1, 2*order-1); Matrix wts = Matrix(M).pinv() * b; return Vector(wts.Dim(0), wts.begin())/2; } private: static void LegPoly(Vector& poly_val, const Vector& X, Long degree){ Vector theta(X.Dim()); for (Long i = 0; i < X.Dim(); i++) theta[i] = acos(X[i]); LegPoly_(poly_val, theta, degree); } static void LegPoly_(Vector& poly_val, const Vector& theta, Long degree){ Long N = theta.Dim(); Long Npoly = (degree + 1) * (degree + 2) / 2; if (poly_val.Dim() != Npoly * N) poly_val.ReInit(Npoly * N); Real fact = 1 / sqrt(4 * const_pi()); Vector cos_theta(N), sin_theta(N); for (Long n = 0; n < N; n++) { cos_theta[n] = cos(theta[n]); sin_theta[n] = sin(theta[n]); poly_val[n] = fact; } Long idx = 0; Long idx_nxt = 0; for (Long i = 1; i <= degree; i++) { idx_nxt += N*(degree-i+2); Real c = sqrt((2*i+1)/(Real)(2*i)); for (Long n = 0; n < N; n++) { poly_val[idx_nxt+n] = -poly_val[idx+n] * sin_theta[n] * c; } idx = idx_nxt; } idx = 0; for (Long m = 0; m < degree; m++) { for (Long n = 0; n < N; n++) { Real pmm = 0; Real pmmp1 = poly_val[idx+n]; for (Long ll = m + 1; ll <= degree; ll++) { Real a = sqrt(((2*ll-1)*(2*ll+1) ) / (Real)((ll-m)*(ll+m) )); Real b = sqrt(((2*ll+1)*(ll+m-1)*(ll-m-1)) / (Real)((ll-m)*(ll+m)*(2*ll-3))); Real pll = cos_theta[n]*a*pmmp1 - b*pmm; pmm = pmmp1; pmmp1 = pll; poly_val[idx + N*(ll-m) + n] = pll; } } idx += N * (degree - m + 1); } } static void LegPolyDeriv(Vector& poly_val, const Vector& X, Long degree){ Vector theta(X.Dim()); for (Long i = 0; i < X.Dim(); i++) theta[i] = acos(X[i]); LegPolyDeriv_(poly_val, theta, degree); } static void LegPolyDeriv_(Vector& poly_val, const Vector& theta, Long degree){ Long N = theta.Dim(); Long Npoly = (degree + 1) * (degree + 2) / 2; if (poly_val.Dim() != N * Npoly) poly_val.ReInit(N * Npoly); Vector cos_theta(N), sin_theta(N); for (Long i = 0; i < N; i++) { cos_theta[i] = cos(theta[i]); sin_theta[i] = sin(theta[i]); } Vector leg_poly(Npoly * N); LegPoly_(leg_poly, theta, degree); for (Long m = 0; m <= degree; m++) { for (Long n = m; n <= degree; n++) { ConstIterator Pn = leg_poly.begin() + N * ((degree * 2 - m + 1) * (m + 0) / 2 + n); ConstIterator Pn_ = leg_poly.begin() + N * ((degree * 2 - m + 0) * (m + 1) / 2 + n) * (m < n); Iterator Hn = poly_val.begin() + N * ((degree * 2 - m + 1) * (m + 0) / 2 + n); Real c2 = sqrt(m0 ? m/sin_theta[i] : 0); Hn[i] = c1*cos_theta[i]*Pn[i] + c2*Pn_[i]; } } } } static void gauss_legendre_approx(Vector& nds, Vector& wts, Integer order) { Vector xd(order), wd(order); int kind = 1; double alpha = 0.0, beta = 0.0, a = 0.0, b = 1.0; cgqf(order, kind, (double)alpha, (double)beta, (double)a, (double)b, &xd[0], &wd[0]); if (nds.Dim()!=order) nds.ReInit(order); if (wts.Dim()!=order) wts.ReInit(order); for (Long i = 0; i < order; i++) { nds[i] = (Real)xd[i]; wts[i] = (Real)wd[i]; } } }; template class InterpQuadRule { public: template static Real Build(Vector& quad_nds, Vector& quad_wts, const BasisObj& integrands, bool symmetric = false, Real eps = 1e-16, Long ORDER = 0, Real nds_start = -1, Real nds_end = 1) { Vector nds, wts; adap_quad_rule(nds, wts, integrands, 0, 1, eps); Matrix M = integrands(nds); return Build(quad_nds, quad_wts, M, nds, wts, symmetric, eps, ORDER, nds_start, nds_end); } static Real Build(Vector& quad_nds, Vector& quad_wts, Matrix M, const Vector& nds, const Vector& wts, bool symmetric = false, Real eps = 1e-16, Long ORDER = 0, Real nds_start = -1, Real nds_end = 1) { Vector eps_vec; Vector ORDER_vec; if (ORDER) { ORDER_vec.PushBack(ORDER); } else { eps_vec.PushBack(eps); } Vector> quad_nds_; Vector> quad_wts_; Vector cond_num_vec = Build(quad_nds_, quad_wts_, M, nds, wts, symmetric, eps_vec, ORDER_vec, nds_start, nds_end); if (quad_nds_.Dim() && quad_wts_.Dim()) { quad_nds = quad_nds_[0]; quad_wts = quad_wts_[0]; return cond_num_vec[0]; } return -1; } static Vector Build(Vector>& quad_nds, Vector>& quad_wts, const Matrix& M, const Vector& nds, const Vector& wts, bool symmetric = false, const Vector& eps_vec = Vector(), const Vector& ORDER_vec = Vector(), Real nds_start = -1, Real nds_end = 1) { Vector ret_vec; if (symmetric) { const Long N0 = M.Dim(0); const Long N1 = M.Dim(1); Matrix M_; M_.ReInit(N0, N1); for (Long i = 0; i < N0; i++) { for (Long j = 0; j < N1; j++) { M_[i][j] = (M[i][j] + M[N0-1-i][j])/2; } } Vector ORDER_vec_; for (const auto& x : ORDER_vec) ORDER_vec_.PushBack((x+1)/2); Vector> quad_nds_, quad_wts_; Real nds_mid_point = (nds_end+nds_start)/2; ret_vec = Build_helper(quad_nds_, quad_wts_, M_, nds, wts, eps_vec, ORDER_vec_, nds_start, nds_mid_point); const Long Nrules = quad_nds_.Dim(); quad_nds.ReInit(Nrules); quad_wts.ReInit(Nrules); for (Long i = 0; i < Nrules; i++) { const Long N = quad_nds_[i].Dim(); quad_nds[i].ReInit(2*N); quad_wts[i].ReInit(2*N); for (Long j = 0; j < N; j++) { quad_nds[i][j] = quad_nds_[i][j]; quad_wts[i][j] = quad_wts_[i][j] / 2; quad_nds[i][2*N-1-j] = 2*nds_mid_point - quad_nds_[i][j]; quad_wts[i][2*N-1-j] = quad_wts_[i][j] / 2; } } } else { ret_vec = Build_helper(quad_nds, quad_wts, M, nds, wts, eps_vec, ORDER_vec, nds_start, nds_end); } return ret_vec; } static void test() { const Integer ORDER = 28; auto integrands = [ORDER](const Vector& nds) { Integer K = ORDER; Long N = nds.Dim(); Matrix M(N,K); for (Long j = 0; j < N; j++) { //for (Long i = 0; i < K; i++) { // M[j][i] = pow(nds[j],i); //} for (Long i = 0; i < K/2; i++) { M[j][i] = pow(nds[j],i); } for (Long i = K/2; i < K; i++) { M[j][i] = pow(nds[j],K-i-1) * log(nds[j]); } } return M; }; Vector nds, wts; Real cond_num = InterpQuadRule::Build(nds, wts, integrands); std::cout< Build_helper(Vector>& quad_nds, Vector>& quad_wts, Matrix M, Vector nds, Vector wts, Vector eps_vec = Vector(), Vector ORDER_vec = Vector(), Real nds_start = 0, Real nds_end = 1) { if (M.Dim(0) * M.Dim(1) == 0) return Vector(); Vector sqrt_wts(wts.Dim()); for (Long i = 0; i < sqrt_wts.Dim(); i++) { // Set sqrt_wts SCTL_ASSERT(wts[i] > 0); sqrt_wts[i] = sqrt(wts[i]); } for (Long i = 0; i < M.Dim(0); i++) { // M <-- diag(sqrt_wts) * M Real sqrt_wts_ = sqrt_wts[i]; for (Long j = 0; j < M.Dim(1); j++) { M[i][j] *= sqrt_wts_; } } Vector S_vec; auto modified_gram_schmidt = [](Matrix& Q, Vector& S, Vector& pivot, Matrix M, Real tol, Long max_rows, bool verbose) { // orthogonalize rows const Long N0 = M.Dim(0), N1 = M.Dim(1); if (N0*N1 == 0) return; Vector row_norm(N0); S.ReInit(max_rows); S.SetZero(); pivot.ReInit(max_rows); pivot = -1; Q.ReInit(max_rows, N1); Q.SetZero(); for (Long i = 0; i < max_rows; i++) { #pragma omp parallel for schedule(static) for (Long j = 0; j < N0; j++) { // compute row_norm Real row_norm2 = 0; for (Long k = 0; k < N1; k++) { row_norm2 += M[j][k]*M[j][k]; } row_norm[j] = sqrt(row_norm2); } Long pivot_idx = 0; Real pivot_norm = 0; for (Long j = 0; j < N0; j++) { // determine pivot if (row_norm[j] > pivot_norm) { pivot_norm = row_norm[j]; pivot_idx = j; } } pivot[i] = pivot_idx; S[i] = pivot_norm; #pragma omp parallel for schedule(static) for (Long k = 0; k < N1; k++) Q[i][k] = M[pivot_idx][k] / pivot_norm; #pragma omp parallel for schedule(static) for (Long j = 0; j < N0; j++) { // orthonormalize Real dot_prod = 0; for (Long k = 0; k < N1; k++) dot_prod += M[j][k] * Q[i][k]; for (Long k = 0; k < N1; k++) M[j][k] -= Q[i][k] * dot_prod; } if (verbose) std::cout< Q; Vector pivot; Real eps = (eps_vec.Dim() ? eps_vec[eps_vec.Dim()-1] : machine_eps()); modified_gram_schmidt(Q, S_vec, pivot, M.Transpose(), eps, M.Dim(1), false); if (1) { M = Q.Transpose(); } else { M.ReInit(Q.Dim(1), Q.Dim(0)); for (Long i = 0; i < Q.Dim(1); i++) { for (Long j = 0; j < Q.Dim(0); j++) { M[i][j] = Q[j][i] * S_vec[j]; } } } } else { // using SVD // TODO: try M = W * M where W is a random matrix to reduce number of rows in M Matrix U, S, Vt; M.SVD(U,S,Vt); Long N = S.Dim(0); S_vec.ReInit(N); Vector> S_idx_lst(N); for (Long i = 0; i < N; i++) { S_idx_lst[i] = std::pair(S[i][i],i); } std::sort(S_idx_lst.begin(), S_idx_lst.end(), std::greater>()); for (Long i = 0; i < N; i++) { S_vec[i] = S_idx_lst[i].first; } Matrix UU(nds.Dim(),N); for (Long i = 0; i < nds.Dim(); i++) { for (Long j = 0; j < N; j++) { UU[i][j] = U[i][S_idx_lst[j].second]; } } M = UU; } if (eps_vec.Dim()) { // Set ORDER_vec SCTL_ASSERT(!ORDER_vec.Dim()); ORDER_vec.ReInit(eps_vec.Dim()); for (Long i = 0; i < eps_vec.Dim(); i++) { ORDER_vec[i] = std::lower_bound(S_vec.begin(), S_vec.end(), eps_vec[i]*S_vec[0], std::greater()) - S_vec.begin(); ORDER_vec[i] = std::min(std::max(ORDER_vec[i],1), S_vec.Dim()); } } Vector cond_num_vec; quad_nds.ReInit(ORDER_vec.Dim()); quad_wts.ReInit(ORDER_vec.Dim()); auto build_quad_rule = [&nds_start, &nds_end, &nds, &modified_gram_schmidt](Vector& quad_nds, Vector& quad_wts, Matrix M, const Vector& sqrt_wts) { const Long idx0 = std::lower_bound(nds.begin(), nds.end(), nds_start) - nds.begin(); const Long idx1 = std::lower_bound(nds.begin(), nds.end(), nds_end ) - nds.begin(); const Long N = M.Dim(0), ORDER = M.Dim(1); { // Set quad_nds Matrix M_(N, ORDER); for (Long i = 0; i < idx0*ORDER; i++) M_[0][i] = 0; for (Long i = idx1*ORDER; i < N*ORDER; i++) M_[0][i] = 0; for (Long i = idx0; i < idx1; i++) { for (Long j = 0; j < ORDER; j++) { M_[i][j] = M[i][j] / sqrt_wts[i]; } } Matrix Q; Vector S; Vector pivot_rows; modified_gram_schmidt(Q, S, pivot_rows, M_, machine_eps(), ORDER, false); quad_nds.ReInit(ORDER); for (Long i = 0; i < ORDER; i++) { SCTL_ASSERT(0<=pivot_rows[i] && pivot_rows[i] MM(ORDER,ORDER); for (Long i = 0; i < ORDER; i++) { for (Long j = 0; j < ORDER; j++) { MM[i][j] = M[pivot_rows[i]][j]; } } Matrix U, S, Vt; MM.SVD(U,S,Vt); std::cout< b = Matrix(1, sqrt_wts.Dim(), (Iterator)sqrt_wts.begin()) * M; Matrix MM(ORDER,ORDER); { // Set MM <-- M[quad_nds][:] / sqrt_wts Vector> sorted_nds(nds.Dim()); for (Long i = 0; i < nds.Dim(); i++) { sorted_nds[i].first = nds[i]; sorted_nds[i].second = i; } std::sort(sorted_nds.begin(), sorted_nds.end()); for (Long i = 0; i < ORDER; i++) { // Set MM <-- M[quad_nds][:] / sqrt_wts Long row_id = std::lower_bound(sorted_nds.begin(), sorted_nds.end(), std::pair(quad_nds[i],0))->second; Real inv_sqrt_wts = 1/sqrt_wts[row_id]; for (Long j = 0; j < ORDER; j++) { MM[i][j] = M[row_id][j] * inv_sqrt_wts; } } } { // set quad_wts <-- b * MM.pinv() Matrix U, S, Vt; MM.SVD(U,S,Vt); Real Smax = S[0][0], Smin = S[0][0]; for (Long i = 0; i < ORDER; i++) { Smin = std::min(Smin, fabs(S[i][i])); Smax = std::max(Smax, fabs(S[i][i])); } cond_num = Smax / Smin; auto quad_wts_ = (b * Vt.Transpose()) * S.pinv(machine_eps()) * U.Transpose(); quad_wts = Vector(ORDER, quad_wts_.begin(), false); for (const auto& a : quad_wts) smallest_wt = std::min(smallest_wt, a); } //std::cout<<(Matrix(1,ORDER,quad_wts.begin())*(Matrix(ORDER,1)*0+1))[0][0]-1<<'\n'; } std::cout<<"condition number = "< nds0_, wts0_, nds1_, wts1_; adap_quad_rule(nds0_, wts0_, fn, a, (a+b)/2, tol); adap_quad_rule(nds1_, wts1_, fn, (a+b)/2, b, tol); nds = concat_vec(nds0_, nds1_); wts = concat_vec(wts0_, wts1_); } }; }; } #endif // _SCTL_QUADRULE_HPP_