cannam@95
|
1 /*
|
cannam@95
|
2 * Copyright (c) 2003, 2007-11 Matteo Frigo
|
cannam@95
|
3 * Copyright (c) 2003, 2007-11 Massachusetts Institute of Technology
|
cannam@95
|
4 *
|
cannam@95
|
5 * This program is free software; you can redistribute it and/or modify
|
cannam@95
|
6 * it under the terms of the GNU General Public License as published by
|
cannam@95
|
7 * the Free Software Foundation; either version 2 of the License, or
|
cannam@95
|
8 * (at your option) any later version.
|
cannam@95
|
9 *
|
cannam@95
|
10 * This program is distributed in the hope that it will be useful,
|
cannam@95
|
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
cannam@95
|
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
cannam@95
|
13 * GNU General Public License for more details.
|
cannam@95
|
14 *
|
cannam@95
|
15 * You should have received a copy of the GNU General Public License
|
cannam@95
|
16 * along with this program; if not, write to the Free Software
|
cannam@95
|
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
cannam@95
|
18 *
|
cannam@95
|
19 */
|
cannam@95
|
20
|
cannam@95
|
21
|
cannam@95
|
22 /* Do a REDFT00 problem via an R2HC problem, with some pre/post-processing.
|
cannam@95
|
23
|
cannam@95
|
24 This code uses the trick from FFTPACK, also documented in a similar
|
cannam@95
|
25 form by Numerical Recipes. Unfortunately, this algorithm seems to
|
cannam@95
|
26 have intrinsic numerical problems (similar to those in
|
cannam@95
|
27 reodft11e-r2hc.c), possibly due to the fact that it multiplies its
|
cannam@95
|
28 input by a cosine, causing a loss of precision near the zero. For
|
cannam@95
|
29 transforms of 16k points, it has already lost three or four decimal
|
cannam@95
|
30 places of accuracy, which we deem unacceptable.
|
cannam@95
|
31
|
cannam@95
|
32 So, we have abandoned this algorithm in favor of the one in
|
cannam@95
|
33 redft00-r2hc-pad.c, which unfortunately sacrifices 30-50% in speed.
|
cannam@95
|
34 The only other alternative in the literature that does not have
|
cannam@95
|
35 similar numerical difficulties seems to be the direct adaptation of
|
cannam@95
|
36 the Cooley-Tukey decomposition for symmetric data, but this would
|
cannam@95
|
37 require a whole new set of codelets and it's not clear that it's
|
cannam@95
|
38 worth it at this point. However, we did implement the latter
|
cannam@95
|
39 algorithm for the specific case of odd n (logically adapting the
|
cannam@95
|
40 split-radix algorithm); see reodft00e-splitradix.c. */
|
cannam@95
|
41
|
cannam@95
|
42 #include "reodft.h"
|
cannam@95
|
43
|
cannam@95
|
44 typedef struct {
|
cannam@95
|
45 solver super;
|
cannam@95
|
46 } S;
|
cannam@95
|
47
|
cannam@95
|
48 typedef struct {
|
cannam@95
|
49 plan_rdft super;
|
cannam@95
|
50 plan *cld;
|
cannam@95
|
51 twid *td;
|
cannam@95
|
52 INT is, os;
|
cannam@95
|
53 INT n;
|
cannam@95
|
54 INT vl;
|
cannam@95
|
55 INT ivs, ovs;
|
cannam@95
|
56 } P;
|
cannam@95
|
57
|
cannam@95
|
58 static void apply(const plan *ego_, R *I, R *O)
|
cannam@95
|
59 {
|
cannam@95
|
60 const P *ego = (const P *) ego_;
|
cannam@95
|
61 INT is = ego->is, os = ego->os;
|
cannam@95
|
62 INT i, n = ego->n;
|
cannam@95
|
63 INT iv, vl = ego->vl;
|
cannam@95
|
64 INT ivs = ego->ivs, ovs = ego->ovs;
|
cannam@95
|
65 R *W = ego->td->W;
|
cannam@95
|
66 R *buf;
|
cannam@95
|
67 E csum;
|
cannam@95
|
68
|
cannam@95
|
69 buf = (R *) MALLOC(sizeof(R) * n, BUFFERS);
|
cannam@95
|
70
|
cannam@95
|
71 for (iv = 0; iv < vl; ++iv, I += ivs, O += ovs) {
|
cannam@95
|
72 buf[0] = I[0] + I[is * n];
|
cannam@95
|
73 csum = I[0] - I[is * n];
|
cannam@95
|
74 for (i = 1; i < n - i; ++i) {
|
cannam@95
|
75 E a, b, apb, amb;
|
cannam@95
|
76 a = I[is * i];
|
cannam@95
|
77 b = I[is * (n - i)];
|
cannam@95
|
78 csum += W[2*i] * (amb = K(2.0)*(a - b));
|
cannam@95
|
79 amb = W[2*i+1] * amb;
|
cannam@95
|
80 apb = (a + b);
|
cannam@95
|
81 buf[i] = apb - amb;
|
cannam@95
|
82 buf[n - i] = apb + amb;
|
cannam@95
|
83 }
|
cannam@95
|
84 if (i == n - i) {
|
cannam@95
|
85 buf[i] = K(2.0) * I[is * i];
|
cannam@95
|
86 }
|
cannam@95
|
87
|
cannam@95
|
88 {
|
cannam@95
|
89 plan_rdft *cld = (plan_rdft *) ego->cld;
|
cannam@95
|
90 cld->apply((plan *) cld, buf, buf);
|
cannam@95
|
91 }
|
cannam@95
|
92
|
cannam@95
|
93 /* FIXME: use recursive/cascade summation for better stability? */
|
cannam@95
|
94 O[0] = buf[0];
|
cannam@95
|
95 O[os] = csum;
|
cannam@95
|
96 for (i = 1; i + i < n; ++i) {
|
cannam@95
|
97 INT k = i + i;
|
cannam@95
|
98 O[os * k] = buf[i];
|
cannam@95
|
99 O[os * (k + 1)] = O[os * (k - 1)] - buf[n - i];
|
cannam@95
|
100 }
|
cannam@95
|
101 if (i + i == n) {
|
cannam@95
|
102 O[os * n] = buf[i];
|
cannam@95
|
103 }
|
cannam@95
|
104 }
|
cannam@95
|
105
|
cannam@95
|
106 X(ifree)(buf);
|
cannam@95
|
107 }
|
cannam@95
|
108
|
cannam@95
|
109 static void awake(plan *ego_, enum wakefulness wakefulness)
|
cannam@95
|
110 {
|
cannam@95
|
111 P *ego = (P *) ego_;
|
cannam@95
|
112 static const tw_instr redft00e_tw[] = {
|
cannam@95
|
113 { TW_COS, 0, 1 },
|
cannam@95
|
114 { TW_SIN, 0, 1 },
|
cannam@95
|
115 { TW_NEXT, 1, 0 }
|
cannam@95
|
116 };
|
cannam@95
|
117
|
cannam@95
|
118 X(plan_awake)(ego->cld, wakefulness);
|
cannam@95
|
119 X(twiddle_awake)(wakefulness,
|
cannam@95
|
120 &ego->td, redft00e_tw, 2*ego->n, 1, (ego->n+1)/2);
|
cannam@95
|
121 }
|
cannam@95
|
122
|
cannam@95
|
123 static void destroy(plan *ego_)
|
cannam@95
|
124 {
|
cannam@95
|
125 P *ego = (P *) ego_;
|
cannam@95
|
126 X(plan_destroy_internal)(ego->cld);
|
cannam@95
|
127 }
|
cannam@95
|
128
|
cannam@95
|
129 static void print(const plan *ego_, printer *p)
|
cannam@95
|
130 {
|
cannam@95
|
131 const P *ego = (const P *) ego_;
|
cannam@95
|
132 p->print(p, "(redft00e-r2hc-%D%v%(%p%))", ego->n + 1, ego->vl, ego->cld);
|
cannam@95
|
133 }
|
cannam@95
|
134
|
cannam@95
|
135 static int applicable0(const solver *ego_, const problem *p_)
|
cannam@95
|
136 {
|
cannam@95
|
137 const problem_rdft *p = (const problem_rdft *) p_;
|
cannam@95
|
138 UNUSED(ego_);
|
cannam@95
|
139
|
cannam@95
|
140 return (1
|
cannam@95
|
141 && p->sz->rnk == 1
|
cannam@95
|
142 && p->vecsz->rnk <= 1
|
cannam@95
|
143 && p->kind[0] == REDFT00
|
cannam@95
|
144 && p->sz->dims[0].n > 1 /* n == 1 is not well-defined */
|
cannam@95
|
145 );
|
cannam@95
|
146 }
|
cannam@95
|
147
|
cannam@95
|
148 static int applicable(const solver *ego, const problem *p, const planner *plnr)
|
cannam@95
|
149 {
|
cannam@95
|
150 return (!NO_SLOWP(plnr) && applicable0(ego, p));
|
cannam@95
|
151 }
|
cannam@95
|
152
|
cannam@95
|
153 static plan *mkplan(const solver *ego_, const problem *p_, planner *plnr)
|
cannam@95
|
154 {
|
cannam@95
|
155 P *pln;
|
cannam@95
|
156 const problem_rdft *p;
|
cannam@95
|
157 plan *cld;
|
cannam@95
|
158 R *buf;
|
cannam@95
|
159 INT n;
|
cannam@95
|
160 opcnt ops;
|
cannam@95
|
161
|
cannam@95
|
162 static const plan_adt padt = {
|
cannam@95
|
163 X(rdft_solve), awake, print, destroy
|
cannam@95
|
164 };
|
cannam@95
|
165
|
cannam@95
|
166 if (!applicable(ego_, p_, plnr))
|
cannam@95
|
167 return (plan *)0;
|
cannam@95
|
168
|
cannam@95
|
169 p = (const problem_rdft *) p_;
|
cannam@95
|
170
|
cannam@95
|
171 n = p->sz->dims[0].n - 1;
|
cannam@95
|
172 A(n > 0);
|
cannam@95
|
173 buf = (R *) MALLOC(sizeof(R) * n, BUFFERS);
|
cannam@95
|
174
|
cannam@95
|
175 cld = X(mkplan_d)(plnr, X(mkproblem_rdft_1_d)(X(mktensor_1d)(n, 1, 1),
|
cannam@95
|
176 X(mktensor_0d)(),
|
cannam@95
|
177 buf, buf, R2HC));
|
cannam@95
|
178 X(ifree)(buf);
|
cannam@95
|
179 if (!cld)
|
cannam@95
|
180 return (plan *)0;
|
cannam@95
|
181
|
cannam@95
|
182 pln = MKPLAN_RDFT(P, &padt, apply);
|
cannam@95
|
183
|
cannam@95
|
184 pln->n = n;
|
cannam@95
|
185 pln->is = p->sz->dims[0].is;
|
cannam@95
|
186 pln->os = p->sz->dims[0].os;
|
cannam@95
|
187 pln->cld = cld;
|
cannam@95
|
188 pln->td = 0;
|
cannam@95
|
189
|
cannam@95
|
190 X(tensor_tornk1)(p->vecsz, &pln->vl, &pln->ivs, &pln->ovs);
|
cannam@95
|
191
|
cannam@95
|
192 X(ops_zero)(&ops);
|
cannam@95
|
193 ops.other = 8 + (n-1)/2 * 11 + (1 - n % 2) * 5;
|
cannam@95
|
194 ops.add = 2 + (n-1)/2 * 5;
|
cannam@95
|
195 ops.mul = (n-1)/2 * 3 + (1 - n % 2) * 1;
|
cannam@95
|
196
|
cannam@95
|
197 X(ops_zero)(&pln->super.super.ops);
|
cannam@95
|
198 X(ops_madd2)(pln->vl, &ops, &pln->super.super.ops);
|
cannam@95
|
199 X(ops_madd2)(pln->vl, &cld->ops, &pln->super.super.ops);
|
cannam@95
|
200
|
cannam@95
|
201 return &(pln->super.super);
|
cannam@95
|
202 }
|
cannam@95
|
203
|
cannam@95
|
204 /* constructor */
|
cannam@95
|
205 static solver *mksolver(void)
|
cannam@95
|
206 {
|
cannam@95
|
207 static const solver_adt sadt = { PROBLEM_RDFT, mkplan, 0 };
|
cannam@95
|
208 S *slv = MKSOLVER(S, &sadt);
|
cannam@95
|
209 return &(slv->super);
|
cannam@95
|
210 }
|
cannam@95
|
211
|
cannam@95
|
212 void X(redft00e_r2hc_register)(planner *p)
|
cannam@95
|
213 {
|
cannam@95
|
214 REGISTER_SOLVER(p, mksolver());
|
cannam@95
|
215 }
|