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 RODFT00 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 sine, 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 rodft00-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 antisymmetric data, but this
|
cannam@95
|
37 would require a whole new set of codelets and it's not clear that
|
cannam@95
|
38 it's 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
|
cannam@95
|
68 buf = (R *) MALLOC(sizeof(R) * n, BUFFERS);
|
cannam@95
|
69
|
cannam@95
|
70 for (iv = 0; iv < vl; ++iv, I += ivs, O += ovs) {
|
cannam@95
|
71 buf[0] = 0;
|
cannam@95
|
72 for (i = 1; i < n - i; ++i) {
|
cannam@95
|
73 E a, b, apb, amb;
|
cannam@95
|
74 a = I[is * (i - 1)];
|
cannam@95
|
75 b = I[is * ((n - i) - 1)];
|
cannam@95
|
76 apb = K(2.0) * W[i] * (a + b);
|
cannam@95
|
77 amb = (a - b);
|
cannam@95
|
78 buf[i] = apb + amb;
|
cannam@95
|
79 buf[n - i] = apb - amb;
|
cannam@95
|
80 }
|
cannam@95
|
81 if (i == n - i) {
|
cannam@95
|
82 buf[i] = K(4.0) * I[is * (i - 1)];
|
cannam@95
|
83 }
|
cannam@95
|
84
|
cannam@95
|
85 {
|
cannam@95
|
86 plan_rdft *cld = (plan_rdft *) ego->cld;
|
cannam@95
|
87 cld->apply((plan *) cld, buf, buf);
|
cannam@95
|
88 }
|
cannam@95
|
89
|
cannam@95
|
90 /* FIXME: use recursive/cascade summation for better stability? */
|
cannam@95
|
91 O[0] = buf[0] * 0.5;
|
cannam@95
|
92 for (i = 1; i + i < n - 1; ++i) {
|
cannam@95
|
93 INT k = i + i;
|
cannam@95
|
94 O[os * (k - 1)] = -buf[n - i];
|
cannam@95
|
95 O[os * k] = O[os * (k - 2)] + buf[i];
|
cannam@95
|
96 }
|
cannam@95
|
97 if (i + i == n - 1) {
|
cannam@95
|
98 O[os * (n - 2)] = -buf[n - i];
|
cannam@95
|
99 }
|
cannam@95
|
100 }
|
cannam@95
|
101
|
cannam@95
|
102 X(ifree)(buf);
|
cannam@95
|
103 }
|
cannam@95
|
104
|
cannam@95
|
105 static void awake(plan *ego_, enum wakefulness wakefulness)
|
cannam@95
|
106 {
|
cannam@95
|
107 P *ego = (P *) ego_;
|
cannam@95
|
108 static const tw_instr rodft00e_tw[] = {
|
cannam@95
|
109 { TW_SIN, 0, 1 },
|
cannam@95
|
110 { TW_NEXT, 1, 0 }
|
cannam@95
|
111 };
|
cannam@95
|
112
|
cannam@95
|
113 X(plan_awake)(ego->cld, wakefulness);
|
cannam@95
|
114
|
cannam@95
|
115 X(twiddle_awake)(wakefulness,
|
cannam@95
|
116 &ego->td, rodft00e_tw, 2*ego->n, 1, (ego->n+1)/2);
|
cannam@95
|
117 }
|
cannam@95
|
118
|
cannam@95
|
119 static void destroy(plan *ego_)
|
cannam@95
|
120 {
|
cannam@95
|
121 P *ego = (P *) ego_;
|
cannam@95
|
122 X(plan_destroy_internal)(ego->cld);
|
cannam@95
|
123 }
|
cannam@95
|
124
|
cannam@95
|
125 static void print(const plan *ego_, printer *p)
|
cannam@95
|
126 {
|
cannam@95
|
127 const P *ego = (const P *) ego_;
|
cannam@95
|
128 p->print(p, "(rodft00e-r2hc-%D%v%(%p%))", ego->n - 1, ego->vl, ego->cld);
|
cannam@95
|
129 }
|
cannam@95
|
130
|
cannam@95
|
131 static int applicable0(const solver *ego_, const problem *p_)
|
cannam@95
|
132 {
|
cannam@95
|
133 const problem_rdft *p = (const problem_rdft *) p_;
|
cannam@95
|
134 UNUSED(ego_);
|
cannam@95
|
135
|
cannam@95
|
136 return (1
|
cannam@95
|
137 && p->sz->rnk == 1
|
cannam@95
|
138 && p->vecsz->rnk <= 1
|
cannam@95
|
139 && p->kind[0] == RODFT00
|
cannam@95
|
140 );
|
cannam@95
|
141 }
|
cannam@95
|
142
|
cannam@95
|
143 static int applicable(const solver *ego, const problem *p, const planner *plnr)
|
cannam@95
|
144 {
|
cannam@95
|
145 return (!NO_SLOWP(plnr) && applicable0(ego, p));
|
cannam@95
|
146 }
|
cannam@95
|
147
|
cannam@95
|
148 static plan *mkplan(const solver *ego_, const problem *p_, planner *plnr)
|
cannam@95
|
149 {
|
cannam@95
|
150 P *pln;
|
cannam@95
|
151 const problem_rdft *p;
|
cannam@95
|
152 plan *cld;
|
cannam@95
|
153 R *buf;
|
cannam@95
|
154 INT n;
|
cannam@95
|
155 opcnt ops;
|
cannam@95
|
156
|
cannam@95
|
157 static const plan_adt padt = {
|
cannam@95
|
158 X(rdft_solve), awake, print, destroy
|
cannam@95
|
159 };
|
cannam@95
|
160
|
cannam@95
|
161 if (!applicable(ego_, p_, plnr))
|
cannam@95
|
162 return (plan *)0;
|
cannam@95
|
163
|
cannam@95
|
164 p = (const problem_rdft *) p_;
|
cannam@95
|
165
|
cannam@95
|
166 n = p->sz->dims[0].n + 1;
|
cannam@95
|
167 buf = (R *) MALLOC(sizeof(R) * n, BUFFERS);
|
cannam@95
|
168
|
cannam@95
|
169 cld = X(mkplan_d)(plnr, X(mkproblem_rdft_1_d)(X(mktensor_1d)(n, 1, 1),
|
cannam@95
|
170 X(mktensor_0d)(),
|
cannam@95
|
171 buf, buf, R2HC));
|
cannam@95
|
172 X(ifree)(buf);
|
cannam@95
|
173 if (!cld)
|
cannam@95
|
174 return (plan *)0;
|
cannam@95
|
175
|
cannam@95
|
176 pln = MKPLAN_RDFT(P, &padt, apply);
|
cannam@95
|
177
|
cannam@95
|
178 pln->n = n;
|
cannam@95
|
179 pln->is = p->sz->dims[0].is;
|
cannam@95
|
180 pln->os = p->sz->dims[0].os;
|
cannam@95
|
181 pln->cld = cld;
|
cannam@95
|
182 pln->td = 0;
|
cannam@95
|
183
|
cannam@95
|
184 X(tensor_tornk1)(p->vecsz, &pln->vl, &pln->ivs, &pln->ovs);
|
cannam@95
|
185
|
cannam@95
|
186 X(ops_zero)(&ops);
|
cannam@95
|
187 ops.other = 4 + (n-1)/2 * 5 + (n-2)/2 * 5;
|
cannam@95
|
188 ops.add = (n-1)/2 * 4 + (n-2)/2 * 1;
|
cannam@95
|
189 ops.mul = 1 + (n-1)/2 * 2;
|
cannam@95
|
190 if (n % 2 == 0)
|
cannam@95
|
191 ops.mul += 1;
|
cannam@95
|
192
|
cannam@95
|
193 X(ops_zero)(&pln->super.super.ops);
|
cannam@95
|
194 X(ops_madd2)(pln->vl, &ops, &pln->super.super.ops);
|
cannam@95
|
195 X(ops_madd2)(pln->vl, &cld->ops, &pln->super.super.ops);
|
cannam@95
|
196
|
cannam@95
|
197 return &(pln->super.super);
|
cannam@95
|
198 }
|
cannam@95
|
199
|
cannam@95
|
200 /* constructor */
|
cannam@95
|
201 static solver *mksolver(void)
|
cannam@95
|
202 {
|
cannam@95
|
203 static const solver_adt sadt = { PROBLEM_RDFT, mkplan, 0 };
|
cannam@95
|
204 S *slv = MKSOLVER(S, &sadt);
|
cannam@95
|
205 return &(slv->super);
|
cannam@95
|
206 }
|
cannam@95
|
207
|
cannam@95
|
208 void X(rodft00e_r2hc_register)(planner *p)
|
cannam@95
|
209 {
|
cannam@95
|
210 REGISTER_SOLVER(p, mksolver());
|
cannam@95
|
211 }
|