1 /* 2 Copyright 2008-2024 3 Matthias Ehmann, 4 Michael Gerhaeuser, 5 Carsten Miller, 6 Alfred Wassermann 7 8 This file is part of JSXGraph. 9 10 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 11 12 You can redistribute it and/or modify it under the terms of the 13 14 * GNU Lesser General Public License as published by 15 the Free Software Foundation, either version 3 of the License, or 16 (at your option) any later version 17 OR 18 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 19 20 JSXGraph is distributed in the hope that it will be useful, 21 but WITHOUT ANY WARRANTY; without even the implied warranty of 22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 GNU Lesser General Public License for more details. 24 25 You should have received a copy of the GNU Lesser General Public License and 26 the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/> 27 and <https://opensource.org/licenses/MIT/>. 28 */ 29 30 /*global JXG: true, define: true*/ 31 /*jslint nomen: true, plusplus: true*/ 32 33 import Mat from "./math.js"; 34 35 /** 36 * Functions for extrapolation of sequences. Used for finding limits of sequences which is used for curve plotting. 37 * @name JXG.Math.Extrapolate 38 * @exports Mat.Extrapolate as JXG.Math.Extrapolate 39 * @namespace 40 */ 41 Mat.Extrapolate = { 42 upper: 15, 43 infty: 1e4, 44 45 /** 46 * Wynn's epsilon algorithm. Ported from the FORTRAN version in 47 * Ernst Joachim Weniger, "Nonlinear sequence transformations for the acceleration of convergence 48 * and the summation of divergent series", Computer Physics Reports Vol. 10, 189-371 (1989). 49 * 50 * @param {Number} s_n next value of sequence, i.e. n-th element of sequence 51 * @param {Number} n index of s_n in the sequence 52 * @param {Array} e One-dimensional array containing the extrapolation data. Has to be supplied by the calling routine. 53 * @returns {Number} New estimate of the limit of the sequence. 54 * 55 * @memberof JXG.Math.Extrapolate 56 */ 57 wynnEps: function (s_n, n, e) { 58 var HUGE = 1e20, 59 TINY = 1e-15, 60 f0 = 1, // f0 may be changed to other values, see vanden Broeck, Schwartz (1979) 61 f, 62 j, 63 aux1, 64 aux2, 65 diff, 66 estlim; 67 68 e[n] = s_n; 69 if (n === 0) { 70 estlim = s_n; 71 } else { 72 aux2 = 0.0; 73 for (j = n; j > 0; j--) { 74 aux1 = aux2; 75 aux2 = e[j - 1]; 76 diff = e[j] - aux2; 77 if (Math.abs(diff) <= TINY) { 78 e[j - 1] = HUGE; 79 } else { 80 f = (n - j + 1) % 2 === 1 ? f0 : 1; 81 e[j - 1] = aux1 * f + 1 / diff; 82 } 83 } 84 estlim = e[n % 2]; 85 } 86 87 return estlim; 88 }, 89 90 // wynnRho: function(s_n, n, e) { 91 // var HUGE = 1.e+20, 92 // TINY = 1.e-15, 93 // j, f, 94 // aux1, aux2, diff, estlim; 95 96 // e[n] = s_n; 97 // if (n === 0) { 98 // estlim = s_n; 99 // } else { 100 // aux2 = 0.0; 101 // for (j = n; j >= 1; j--) { 102 // aux1 = aux2; 103 // aux2 = e[j - 1]; 104 // diff = e[j] - aux2; 105 // if (Math.abs(diff) <= TINY) { 106 // e[j - 1] = HUGE; 107 // } else { 108 // f = ((n - j + 1) % 2 === 1) ? n - j + 1 : 1; 109 // e[j - 1] = aux1 + f / diff; 110 // } 111 // } 112 // estlim = e[n % 2]; 113 // } 114 115 // return estlim; 116 // }, 117 118 /** 119 * Aitken transformation. Ported from the FORTRAN version in 120 * Ernst Joachim Weniger, "Nonlinear sequence transformations for the acceleration of convergence 121 * and the summation of divergent series", Computer Physics Reports Vol. 10, 189-371 (1989). 122 * 123 * @param {Number} s_n next value of sequence, i.e. n-th element of sequence 124 * @param {Number} n index of s_n in the sequence 125 * @param {Array} a One-dimensional array containing the extrapolation data. Has to be supplied by the calling routine. 126 * @returns {Number} New estimate of the limit of the sequence. 127 * 128 * @memberof JXG.Math.Extrapolate 129 */ 130 aitken: function (s_n, n, a) { 131 var estlim, 132 HUGE = 1e20, 133 TINY = 1e-15, 134 denom, 135 v, 136 lowmax, 137 j, 138 m; 139 140 a[n] = s_n; 141 if (n < 2) { 142 estlim = s_n; 143 } else { 144 lowmax = n / 2; 145 for (j = 1; j <= lowmax; j++) { 146 m = n - 2 * j; 147 denom = a[m + 2] - 2 * a[m + 1] + a[m]; 148 if (Math.abs(denom) < TINY) { 149 a[m] = HUGE; 150 } else { 151 v = a[m] - a[m + 1]; 152 a[m] -= (v * v) / denom; 153 } 154 } 155 estlim = a[n % 2]; 156 } 157 return estlim; 158 }, 159 160 /** 161 * Iterated Brezinski transformation. Ported from the FORTRAN version in 162 * Ernst Joachim Weniger, "Nonlinear sequence transformations for the acceleration of convergence 163 * and the summation of divergent series", Computer Physics Reports Vol. 10, 189-371 (1989). 164 * 165 * @param {Number} s_n next value of sequence, i.e. n-th element of sequence 166 * @param {Number} n index of s_n in the sequence 167 * @param {Array} a One-dimensional array containing the extrapolation data. Has to be supplied by the calling routine. 168 * @returns {Number} New estimate of the limit of the sequence. 169 * 170 * @memberof JXG.Math.Extrapolate 171 */ 172 brezinski: function (s_n, n, a) { 173 var estlim, 174 HUGE = 1e20, 175 TINY = 1e-15, 176 denom, 177 d0, 178 d1, 179 d2, 180 lowmax, 181 j, 182 m; 183 184 a[n] = s_n; 185 if (n < 3) { 186 estlim = s_n; 187 } else { 188 lowmax = n / 3; 189 m = n; 190 for (j = 1; j <= lowmax; j++) { 191 m -= 3; 192 d0 = a[m + 1] - a[m]; 193 d1 = a[m + 2] - a[m + 1]; 194 d2 = a[m + 3] - a[m + 2]; 195 denom = d2 * (d1 - d0) - d0 * (d2 - d1); 196 if (Math.abs(denom) < TINY) { 197 a[m] = HUGE; 198 } else { 199 a[m] = a[m + 1] - (d0 * d1 * (d2 - d1)) / denom; 200 } 201 } 202 estlim = a[n % 3]; 203 } 204 return estlim; 205 }, 206 207 /** 208 * Extrapolated iteration to approximate the value f(x_0). 209 * 210 * @param {Number} x0 Value for which the limit of f is to be determined. f(x0) may or may not exist. 211 * @param {Number} h0 Initial (signed) distance from x0. 212 * @param {Function} f Function for which the limit at x0 is to be determined 213 * @param {String} method String to choose the method. Available values: "wynnEps", "aitken", "brezinski" 214 * @param {Number} step_type Approximation method. step_type = 0 uses the sequence x0 + h0/n; step_type = 1 uses the sequence x0 + h0 * 2^(-n) 215 * 216 * @returns {Array} Array of length 3. Position 0: estimated value for f(x0), position 1: 'finite', 'infinite', or 'NaN'. 217 * Position 2: value between 0 and 1 judging the reliability of the result (1: high, 0: not successful). 218 * 219 * @memberof JXG.Math.Extrapolate 220 * @see JXG.Math.Extrapolate.limit 221 * @see JXG.Math.Extrapolate.wynnEps 222 * @see JXG.Math.Extrapolate.aitken 223 * @see JXG.Math.Extrapolate.brezinski 224 */ 225 iteration: function (x0, h0, f, method, step_type) { 226 var n, 227 v, 228 w, 229 estlim = NaN, 230 diff, 231 r = 0.5, 232 E = [], 233 result = "finite", 234 h = h0; 235 236 step_type = step_type || 0; 237 238 for (n = 1; n <= this.upper; n++) { 239 h = step_type === 0 ? h0 / (n + 1) : h * r; 240 v = f(x0 + h, true); 241 242 w = this[method](v, n - 1, E); 243 //console.log(n, x0 + h, v, w); 244 if (isNaN(w)) { 245 result = "NaN"; 246 break; 247 } 248 if (v !== 0 && w / v > this.infty) { 249 estlim = w; 250 result = "infinite"; 251 break; 252 } 253 diff = w - estlim; 254 if (Math.abs(diff) < 1e-7) { 255 break; 256 } 257 estlim = w; 258 } 259 return [estlim, result, 1 - (n - 1) / this.upper]; 260 }, 261 262 /** 263 * Levin transformation. See Numerical Recipes, ed. 3. 264 * Not yet ready for use. 265 * 266 * @param {Number} s_n next value of sequence, i.e. n-th element of sequence 267 * @param {Number} n index of s_n in the sequence 268 * @param {Array} numer One-dimensional array containing the extrapolation data for the numerator. Has to be supplied by the calling routine. 269 * @param {Array} denom One-dimensional array containing the extrapolation data for the denominator. Has to be supplied by the calling routine. 270 * 271 * @memberof JXG.Math.Extrapolate 272 */ 273 levin: function (s_n, n, omega, beta, numer, denom) { 274 var HUGE = 1e20, 275 TINY = 1e-15, 276 j, 277 fact, 278 ratio, 279 term, 280 estlim; 281 282 term = 1.0 / (beta + n); 283 numer[n] = s_n / omega; 284 denom[n] = 1 / omega; 285 if (n > 0) { 286 numer[n - 1] = numer[n] - numer[n - 1]; 287 denom[n - 1] = denom[n] - denom[n - 1]; 288 if (n > 1) { 289 ratio = (beta + n - 1) * term; 290 for (j = 2; j <= n; j++) { 291 fact = (beta + n - j) * Math.pow(ratio, j - 2) * term; 292 numer[n - j] = numer[n - j + 1] - fact * numer[n - j]; 293 denom[n - j] = denom[n - j + 1] - fact * denom[n - j]; 294 term *= ratio; 295 } 296 } 297 } 298 if (Math.abs(denom[0]) < TINY) { 299 estlim = HUGE; 300 } else { 301 estlim = numer[0] / denom[0]; 302 } 303 return estlim; 304 }, 305 306 iteration_levin: function (x0, h0, f, step_type) { 307 var n, 308 v, 309 w, 310 estlim = NaN, 311 v_prev, 312 delta, 313 diff, 314 omega, 315 beta = 1, 316 r = 0.5, 317 numer = [], 318 denom = [], 319 result = "finite", 320 h = h0, 321 transform = "u"; 322 323 step_type = step_type || 0; 324 325 v_prev = f(x0 + h0, true); 326 for (n = 1; n <= this.upper; n++) { 327 h = step_type === 0 ? h0 / (n + 1) : h * r; 328 v = f(x0 + h, true); 329 delta = v - v_prev; 330 if (Math.abs(delta) < 1) { 331 transform = "u"; 332 } else { 333 transform = "t"; 334 } 335 if (transform === "u") { 336 omega = (beta + n) * delta; // u transformation 337 } else { 338 omega = delta; // t transformation 339 } 340 341 v_prev = v; 342 w = this.levin(v, n - 1, omega, beta, numer, denom); 343 diff = w - estlim; 344 // console.log(n, delta, transform, x0 + h, v, w, diff); 345 346 if (isNaN(w)) { 347 result = "NaN"; 348 break; 349 } 350 if (v !== 0 && w / v > this.infty) { 351 estlim = w; 352 result = "infinite"; 353 break; 354 } 355 if (Math.abs(diff) < 1e-7) { 356 break; 357 } 358 estlim = w; 359 } 360 return [estlim, result, 1 - (n - 1) / this.upper]; 361 }, 362 363 /** 364 * 365 * @param {Number} x0 Value for which the limit of f is to be determined. f(x0) may or may not exist. 366 * @param {Number} h0 Initial (signed) distance from x0. 367 * @param {Function} f Function for which the limit at x0 is to be determined 368 * 369 * @returns {Array} Array of length 3. Position 0: estimated value for f(x0), position 1: 'finite', 'infinite', or 'NaN'. 370 * Position 2: value between 0 and 1 judging the reliability of the result (1: high, 0: not successful). 371 * In case that the extrapolation fails, position 1 and 2 contain 'direct' and 0. 372 * 373 * @example 374 * var f1 = (x) => Math.log(x), 375 * f2 = (x) => Math.tan(x - Math.PI * 0.5), 376 * f3 = (x) => 4 / x; 377 * 378 * var x0 = 0.0000001; 379 * var h = 0.1; 380 * for (let f of [f1, f2, f3]) { 381 * console.log("x0=", x0, f.toString()); 382 * console.log(JXG.Math.Extrapolate.limit(x0, h, f)); 383 * } 384 * 385 * </pre><div id="JXG5e8c6a7e-eeae-43fb-a669-26b5c9e40cab" class="jxgbox" style="width: 300px; height: 300px;"></div> 386 * <script type="text/javascript"> 387 * (function() { 388 * var board = JXG.JSXGraph.initBoard('JXG5e8c6a7e-eeae-43fb-a669-26b5c9e40cab', 389 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 390 * var f1 = (x) => Math.log(x), 391 * f2 = (x) => Math.tan(x - Math.PI * 0.5), 392 * f3 = (x) => 4 / x; 393 * 394 * var x0 = 0.0000001; 395 * var h = 0.1; 396 * for (let f of [f1, f2, f3]) { 397 * console.log("x0=", x0, f.toString()); 398 * console.log(JXG.Math.Extrapolate.limit(x0, h, f)); 399 * } 400 * 401 * })(); 402 * 403 * </script><pre> 404 * 405 * 406 * @see JXG.Math.Extrapolate.iteration 407 * @memberof JXG.Math.Extrapolate 408 */ 409 limit: function (x0, h0, f) { 410 return this.iteration_levin(x0, h0, f, 0); 411 //return this.iteration(x0, h0, f, 'wynnEps', 1); 412 413 // var algs = ['wynnEps', 'levin'], //, 'wynnEps', 'levin', 'aitken', 'brezinski'], 414 // le = algs.length, 415 // i, t, res; 416 // for (i = 0; i < le; i++) { 417 // for (t = 0; t < 1; t++) { 418 // if (algs[i] === 'levin') { 419 // res = this.iteration_levin(x0, h0, f, t); 420 // } else { 421 // res = this.iteration(x0, h0, f, algs[i], t); 422 // } 423 // if (res[2] > 0.6) { 424 // return res; 425 // } 426 // console.log(algs[i], t, res) 427 // } 428 // } 429 // return [f(x0 + Math.sign(h0) * Math.sqrt(Mat.eps)), 'direct', 0]; 430 } 431 }; 432 433 export default Mat.Extrapolate; 434