// MFEM Example 33 - Serial/Parallel Shared Code // (Implementation of the AAA algorithm) // // Here, we implement the triple-A algorithm [1] for the rational approximation // of complex-valued functions, // // p(z)/q(z) ≈ f(z). // // In this file, we always assume f(z) = z^{-α}. The triple-A algorithm // provides a robust, accurate approximation in rational barycentric form. // This representation must be transformed into a partial fraction // representation in order to be used to solve a spectral FPDE. // // More specifically, we first expand the numerator in terms of the zeros of // the rational approximation, // // p(z) ∝ Π_i (z - z_i), // // and expand the denominator in terms of the poles of the rational // approximation, // // q(z) ∝ Π_i (z - p_i). // // We then use these zeros and poles to derive the partial fraction expansion // // f(z) ≈ p(z)/q(z) = Σ_i c_i / (z - p_i). // // [1] Nakatsukasa, Y., Sète, O., & Trefethen, L. N. (2018). The AAA algorithm // for rational approximation. SIAM Journal on Scientific Computing, 40(3), // A1494-A1522. #include "mfem.hpp" #include #include #include using namespace std; using namespace mfem; /** RationalApproximation_AAA: compute the rational approximation (RA) of data @a val [in] at the set of points @a pt [in]. @param[in] val Vector of data values @param[in] pt Vector of sample points @param[in] tol Relative tolerance @param[in] max_order Maximum number of terms (order) of the RA @param[out] z Support points of the RA in rational barycentric form @param[out] f Data values at support points @a z @param[out] w Weights of the RA in rational barycentric form See pg. A1501 of Nakatsukasa et al. [1]. */ void RationalApproximation_AAA(const Vector &val, const Vector &pt, Array &z, Array &f, Vector &w, double tol, int max_order) { // number of sample points int size = val.Size(); MFEM_VERIFY(pt.Size() == size, "size mismatch"); // Initializations Array J(size); for (int i = 0; i < size; i++) { J[i] = i; } z.SetSize(0); f.SetSize(0); DenseMatrix C, Ctemp, A, Am; // auxiliary arrays and vectors Vector f_vec; Array c_i; // mean of the value vector Vector R(val.Size()); double mean_val = val.Sum()/size; for (int i = 0; i tmp_max) { tmp_max = tmp; idx = j; } } // Append support points and data values z.Append(pt(idx)); f.Append(val(idx)); // Update index vector J.DeleteFirst(idx); // next column in Cauchy matrix Array C_tmp(size); for (int j = 0; j < size; j++) { C_tmp[j] = 1.0/(pt(j)-pt(idx)); } c_i.Append(C_tmp); int h_C = C_tmp.Size(); int w_C = k+1; C.UseExternalData(c_i.GetData(),h_C,w_C); Ctemp = C; f_vec.SetDataAndSize(f.GetData(),f.Size()); Ctemp.InvLeftScaling(val); Ctemp.RightScaling(f_vec); A.SetSize(C.Height(), C.Width()); Add(C,Ctemp,-1.0,A); A.LeftScaling(val); int h_Am = J.Size(); int w_Am = A.Width(); Am.SetSize(h_Am,w_Am); for (int i = 0; i & poles, Array & zeros, double &scale) { // Initialization poles.SetSize(0); zeros.SetSize(0); // Compute the poles int m = w.Size(); DenseMatrix B(m+1); B = 0.; DenseMatrix E(m+1); E = 0.; for (int i = 1; i<=m; i++) { B(i,i) = 1.; E(0,i) = w(i-1); E(i,0) = 1.; E(i,i) = z(i-1); } #ifdef MFEM_USE_LAPACK DenseMatrixGeneralizedEigensystem eig1(E,B); eig1.Eval(); Vector & evalues = eig1.EigenvaluesRealPart(); for (int i = 0; i & poles, Array & zeros, Array & coeffs) { int psize = poles.Size(); int zsize = zeros.Size(); coeffs.SetSize(psize); coeffs = scale; // Note: C p(z)/q(z) = Σ_i c_i / (z - p_i) results in an system of equations // where the N unknowns are the coefficients c_i. After multiplying the // system with q(z), the coefficients c_i can be computed analytically by // choosing N values for z. Choosing z_j = = p_j diagonalizes the system and // one can obtain an analytic form for the c_i coefficients. The result is // implemented in the code block below. for (int i=0; i & coeffs, Array & poles, double lmax = 1000., double tol=1e-10, int npoints = 1000, int max_order = 100) { MFEM_VERIFY(alpha < 1., "alpha must be less than 1"); MFEM_VERIFY(alpha > 0., "alpha must be greater than 0"); MFEM_VERIFY(npoints > 2, "npoints must be greater than 2"); MFEM_VERIFY(lmax > 0, "lmin must be greater than 0"); MFEM_VERIFY(tol > 0, "tol must be greater than 0"); bool print_warning = true; #ifdef MFEM_USE_MPI if ((Mpi::IsInitialized() && !Mpi::Root())) { print_warning = false; } #endif #ifndef MFEM_USE_LAPACK if (print_warning) { mfem::out << "\n" << string(80, '=') << "\nMFEM is compiled without LAPACK." << "\nUsing precomputed values for PartialFractionApproximation." << "\nOnly alpha = 0.33, 0.5, and 0.99 are available." << "\nThe default is alpha = 0.5.\n" << string(80, '=') << "\n" << endl; } const double eps = std::numeric_limits::epsilon(); if (abs(alpha - 0.33) < eps) { coeffs = Array ({1.821898e+03, 9.101221e+01, 2.650611e+01, 1.174937e+01, 6.140444e+00, 3.441713e+00, 1.985735e+00, 1.162634e+00, 6.891560e-01, 4.111574e-01, 2.298736e-01}); poles = Array ({-4.155583e+04, -2.956285e+03, -8.331715e+02, -3.139332e+02, -1.303448e+02, -5.563385e+01, -2.356255e+01, -9.595516e+00, -3.552160e+00, -1.032136e+00, -1.241480e-01}); } else if (abs(alpha - 0.99) < eps) { coeffs = Array({2.919591e-02, 1.419750e-02, 1.065798e-02, 9.395094e-03, 8.915329e-03, 8.822991e-03, 9.058247e-03, 9.814521e-03, 1.180396e-02, 1.834554e-02, 9.840482e-01}); poles = Array ({-1.069683e+04, -1.769370e+03, -5.718374e+02, -2.242095e+02, -9.419132e+01, -4.031012e+01, -1.701525e+01, -6.810088e+00, -2.382810e+00, -5.700059e-01, -1.384324e-03}); } else { if (abs(alpha - 0.5) > eps && print_warning) { alpha = 0.5; } coeffs = Array({2.290262e+02, 2.641819e+01, 1.005566e+01, 5.390411e+00, 3.340725e+00, 2.211205e+00, 1.508883e+00, 1.049474e+00, 7.462709e-01, 5.482686e-01, 4.232510e-01, 3.578967e-01}); poles = Array({-3.168211e+04, -3.236077e+03, -9.868287e+02, -3.945597e+02, -1.738889e+02, -7.925178e+01, -3.624992e+01, -1.629196e+01, -6.982956e+00, -2.679984e+00, -7.782607e-01, -7.649166e-02}); } if (print_warning) { mfem::out << "=> Using precomputed values for alpha = " << alpha << "\n" << std::endl; } return; #endif Vector x(npoints); Vector val(npoints); double dx = lmax / (double)(npoints-1); for (int i = 0; i z, f; Vector w; RationalApproximation_AAA(val,x,z,f,w,tol,max_order); Vector vecz, vecf; vecz.SetDataAndSize(z.GetData(), z.Size()); vecf.SetDataAndSize(f.GetData(), f.Size()); // Compute poles and zeros for RA of f(x) = x^{1-a} double scale; Array zeros; ComputePolesAndZeros(vecz, vecf, w, poles, zeros, scale); // Remove the zero at x=0, thus, delivering a RA for f(x) = x^{-a} zeros.DeleteFirst(0.0); // Compute partial fraction approximation of f(x) = x^{-a} PartialFractionExpansion(scale, poles, zeros, coeffs); }