source: trunk/test/math/remez-solver.h @ 1097

Last change on this file since 1097 was 1097, checked in by sam, 11 years ago

ps3: start implementing the PS3 threading system, and port the new
Mandelbrot shader code to Cg.

  • Property svn:keywords set to Id
File size: 8.9 KB
Line 
1//
2// Lol Engine - Sample math program: Chebyshev polynomials
3//
4// Copyright: (c) 2005-2011 Sam Hocevar <sam@hocevar.net>
5//   This program is free software; you can redistribute it and/or
6//   modify it under the terms of the Do What The Fuck You Want To
7//   Public License, Version 2, as published by Sam Hocevar. See
8//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
9//
10
11#if !defined __REMEZ_SOLVER_H__
12#define __REMEZ_SOLVER_H__
13
14template<int ORDER> class RemezSolver
15{
16public:
17    typedef real RealFunc(real const &x);
18
19    RemezSolver()
20    {
21    }
22
23    void Run(real a, real b, RealFunc *func, RealFunc *weight, int steps)
24    {
25        m_func = func;
26        m_weight = weight;
27        m_k1 = (b + a) >> 1;
28        m_k2 = (b - a) >> 1;
29        m_invk2 = re(m_k2);
30        m_invk1 = -m_k1 * m_invk2;
31
32        Init();
33
34        PrintPoly();
35
36        for (int n = 0; n < steps; n++)
37        {
38            FindExtrema();
39            Step();
40
41            PrintPoly();
42
43            FindZeroes();
44        }
45
46        FindExtrema();
47        Step();
48
49        PrintPoly();
50    }
51
52    real ChebyEval(real const &x)
53    {
54        real ret = 0.0, xn = 1.0;
55
56        for (int i = 0; i < ORDER + 1; i++)
57        {
58            real mul = 0;
59            for (int j = 0; j < ORDER + 1; j++)
60                mul += coeff[j] * (real)Cheby(j, i);
61            ret += mul * xn;
62            xn *= x;
63        }
64
65        return ret;
66    }
67
68    void Init()
69    {
70        /* Pick up x_i where error will be 0 and compute f(x_i) */
71        real fxn[ORDER + 1];
72        for (int i = 0; i < ORDER + 1; i++)
73        {
74            zeroes[i] = (real)(2 * i - ORDER) / (real)(ORDER + 1);
75            fxn[i] = Value(zeroes[i]);
76        }
77
78        /* We build a matrix of Chebishev evaluations: row i contains the
79         * evaluations of x_i for polynomial order n = 0, 1, ... */
80        Matrix<ORDER + 1> mat;
81        for (int i = 0; i < ORDER + 1; i++)
82        {
83            /* Compute the powers of x_i */
84            real powers[ORDER + 1];
85            powers[0] = 1.0;
86            for (int n = 1; n < ORDER + 1; n++)
87                 powers[n] = powers[n - 1] * zeroes[i];
88
89            /* Compute the Chebishev evaluations at x_i */
90            for (int n = 0; n < ORDER + 1; n++)
91            {
92                real sum = 0.0;
93                for (int k = 0; k < ORDER + 1; k++)
94                    sum += (real)Cheby(n, k) * powers[k];
95                mat.m[i][n] = sum;
96            }
97        }
98
99        /* Solve the system */
100        mat = mat.inv();
101
102        /* Compute interpolation coefficients */
103        for (int j = 0; j < ORDER + 1; j++)
104        {
105            coeff[j] = 0;
106            for (int i = 0; i < ORDER + 1; i++)
107                coeff[j] += mat.m[j][i] * fxn[i];
108        }
109    }
110
111    void FindZeroes()
112    {
113        /* Find ORDER + 1 zeroes of the error function. No need to
114         * compute the relative error: its zeroes are at the same
115         * place as the absolute error! */
116        for (int i = 0; i < ORDER + 1; i++)
117        {
118            struct { real value, error; } left, right, mid;
119
120            left.value = control[i];
121            left.error = ChebyEval(left.value) - Value(left.value);
122            right.value = control[i + 1];
123            right.error = ChebyEval(right.value) - Value(right.value);
124
125            static real limit = real::R_1 >> 500;
126            while (fabs(left.value - right.value) > limit)
127            {
128                mid.value = (left.value + right.value) >> 1;
129                mid.error = ChebyEval(mid.value) - Value(mid.value);
130
131                if ((left.error < real::R_0 && mid.error < real::R_0)
132                     || (left.error > real::R_0 && mid.error > real::R_0))
133                    left = mid;
134                else
135                    right = mid;
136            }
137
138            zeroes[i] = mid.value;
139        }
140    }
141
142    void FindExtrema()
143    {
144        using std::printf;
145
146        /* Find ORDER + 2 extrema of the error function. We need to
147         * compute the relative error, since its extrema are at slightly
148         * different locations than the absolute error’s. */
149        real final = 0;
150
151        for (int i = 0; i < ORDER + 2; i++)
152        {
153            real a = -1, b = 1;
154            if (i > 0)
155                a = zeroes[i - 1];
156            if (i < ORDER + 1)
157                b = zeroes[i];
158
159            for (;;)
160            {
161                real c = a, delta = (b - a) >> 3;
162                real maxerror = 0;
163                real maxweight = 0;
164                int best = -1;
165                for (int k = 1; k <= 7; k++)
166                {
167                    real error = ChebyEval(c) - Value(c);
168                    real weight = Weight(c);
169                    if (fabs(error * maxweight) >= fabs(maxerror * weight))
170                    {
171                        maxerror = error;
172                        maxweight = weight;
173                        best = k;
174                    }
175                    c += delta;
176                }
177
178                b = a + (real)(best + 1) * delta;
179                a = a + (real)(best - 1) * delta;
180
181                if (b - a < (real)1e-18)
182                {
183                    real e = maxerror / maxweight;
184                    if (e > final)
185                        final = e;
186                    control[i] = (a + b) >> 1;
187                    break;
188                }
189            }
190        }
191
192        printf("Final error: ");
193        final.print(40);
194    }
195
196    void Step()
197    {
198        /* Pick up x_i where error will be 0 and compute f(x_i) */
199        real fxn[ORDER + 2];
200        for (int i = 0; i < ORDER + 2; i++)
201            fxn[i] = Value(control[i]);
202
203        /* We build a matrix of Chebishev evaluations: row i contains the
204         * evaluations of x_i for polynomial order n = 0, 1, ... */
205        Matrix<ORDER + 2> mat;
206        for (int i = 0; i < ORDER + 2; i++)
207        {
208            /* Compute the powers of x_i */
209            real powers[ORDER + 1];
210            powers[0] = 1.0;
211            for (int n = 1; n < ORDER + 1; n++)
212                 powers[n] = powers[n - 1] * control[i];
213
214            /* Compute the Chebishev evaluations at x_i */
215            for (int n = 0; n < ORDER + 1; n++)
216            {
217                real sum = 0.0;
218                for (int k = 0; k < ORDER + 1; k++)
219                    sum += (real)Cheby(n, k) * powers[k];
220                mat.m[i][n] = sum;
221            }
222            if (i & 1)
223                mat.m[i][ORDER + 1] = fabs(Weight(control[i]));
224            else
225                mat.m[i][ORDER + 1] = -fabs(Weight(control[i]));
226        }
227
228        /* Solve the system */
229        mat = mat.inv();
230
231        /* Compute interpolation coefficients */
232        for (int j = 0; j < ORDER + 1; j++)
233        {
234            coeff[j] = 0;
235            for (int i = 0; i < ORDER + 2; i++)
236                coeff[j] += mat.m[j][i] * fxn[i];
237        }
238
239        /* Compute the error */
240        real error = 0;
241        for (int i = 0; i < ORDER + 2; i++)
242            error += mat.m[ORDER + 1][i] * fxn[i];
243    }
244
245    int Cheby(int n, int k)
246    {
247        if (k > n || k < 0)
248            return 0;
249        if (n <= 1)
250            return (n ^ k ^ 1) & 1;
251        return 2 * Cheby(n - 1, k - 1) - Cheby(n - 2, k);
252    }
253
254    int Comb(int n, int k)
255    {
256        if (k == 0 || k == n)
257            return 1;
258        return Comb(n - 1, k - 1) + Comb(n - 1, k);
259    }
260
261    void PrintPoly()
262    {
263        using std::printf;
264
265        /* Transform Chebyshev polynomial weights into powers of X^i
266         * in the [-1..1] range. */
267        real bn[ORDER + 1];
268
269        for (int i = 0; i < ORDER + 1; i++)
270        {
271            bn[i] = 0;
272            for (int j = 0; j < ORDER + 1; j++)
273                bn[i] += coeff[j] * (real)Cheby(j, i);
274        }
275
276        /* Transform a polynomial in the [-1..1] range into a polynomial
277         * in the [a..b] range. */
278        real k1p[ORDER + 1], k2p[ORDER + 1];
279        real an[ORDER + 1];
280
281        for (int i = 0; i < ORDER + 1; i++)
282        {
283            k1p[i] = i ? k1p[i - 1] * m_invk1 : real::R_1;
284            k2p[i] = i ? k2p[i - 1] * m_invk2 : real::R_1;
285        }
286
287        for (int i = 0; i < ORDER + 1; i++)
288        {
289            an[i] = 0;
290            for (int j = i; j < ORDER + 1; j++)
291                an[i] += (real)Comb(j, i) * k1p[j - i] * bn[j];
292            an[i] *= k2p[i];
293        }
294
295        printf("Polynomial estimate:\n");
296        for (int j = 0; j < ORDER + 1; j++)
297        {
298            if (j)
299                printf("+");
300            printf("x^%i*", j);
301            an[j].print(40);
302        }
303        printf("\n");
304    }
305
306    real Value(real const &x)
307    {
308        return m_func(x * m_k2 + m_k1);
309    }
310
311    real Weight(real const &x)
312    {
313        return m_weight(x * m_k2 + m_k1);
314    }
315
316    /* ORDER + 1 Chebyshev coefficients and 1 error value */
317    real coeff[ORDER + 2];
318    /* ORDER + 1 zeroes of the error function */
319    real zeroes[ORDER + 1];
320    /* ORDER + 2 control points */
321    real control[ORDER + 2];
322
323private:
324    RealFunc *m_func, *m_weight;
325    real m_k1, m_k2, m_invk1, m_invk2;
326};
327
328#endif /* __REMEZ_SOLVER_H__ */
329
Note: See TracBrowser for help on using the repository browser.