Grid 0.7.0
ZMobius.cc
Go to the documentation of this file.
1/*************************************************************************************
2
3 Grid physics library, www.github.com/paboyle/Grid
4
5 Source file: ./lib/algorithms/approx/ZMobius.cc
6
7 Copyright (C) 2015
8
9Author: Christopher Kelly <ckelly@phys.columbia.edu>
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License along
22 with this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24
25 See the full license in the file "LICENSE" in the top level distribution directory
26*************************************************************************************/
27/* END LEGAL */
28
31
34
35//Compute the tanh approximation
36inline double epsilonMobius(const double x, const std::vector<ComplexD> &w){
37 int Ls = w.size();
38
39 ComplexD fxp = 1., fmp = 1.;
40 for(int i=0;i<Ls;i++){
41 fxp = fxp * ( w[i] + x );
42 fmp = fmp * ( w[i] - x );
43 }
44 return ((fxp - fmp)/(fxp + fmp)).real();
45}
46inline double epsilonMobius(const double x, const std::vector<RealD> &w){
47 int Ls = w.size();
48
49 double fxp = 1., fmp = 1.;
50 for(int i=0;i<Ls;i++){
51 fxp = fxp * ( w[i] + x );
52 fmp = fmp * ( w[i] - x );
53 }
54 return (fxp - fmp)/(fxp + fmp);
55}
56
57
58
59//Compute the tanh approximation in a form suitable for the Remez
61 const std::vector<RealD> &omega = *( (std::vector<RealD> const*)data );
62 bigfloat fxp(1.0);
63 bigfloat fmp(1.0);
64
65 for(int i=0;i<omega.size();i++){
66 fxp = fxp * ( bigfloat(omega[i]) + x);
67 fmp = fmp * ( bigfloat(omega[i]) - x);
68 }
69 return (fxp - fmp)/(fxp + fmp);
70}
71
72//Compute the Zmobius Omega parameters suitable for eigenvalue range -lambda_bound <= lambda <= lambda_bound
73//Note omega_i = 1/(b_i + c_i) where b_i and c_i are the Mobius parameters
74void computeZmobiusOmega(std::vector<ComplexD> &omega_out, const int Ls_out,
75 const std::vector<RealD> &omega_in, const int Ls_in,
76 const RealD lambda_bound){
77 assert(omega_in.size() == Ls_in);
78 omega_out.resize(Ls_out);
79
80 //Use the Remez algorithm to generate the appropriate rational polynomial
81 //For odd polynomial, to satisfy Haar condition must take either positive or negative half of range (cf https://arxiv.org/pdf/0803.0439.pdf page 6)
82 AlgRemezGeneral remez(0, lambda_bound, 64, &epsilonMobius, (void*)&omega_in);
83 remez.generateApprox(Ls_out-1, Ls_out,AlgRemezGeneral::Odd, AlgRemezGeneral::Even, 1e-15, 100);
84 remez.csv(std::cout);
85
86 //The rational approximation has the form [ f(x) - f(-x) ] / [ f(x) + f(-x) ] where f(x) = \Prod_{i=0}^{L_s-1} ( \omega_i + x )
87 //cf https://academiccommons.columbia.edu/doi/10.7916/D8T72HD7 pg 102
88 //omega_i are therefore the negative of the complex roots of f(x)
89
90 //We can find the roots by recognizing that the eigenvalues of a matrix A are the roots of the characteristic polynomial
91 // \rho(\lambda) = det( A - \lambda I ) where I is the unit matrix
92 //The matrix whose characteristic polynomial is an arbitrary monic polynomial a0 + a1 x + a2 x^2 + ... x^n is the companion matrix
93 // A = | 0 1 0 0 0 .... 0 |
94 // | 0 0 1 0 0 .... 0 |
95 // | : : : : : : |
96 // | 0 0 0 0 0 1
97 // | -a0 -a1 -a2 ... ... -an|
98
99
100 //Note the Remez defines the largest power to have unit coefficient
101 std::vector<RealD> coeffs(Ls_out+1);
102 for(int i=0;i<Ls_out+1;i+=2) coeffs[i] = coeffs[i] = remez.getCoeffDen(i); //even powers
103 for(int i=1;i<Ls_out+1;i+=2) coeffs[i] = coeffs[i] = remez.getCoeffNum(i); //odd powers
104
105 std::vector<std::complex<RealD> > roots(Ls_out);
106
107 //Form the companion matrix
108 Eigen::MatrixXd compn(Ls_out,Ls_out);
109 for(int i=0;i<Ls_out-1;i++) compn(i,0) = 0.;
110 compn(Ls_out - 1, 0) = -coeffs[0];
111
112 for(int j=1;j<Ls_out;j++){
113 for(int i=0;i<Ls_out-1;i++) compn(i,j) = i == j-1 ? 1. : 0.;
114 compn(Ls_out - 1, j) = -coeffs[j];
115 }
116
117 //Eigensolve
118 Eigen::EigenSolver<Eigen::MatrixXd> slv(compn, false);
119
120 const auto & ev = slv.eigenvalues();
121 for(int i=0;i<Ls_out;i++)
122 omega_out[i] = -ev(i);
123
124 //Sort ascending (smallest at start of vector!)
125 std::sort(omega_out.begin(), omega_out.end(),
126 [&](const ComplexD &a, const ComplexD &b){ return a.real() < b.real() || (a.real() == b.real() && a.imag() < b.imag()); });
127
128 //McGlynn thesis pg 122 suggest improved iteration counts if magnitude of omega diminishes towards the center of the 5th dimension
129 std::vector<ComplexD> omega_tmp = omega_out;
130 int s_low=0, s_high=Ls_out-1, ss=0;
131 for(int s_from = Ls_out-1; s_from >= 0; s_from--){ //loop from largest omega
132 int s_to;
133 if(ss % 2 == 0){
134 s_to = s_low++;
135 }else{
136 s_to = s_high--;
137 }
138 omega_out[s_to] = omega_tmp[s_from];
139 ++ss;
140 }
141
142 std::cout << "Resulting omega_i:" << std::endl;
143 for(int i=0;i<Ls_out;i++)
144 std::cout << omega_out[i] << std::endl;
145
146 std::cout << "Test result matches the approximate polynomial found by the Remez" << std::endl;
147 std::cout << "<x> <remez approx> <poly approx> <diff poly approx remez approx> <exact> <diff poly approx exact>\n";
148
149 int npt = 60;
150 double dlt = lambda_bound/double(npt-1);
151
152 for (int i =0; i<npt; i++){
153 double x = i*dlt;
154 double r = remez.evaluateApprox(x);
155 double p = epsilonMobius(x, omega_out);
156 double e = epsilonMobius(x, omega_in);
157
158 std::cout << x<< " " << r << " " << p <<" " <<r-p << " " << e << " " << e-p << std::endl;
159 }
160
161}
162
163//mobius_param = b+c with b-c=1
164void computeZmobiusOmega(std::vector<ComplexD> &omega_out, const int Ls_out, const RealD mobius_param, const int Ls_in, const RealD lambda_bound){
165 std::vector<RealD> omega_in(Ls_in, 1./mobius_param);
166 computeZmobiusOmega(omega_out, Ls_out, omega_in, Ls_in, lambda_bound);
167}
168
169//ZMobius class takes gamma_i = (b+c) omega_i as its input, where b, c are factored out
170void computeZmobiusGamma(std::vector<ComplexD> &gamma_out,
171 const RealD mobius_param_out, const int Ls_out,
172 const RealD mobius_param_in, const int Ls_in,
173 const RealD lambda_bound){
174 computeZmobiusOmega(gamma_out, Ls_out, mobius_param_in, Ls_in, lambda_bound);
175 for(int i=0;i<Ls_out;i++) gamma_out[i] = gamma_out[i] * mobius_param_out;
176}
177//Assumes mobius_param_out == mobius_param_in
178void computeZmobiusGamma(std::vector<ComplexD> &gamma_out, const int Ls_out, const RealD mobius_param, const int Ls_in, const RealD lambda_bound){
179 computeZmobiusGamma(gamma_out, mobius_param, Ls_out, mobius_param, Ls_in, lambda_bound);
180}
181
#define NAMESPACE_BEGIN(A)
Definition Namespace.h:35
#define NAMESPACE_END(A)
Definition Namespace.h:36
std::complex< RealD > ComplexD
Definition Simd.h:79
double RealD
Definition Simd.h:61
void computeZmobiusGamma(std::vector< ComplexD > &gamma_out, const RealD mobius_param_out, const int Ls_out, const RealD mobius_param_in, const int Ls_in, const RealD lambda_bound)
Definition ZMobius.cc:170
double epsilonMobius(const double x, const std::vector< ComplexD > &w)
Definition ZMobius.cc:36
void computeZmobiusOmega(std::vector< ComplexD > &omega_out, const int Ls_out, const std::vector< RealD > &omega_in, const int Ls_in, const RealD lambda_bound)
Definition ZMobius.cc:74
double generateApprox(int num_degree, int den_degree, PolyType num_type, PolyType den_type, const double tolerance=1e-15, const int report_freq=1000)
double evaluateApprox(double x) const
void csv(std::ostream &os=std::cout) const
double getCoeffNum(const int i) const
double getCoeffDen(const int i) const