| @@ -19,23 +19,19 @@ | |||||
| using namespace lol; | using namespace lol; | ||||
| /* The order of the approximation we're looking for */ | /* The order of the approximation we're looking for */ | ||||
| static int const ORDER = 3; | |||||
| static int const ORDER = 4; | |||||
| /* The function we want to approximate */ | /* The function we want to approximate */ | ||||
| double myfun(double x) | |||||
| { | |||||
| return exp(x); | |||||
| } | |||||
| real myfun(real const &x) | real myfun(real const &x) | ||||
| { | { | ||||
| return exp(x); | |||||
| static real const one = 1.0; | |||||
| return cos(x) - one; | |||||
| //return exp(x); | |||||
| } | } | ||||
| /* Naive matrix inversion */ | /* Naive matrix inversion */ | ||||
| template<int N> class Matrix | |||||
| template<int N> struct Matrix | |||||
| { | { | ||||
| public: | |||||
| inline Matrix() {} | inline Matrix() {} | ||||
| Matrix(real x) | Matrix(real x) | ||||
| @@ -53,7 +49,7 @@ public: | |||||
| Matrix a = *this, b(real(1.0)); | Matrix a = *this, b(real(1.0)); | ||||
| /* Inversion method: iterate through all columns and make sure | /* Inversion method: iterate through all columns and make sure | ||||
| * all the terms are zero except on the diagonal where it is one */ | |||||
| * all the terms are 1 on the diagonal and 0 everywhere else */ | |||||
| for (int i = 0; i < N; i++) | for (int i = 0; i < N; i++) | ||||
| { | { | ||||
| /* If the expected coefficient is zero, add one of | /* If the expected coefficient is zero, add one of | ||||
| @@ -117,7 +113,7 @@ public: | |||||
| static int cheby[ORDER + 1][ORDER + 1]; | static int cheby[ORDER + 1][ORDER + 1]; | ||||
| /* Fill the Chebyshev tables */ | /* Fill the Chebyshev tables */ | ||||
| static void make_table() | |||||
| static void cheby_init() | |||||
| { | { | ||||
| memset(cheby, 0, sizeof(cheby)); | memset(cheby, 0, sizeof(cheby)); | ||||
| @@ -132,54 +128,249 @@ static void make_table() | |||||
| } | } | ||||
| } | } | ||||
| int main(int argc, char **argv) | |||||
| static void cheby_coeff(real *coeff, real *bn) | |||||
| { | |||||
| for (int i = 0; i < ORDER + 1; i++) | |||||
| { | |||||
| bn[i] = 0; | |||||
| for (int j = 0; j < ORDER + 1; j++) | |||||
| if (cheby[j][i]) | |||||
| bn[i] += coeff[j] * (real)cheby[j][i]; | |||||
| } | |||||
| } | |||||
| static real cheby_eval(real *coeff, real const &x) | |||||
| { | { | ||||
| make_table(); | |||||
| real ret = 0.0, xn = 1.0; | |||||
| /* We start with ORDER+1 points and their images through myfun() */ | |||||
| real xn[ORDER + 1]; | |||||
| for (int i = 0; i < ORDER + 1; i++) | |||||
| { | |||||
| real mul = 0; | |||||
| for (int j = 0; j < ORDER + 1; j++) | |||||
| if (cheby[j][i]) | |||||
| mul += coeff[j] * (real)cheby[j][i]; | |||||
| ret += mul * xn; | |||||
| xn *= x; | |||||
| } | |||||
| return ret; | |||||
| } | |||||
| static void remez_init(real *coeff, real *zeroes) | |||||
| { | |||||
| /* Pick up x_i where error will be 0 and compute f(x_i) */ | |||||
| real fxn[ORDER + 1]; | real fxn[ORDER + 1]; | ||||
| for (int i = 0; i < ORDER + 1; i++) | for (int i = 0; i < ORDER + 1; i++) | ||||
| { | { | ||||
| //xn[i] = real(2 * i - ORDER) / real(ORDER + 1); | |||||
| xn[i] = real(2 * i - ORDER + 1) / real(ORDER - 1); | |||||
| fxn[i] = myfun(xn[i]); | |||||
| zeroes[i] = real(2 * i - ORDER) / real(ORDER + 1); | |||||
| fxn[i] = myfun(zeroes[i]); | |||||
| } | } | ||||
| /* We build a matrix of Chebishev evaluations: one row per point | |||||
| * in our array, and column i is the evaluation of the ith order | |||||
| * polynomial. */ | |||||
| /* We build a matrix of Chebishev evaluations: row i contains the | |||||
| * evaluations of x_i for polynomial order n = 0, 1, ... */ | |||||
| Matrix<ORDER + 1> mat; | Matrix<ORDER + 1> mat; | ||||
| for (int j = 0; j < ORDER + 1; j++) | |||||
| for (int i = 0; i < ORDER + 1; i++) | |||||
| { | { | ||||
| /* Compute the powers of x_j */ | |||||
| /* Compute the powers of x_i */ | |||||
| real powers[ORDER + 1]; | real powers[ORDER + 1]; | ||||
| powers[0] = 1.0; | powers[0] = 1.0; | ||||
| for (int i = 1; i < ORDER + 1; i++) | |||||
| powers[i] = powers[i - 1] * xn[j]; | |||||
| for (int n = 1; n < ORDER + 1; n++) | |||||
| powers[n] = powers[n - 1] * zeroes[i]; | |||||
| /* Compute the Chebishev evaluations at x_j */ | |||||
| for (int i = 0; i < ORDER + 1; i++) | |||||
| /* Compute the Chebishev evaluations at x_i */ | |||||
| for (int n = 0; n < ORDER + 1; n++) | |||||
| { | { | ||||
| real sum = 0.0; | real sum = 0.0; | ||||
| for (int k = 0; k < ORDER + 1; k++) | for (int k = 0; k < ORDER + 1; k++) | ||||
| if (cheby[i][k]) | |||||
| sum += real(cheby[i][k]) * powers[k]; | |||||
| mat.m[j][i] = sum; | |||||
| if (cheby[n][k]) | |||||
| sum += real(cheby[n][k]) * powers[k]; | |||||
| mat.m[i][n] = sum; | |||||
| } | } | ||||
| } | } | ||||
| /* Invert the matrix and build interpolation coefficients */ | |||||
| /* Solve the system */ | |||||
| mat = mat.inv(); | mat = mat.inv(); | ||||
| real an[ORDER + 1]; | |||||
| /* Compute interpolation coefficients */ | |||||
| for (int j = 0; j < ORDER + 1; j++) | for (int j = 0; j < ORDER + 1; j++) | ||||
| { | { | ||||
| an[j] = 0; | |||||
| coeff[j] = 0; | |||||
| for (int i = 0; i < ORDER + 1; i++) | for (int i = 0; i < ORDER + 1; i++) | ||||
| an[j] += mat.m[j][i] * fxn[i]; | |||||
| an[j].print(10); | |||||
| coeff[j] += mat.m[j][i] * fxn[i]; | |||||
| } | |||||
| } | |||||
| static void remez_findzeroes(real *coeff, real *zeroes, real *control) | |||||
| { | |||||
| /* FIXME: this is fake for now */ | |||||
| for (int i = 0; i < ORDER + 1; i++) | |||||
| { | |||||
| real a = control[i]; | |||||
| real ea = cheby_eval(coeff, a) - myfun(a); | |||||
| real b = control[i + 1]; | |||||
| real eb = cheby_eval(coeff, b) - myfun(b); | |||||
| while (fabs(a - b) > (real)1e-140) | |||||
| { | |||||
| real c = (a + b) * (real)0.5; | |||||
| real ec = cheby_eval(coeff, c) - myfun(c); | |||||
| if ((ea < (real)0 && ec < (real)0) | |||||
| || (ea > (real)0 && ec > (real)0)) | |||||
| { | |||||
| a = c; | |||||
| ea = ec; | |||||
| } | |||||
| else | |||||
| { | |||||
| b = c; | |||||
| eb = ec; | |||||
| } | |||||
| } | |||||
| zeroes[i] = a; | |||||
| } | |||||
| } | |||||
| static void remez_finderror(real *coeff, real *zeroes, real *control) | |||||
| { | |||||
| real final = 0; | |||||
| for (int i = 0; i < ORDER + 2; i++) | |||||
| { | |||||
| real a = -1, b = 1; | |||||
| if (i > 0) | |||||
| a = zeroes[i - 1]; | |||||
| if (i < ORDER + 1) | |||||
| b = zeroes[i]; | |||||
| printf("Error for [%g..%g]: ", (double)a, (double)b); | |||||
| for (;;) | |||||
| { | |||||
| real c = a, delta = (b - a) / (real)10.0; | |||||
| real maxerror = 0; | |||||
| int best = -1; | |||||
| for (int k = 0; k <= 10; k++) | |||||
| { | |||||
| real e = fabs(cheby_eval(coeff, c) - myfun(c)); | |||||
| if (e > maxerror) | |||||
| { | |||||
| maxerror = e; | |||||
| best = k; | |||||
| } | |||||
| c += delta; | |||||
| } | |||||
| if (best == 0) | |||||
| best = 1; | |||||
| if (best == 10) | |||||
| best = 9; | |||||
| b = a + (real)(best + 1) * delta; | |||||
| a = a + (real)(best - 1) * delta; | |||||
| if (b - a < (real)1e-15) | |||||
| { | |||||
| if (maxerror > final) | |||||
| final = maxerror; | |||||
| control[i] = (a + b) * (real)0.5; | |||||
| printf("%g (in %g)\n", (double)maxerror, (double)control[i]); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| printf("Final error: %g\n", (double)final); | |||||
| } | |||||
| static void remez_step(real *coeff, real *control) | |||||
| { | |||||
| /* Pick up x_i where error will be 0 and compute f(x_i) */ | |||||
| real fxn[ORDER + 2]; | |||||
| for (int i = 0; i < ORDER + 2; i++) | |||||
| fxn[i] = myfun(control[i]); | |||||
| /* We build a matrix of Chebishev evaluations: row i contains the | |||||
| * evaluations of x_i for polynomial order n = 0, 1, ... */ | |||||
| Matrix<ORDER + 2> mat; | |||||
| for (int i = 0; i < ORDER + 2; i++) | |||||
| { | |||||
| /* Compute the powers of x_i */ | |||||
| real powers[ORDER + 1]; | |||||
| powers[0] = 1.0; | |||||
| for (int n = 1; n < ORDER + 1; n++) | |||||
| powers[n] = powers[n - 1] * control[i]; | |||||
| /* Compute the Chebishev evaluations at x_i */ | |||||
| for (int n = 0; n < ORDER + 1; n++) | |||||
| { | |||||
| real sum = 0.0; | |||||
| for (int k = 0; k < ORDER + 1; k++) | |||||
| if (cheby[n][k]) | |||||
| sum += real(cheby[n][k]) * powers[k]; | |||||
| mat.m[i][n] = sum; | |||||
| } | |||||
| mat.m[i][ORDER + 1] = (real)(-1 + (i & 1) * 2); | |||||
| } | |||||
| /* Solve the system */ | |||||
| mat = mat.inv(); | |||||
| /* Compute interpolation coefficients */ | |||||
| for (int j = 0; j < ORDER + 1; j++) | |||||
| { | |||||
| coeff[j] = 0; | |||||
| for (int i = 0; i < ORDER + 2; i++) | |||||
| coeff[j] += mat.m[j][i] * fxn[i]; | |||||
| } | } | ||||
| /* Compute the error */ | |||||
| real error = 0; | |||||
| for (int i = 0; i < ORDER + 2; i++) | |||||
| error += mat.m[ORDER + 1][i] * fxn[i]; | |||||
| } | |||||
| int main(int argc, char **argv) | |||||
| { | |||||
| cheby_init(); | |||||
| /* ORDER + 1 chebyshev coefficients and 1 error value */ | |||||
| real coeff[ORDER + 2]; | |||||
| /* ORDER + 1 zeroes of the error function */ | |||||
| real zeroes[ORDER + 1]; | |||||
| /* ORDER + 2 control points */ | |||||
| real control[ORDER + 2]; | |||||
| real bn[ORDER + 1]; | |||||
| remez_init(coeff, zeroes); | |||||
| cheby_coeff(coeff, bn); | |||||
| for (int j = 0; j < ORDER + 1; j++) | |||||
| printf("%s%12.10gx^%i", j ? "+" : "", (double)bn[j], j); | |||||
| printf("\n"); | |||||
| for (int n = 0; n < 200; n++) | |||||
| { | |||||
| remez_finderror(coeff, zeroes, control); | |||||
| remez_step(coeff, control); | |||||
| cheby_coeff(coeff, bn); | |||||
| for (int j = 0; j < ORDER + 1; j++) | |||||
| printf("%s%12.10gx^%i", j ? "+" : "", (double)bn[j], j); | |||||
| printf("\n"); | |||||
| remez_findzeroes(coeff, zeroes, control); | |||||
| } | |||||
| remez_finderror(coeff, zeroes, control); | |||||
| remez_step(coeff, control); | |||||
| cheby_coeff(coeff, bn); | |||||
| for (int j = 0; j < ORDER + 1; j++) | |||||
| printf("%s%12.10gx^%i", j ? "+" : "", (double)bn[j], j); | |||||
| printf("\n"); | |||||
| return EXIT_SUCCESS; | return EXIT_SUCCESS; | ||||
| } | } | ||||