Grid 0.7.0
RemezGeneral.cc
Go to the documentation of this file.
1#include<math.h>
2#include<stdio.h>
3#include<stdlib.h>
4#include<string>
5#include<iostream>
6#include<iomanip>
7#include<cassert>
8
10
11
12// Constructor
13AlgRemezGeneral::AlgRemezGeneral(double lower, double upper, long precision,
14 bigfloat (*f)(bigfloat x, void *data), void *data): f(f),
15 data(data),
16 prec(precision),
17 apstrt(lower), apend(upper), apwidt(upper - lower),
18 n(0), d(0), pow_n(0), pow_d(0)
19{
21
22 std::cout<<"Approximation bounds are ["<<apstrt<<","<<apend<<"]\n";
23 std::cout<<"Precision of arithmetic is "<<precision<<std::endl;
24}
25
26//Determine the properties of the numerator and denominator polynomials
27void AlgRemezGeneral::setupPolyProperties(int num_degree, int den_degree, PolyType num_type_in, PolyType den_type_in){
28 pow_n = num_degree;
29 pow_d = den_degree;
30
31 if(pow_n % 2 == 0 && num_type_in == PolyType::Odd) assert(0);
32 if(pow_n % 2 == 1 && num_type_in == PolyType::Even) assert(0);
33
34 if(pow_d % 2 == 0 && den_type_in == PolyType::Odd) assert(0);
35 if(pow_d % 2 == 1 && den_type_in == PolyType::Even) assert(0);
36
37 num_type = num_type_in;
38 den_type = den_type_in;
39
40 num_pows.resize(pow_n+1);
41 den_pows.resize(pow_d+1);
42
43 int n_in = 0;
46 for(int i=0;i<=pow_n;i++){
47 num_pows[i] = -1;
48 if(i % 2 == 0 && even) num_pows[i] = n_in++;
49 if(i % 2 == 1 && odd) num_pows[i] = n_in++;
50 }
51
52 std::cout << n_in << " terms in numerator" << std::endl;
53 --n_in; //power is 1 less than the number of terms, eg pow=1 a x^1 + b x^0
54
55 int d_in = 0;
58 for(int i=0;i<=pow_d;i++){
59 den_pows[i] = -1;
60 if(i % 2 == 0 && even) den_pows[i] = d_in++;
61 if(i % 2 == 1 && odd) den_pows[i] = d_in++;
62 }
63
64 std::cout << d_in << " terms in denominator" << std::endl;
65 --d_in;
66
67 n = n_in;
68 d = d_in;
69}
70
71//Setup algorithm
73 spread = 1.0e37;
74 iter = 0;
75
76 neq = n + d + 1; //not +2 because highest-power term in denominator is fixed to 1
77
78 param.resize(neq);
79 yy.resize(neq+1);
80
81 //Initialize linear equation temporaries
82 A.resize(neq*neq);
83 B.resize(neq);
84 IPS.resize(neq);
85
86 //Initialize maximum and minimum errors
87 xx.resize(neq+2);
88 mm.resize(neq+1);
90
91 //Initialize search steps
92 step.resize(neq+1);
93 stpini();
94}
95
96double AlgRemezGeneral::generateApprox(const int num_degree, const int den_degree,
97 const PolyType num_type_in, const PolyType den_type_in,
98 const double _tolerance, const int report_freq){
99 //Setup the properties of the polynomial
100 setupPolyProperties(num_degree, den_degree, num_type_in, den_type_in);
101
102 //Setup the algorithm
104
105 bigfloat tolerance = _tolerance;
106
107 //Iterate until convergance
108 while (spread > tolerance) {
109 if (iter++ % report_freq==0)
110 std::cout<<"Iteration " <<iter-1<<" spread "<<(double)spread<<" delta "<<(double)delta << std::endl;
111
112 equations();
113 if (delta < tolerance) {
114 std::cout<<"Iteration " << iter-1 << " delta too small (" << delta << "<" << tolerance << "), try increasing precision\n";
115 assert(0);
116 };
117 assert( delta>= tolerance );
118
119 search();
120 }
121
122 int sign;
123 double error = (double)getErr(mm[0],&sign);
124 std::cout<<"Converged at "<<iter<<" iterations; error = "<<error<<std::endl;
125
126 // Return the maximum error in the approximation
127 return error;
128}
129
130
131// Initial values of maximal and minimal errors
133 // Supply initial guesses for solution points
134 long ncheb = neq; // Degree of Chebyshev error estimate
135
136 // Find ncheb+1 extrema of Chebyshev polynomial
137 bigfloat a = ncheb;
138 bigfloat r;
139
140 mm[0] = apstrt;
141 for (long i = 1; i < ncheb; i++) {
142 r = 0.5 * (1 - cos((M_PI * i)/(double) a));
143 //r *= sqrt_bf(r);
144 r = (exp((double)r)-1.0)/(exp(1.0)-1.0);
145 mm[i] = apstrt + r * apwidt;
146 }
147 mm[ncheb] = apend;
148
149 a = 2.0 * ncheb;
150 for (long i = 0; i <= ncheb; i++) {
151 r = 0.5 * (1 - cos(M_PI * (2*i+1)/(double) a));
152 //r *= sqrt_bf(r); // Squeeze to low end of interval
153 r = (exp((double)r)-1.0)/(exp(1.0)-1.0);
154 xx[i] = apstrt + r * apwidt;
155 }
156}
157
158// Initialise step sizes
160 xx[neq+1] = apend;
161 delta = 0.25;
162 step[0] = xx[0] - apstrt;
163 for (int i = 1; i < neq; i++) step[i] = xx[i] - xx[i-1];
164 step[neq] = step[neq-1];
165}
166
167// Search for error maxima and minima
169 bigfloat a, q, xm, ym, xn, yn, xx1;
170 int emsign, ensign, steps;
171
172 int meq = neq + 1;
173
174 bigfloat eclose = 1.0e30;
175 bigfloat farther = 0l;
176
177 bigfloat xx0 = apstrt;
178
179 for (int i = 0; i < meq; i++) {
180 steps = 0;
181 xx1 = xx[i]; // Next zero
182 if (i == meq-1) xx1 = apend;
183 xm = mm[i];
184 ym = getErr(xm,&emsign);
185 q = step[i];
186 xn = xm + q;
187 if (xn < xx0 || xn >= xx1) { // Cannot skip over adjacent boundaries
188 q = -q;
189 xn = xm;
190 yn = ym;
191 ensign = emsign;
192 } else {
193 yn = getErr(xn,&ensign);
194 if (yn < ym) {
195 q = -q;
196 xn = xm;
197 yn = ym;
198 ensign = emsign;
199 }
200 }
201
202 while(yn >= ym) { // March until error becomes smaller.
203 if (++steps > 10)
204 break;
205
206 ym = yn;
207 xm = xn;
208 emsign = ensign;
209 a = xm + q;
210 if (a == xm || a <= xx0 || a >= xx1)
211 break;// Must not skip over the zeros either side.
212
213 xn = a;
214 yn = getErr(xn,&ensign);
215 }
216
217 mm[i] = xm; // Position of maximum
218 yy[i] = ym; // Value of maximum
219
220 if (eclose > ym) eclose = ym;
221 if (farther < ym) farther = ym;
222
223 xx0 = xx1; // Walk to next zero.
224 } // end of search loop
225
226 q = (farther - eclose); // Decrease step size if error spread increased
227
228 if (eclose != 0.0) q /= eclose; // Relative error spread
229
230 if (q >= spread)
231 delta *= 0.5; // Spread is increasing; decrease step size
232
233 spread = q;
234
235 for (int i = 0; i < neq; i++) {
236 q = yy[i+1];
237 if (q != 0.0) q = yy[i] / q - (bigfloat)1l;
238 else q = 0.0625;
239 if (q > (bigfloat)0.25) q = 0.25;
240 q *= mm[i+1] - mm[i];
241 step[i] = q * delta;
242 }
243 step[neq] = step[neq-1];
244
245 for (int i = 0; i < neq; i++) { // Insert new locations for the zeros.
246 xm = xx[i] - step[i];
247
248 if (xm <= apstrt)
249 continue;
250
251 if (xm >= apend)
252 continue;
253
254 if (xm <= mm[i])
255 xm = (bigfloat)0.5 * (mm[i] + xx[i]);
256
257 if (xm >= mm[i+1])
258 xm = (bigfloat)0.5 * (mm[i+1] + xx[i]);
259
260 xx[i] = xm;
261 }
262}
263
264// Solve the equations
266 bigfloat x, y, z;
267 bigfloat *aa;
268
269 for (int i = 0; i < neq; i++) { // set up the equations for solution by simq()
270 int ip = neq * i; // offset to 1st element of this row of matrix
271 x = xx[i]; // the guess for this row
272 y = func(x); // right-hand-side vector
273
274 z = (bigfloat)1l;
275 aa = A.data()+ip;
276 int t = 0;
277 for (int j = 0; j <= pow_n; j++) {
278 if(num_pows[j] != -1){ *aa++ = z; t++; }
279 z *= x;
280 }
281 assert(t == n+1);
282
283 z = (bigfloat)1l;
284 t = 0;
285 for (int j = 0; j < pow_d; j++) {
286 if(den_pows[j] != -1){ *aa++ = -y * z; t++; }
287 z *= x;
288 }
289 assert(t == d);
290
291 B[i] = y * z; // Right hand side vector
292 }
293
294 // Solve the simultaneous linear equations.
295 if (simq()){
296 std::cout<<"simq failed\n";
297 exit(0);
298 }
299}
300
301
302// Evaluate the rational form P(x)/Q(x) using coefficients
303// from the solution vector param
305 // Work backwards toward the constant term.
306 int c = n;
307 bigfloat yn = param[c--]; // Highest order numerator coefficient
308 for (int i = pow_n-1; i >= 0; i--) yn = x * yn + (num_pows[i] != -1 ? param[c--] : bigfloat(0l));
309
310 c = n+d;
311 bigfloat yd = 1l; //Highest degree coefficient is 1.0
312 for (int i = pow_d-1; i >= 0; i--) yd = x * yd + (den_pows[i] != -1 ? param[c--] : bigfloat(0l));
313
314 return(yn/yd);
315}
316
317// Compute size and sign of the approximation error at x
319 bigfloat f = func(x);
320 bigfloat e = approx(x) - f;
321 if (f != 0) e /= f;
322 if (e < (bigfloat)0.0) {
323 *sign = -1;
324 e = -e;
325 }
326 else *sign = 1;
327
328 return(e);
329}
330
331// Solve the system AX=B
333
334 int ip, ipj, ipk, ipn;
335 int idxpiv;
336 int kp, kp1, kpk, kpn;
337 int nip, nkp;
338 bigfloat em, q, rownrm, big, size, pivot, sum;
339 bigfloat *aa;
340 bigfloat *X = param.data();
341
342 int n = neq;
343 int nm1 = n - 1;
344 // Initialize IPS and X
345
346 int ij = 0;
347 for (int i = 0; i < n; i++) {
348 IPS[i] = i;
349 rownrm = 0.0;
350 for(int j = 0; j < n; j++) {
351 q = abs_bf(A[ij]);
352 if(rownrm < q) rownrm = q;
353 ++ij;
354 }
355 if (rownrm == (bigfloat)0l) {
356 std::cout<<"simq rownrm=0\n";
357 return(1);
358 }
359 X[i] = (bigfloat)1.0 / rownrm;
360 }
361
362 for (int k = 0; k < nm1; k++) {
363 big = 0.0;
364 idxpiv = 0;
365
366 for (int i = k; i < n; i++) {
367 ip = IPS[i];
368 ipk = n*ip + k;
369 size = abs_bf(A[ipk]) * X[ip];
370 if (size > big) {
371 big = size;
372 idxpiv = i;
373 }
374 }
375
376 if (big == (bigfloat)0l) {
377 std::cout<<"simq big=0\n";
378 return(2);
379 }
380 if (idxpiv != k) {
381 int j = IPS[k];
382 IPS[k] = IPS[idxpiv];
383 IPS[idxpiv] = j;
384 }
385 kp = IPS[k];
386 kpk = n*kp + k;
387 pivot = A[kpk];
388 kp1 = k+1;
389 for (int i = kp1; i < n; i++) {
390 ip = IPS[i];
391 ipk = n*ip + k;
392 em = -A[ipk] / pivot;
393 A[ipk] = -em;
394 nip = n*ip;
395 nkp = n*kp;
396 aa = A.data()+nkp+kp1;
397 for (int j = kp1; j < n; j++) {
398 ipj = nip + j;
399 A[ipj] = A[ipj] + em * *aa++;
400 }
401 }
402 }
403 kpn = n * IPS[n-1] + n - 1; // last element of IPS[n] th row
404 if (A[kpn] == (bigfloat)0l) {
405 std::cout<<"simq A[kpn]=0\n";
406 return(3);
407 }
408
409
410 ip = IPS[0];
411 X[0] = B[ip];
412 for (int i = 1; i < n; i++) {
413 ip = IPS[i];
414 ipj = n * ip;
415 sum = 0.0;
416 for (int j = 0; j < i; j++) {
417 sum += A[ipj] * X[j];
418 ++ipj;
419 }
420 X[i] = B[ip] - sum;
421 }
422
423 ipn = n * IPS[n-1] + n - 1;
424 X[n-1] = X[n-1] / A[ipn];
425
426 for (int iback = 1; iback < n; iback++) {
427 //i goes (n-1),...,1
428 int i = nm1 - iback;
429 ip = IPS[i];
430 nip = n*ip;
431 sum = 0.0;
432 aa = A.data()+nip+i+1;
433 for (int j= i + 1; j < n; j++)
434 sum += *aa++ * X[j];
435 X[i] = (X[i] - sum) / A[nip+i];
436 }
437
438 return(0);
439}
440
441void AlgRemezGeneral::csv(std::ostream & os) const{
442 os << "Numerator" << std::endl;
443 for(int i=0;i<=pow_n;i++){
444 os << getCoeffNum(i) << "*x^" << i;
445 if(i!=pow_n) os << " + ";
446 }
447 os << std::endl;
448
449 os << "Denominator" << std::endl;
450 for(int i=0;i<=pow_d;i++){
451 os << getCoeffDen(i) << "*x^" << i;
452 if(i!=pow_d) os << " + ";
453 }
454 os << std::endl;
455
456 //For a true minimax solution the errors should all be equal and the signs should oscillate +-+-+- etc
457 int sign;
458 os << "Errors at maxima: coordinate, error, (sign)" << std::endl;
459 for(int i=0;i<neq+1;i++){
460 os << mm[i] << " " << getErr(mm[i],&sign) << " (" << sign << ")" << std::endl;
461 }
462
463 os << "Scan over range:" << std::endl;
464 int npt = 60;
465 bigfloat dlt = (apend - apstrt)/bigfloat(npt-1);
466
467 for (bigfloat x=apstrt; x<=apend; x = x + dlt) {
468 double f = evaluateFunc(x);
469 double r = evaluateApprox(x);
470 os<< x<<","<<r<<","<<f<<","<<r-f<<std::endl;
471 }
472 return;
473}
accelerator_inline Grid_simd< S, V > cos(const Grid_simd< S, V > &r)
accelerator_inline Grid_simd< S, V > exp(const Grid_simd< S, V > &r)
vobj::scalar_object sum(const vobj *arg, Integer osites)
#define M_PI
Definition Zolotarev.cc:41
PolyType num_type
std::vector< bigfloat > xx
std::vector< bigfloat > A
double generateApprox(int num_degree, int den_degree, PolyType num_type, PolyType den_type, const double tolerance=1e-15, const int report_freq=1000)
void setupPolyProperties(int num_degree, int den_degree, PolyType num_type_in, PolyType den_type_in)
std::vector< int > num_pows
double evaluateApprox(double x) const
bigfloat approx(bigfloat x) const
bigfloat func(bigfloat x) const
void csv(std::ostream &os=std::cout) const
bigfloat(* f)(bigfloat x, void *data)
double getCoeffNum(const int i) const
bigfloat getErr(bigfloat x, int *sign) const
PolyType den_type
double evaluateFunc(double x) const
std::vector< bigfloat > param
void reinitializeAlgorithm()
double getCoeffDen(const int i) const
AlgRemezGeneral(double lower, double upper, long prec, bigfloat(*f)(bigfloat x, void *data), void *data)
std::vector< bigfloat > step
std::vector< bigfloat > B
std::vector< int > IPS
std::vector< int > den_pows
std::vector< bigfloat > mm
std::vector< bigfloat > yy
static void setDefaultPrecision(unsigned long dprec)
Definition bigfloat.h:37