Coverage Report

Created: 2025-08-26 06:35

/src/fftw3/rdft/rank-geq2-rdft2.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2003, 2007-14 Matteo Frigo
3
 * Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18
 *
19
 */
20
21
22
/* plans for RDFT2 of rank >= 2 (multidimensional) */
23
24
#include "rdft/rdft.h"
25
#include "dft/dft.h"
26
27
typedef struct {
28
     solver super;
29
     int spltrnk;
30
     const int *buddies;
31
     size_t nbuddies;
32
} S;
33
34
typedef struct {
35
     plan_dft super;
36
     plan *cldr, *cldc;
37
     const S *solver;
38
} P;
39
40
static void apply_r2hc(const plan *ego_, R *r0, R *r1, R *cr, R *ci)
41
0
{
42
0
     const P *ego = (const P *) ego_;
43
44
0
     {
45
0
    plan_rdft2 *cldr = (plan_rdft2 *) ego->cldr;
46
0
    cldr->apply((plan *) cldr, r0, r1, cr, ci);
47
0
     }
48
     
49
0
     {
50
0
    plan_dft *cldc = (plan_dft *) ego->cldc;
51
0
    cldc->apply((plan *) cldc, cr, ci, cr, ci);
52
0
     }
53
0
}
54
55
static void apply_hc2r(const plan *ego_, R *r0, R *r1, R *cr, R *ci)
56
0
{
57
0
     const P *ego = (const P *) ego_;
58
59
0
     {
60
0
    plan_dft *cldc = (plan_dft *) ego->cldc;
61
0
    cldc->apply((plan *) cldc, ci, cr, ci, cr);
62
0
     }
63
64
0
     {
65
0
    plan_rdft2 *cldr = (plan_rdft2 *) ego->cldr;
66
0
    cldr->apply((plan *) cldr, r0, r1, cr, ci);
67
0
     }
68
     
69
0
}
70
71
static void awake(plan *ego_, enum wakefulness wakefulness)
72
0
{
73
0
     P *ego = (P *) ego_;
74
0
     X(plan_awake)(ego->cldr, wakefulness);
75
0
     X(plan_awake)(ego->cldc, wakefulness);
76
0
}
77
78
static void destroy(plan *ego_)
79
0
{
80
0
     P *ego = (P *) ego_;
81
0
     X(plan_destroy_internal)(ego->cldr);
82
0
     X(plan_destroy_internal)(ego->cldc);
83
0
}
84
85
static void print(const plan *ego_, printer *p)
86
0
{
87
0
     const P *ego = (const P *) ego_;
88
0
     const S *s = ego->solver;
89
0
     p->print(p, "(rdft2-rank>=2/%d%(%p%)%(%p%))", 
90
0
        s->spltrnk, ego->cldr, ego->cldc);
91
0
}
92
 
93
static int picksplit(const S *ego, const tensor *sz, int *rp)
94
0
{
95
0
     A(sz->rnk > 1); /* cannot split rnk <= 1 */
96
0
     if (!X(pickdim)(ego->spltrnk, ego->buddies, ego->nbuddies, sz, 1, rp))
97
0
          return 0;
98
0
     *rp += 1; /* convert from dim. index to rank */
99
0
     if (*rp >= sz->rnk) /* split must reduce rank */
100
0
          return 0;
101
0
     return 1;
102
0
}
103
104
static int applicable0(const solver *ego_, const problem *p_, int *rp,
105
           const planner *plnr)
106
0
{
107
0
     const problem_rdft2 *p = (const problem_rdft2 *) p_;
108
0
     const S *ego = (const S *)ego_;
109
0
     return (1
110
0
       && FINITE_RNK(p->sz->rnk) && FINITE_RNK(p->vecsz->rnk)
111
112
       /* FIXME: multidimensional R2HCII ? */
113
0
       && (p->kind == R2HC || p->kind == HC2R)
114
115
0
       && p->sz->rnk >= 2
116
0
       && picksplit(ego, p->sz, rp)
117
0
       && (0
118
119
     /* can work out-of-place, but HC2R destroys input */
120
0
     || (p->r0 != p->cr && 
121
0
         (p->kind == R2HC || !NO_DESTROY_INPUTP(plnr)))
122
123
     /* FIXME: what are sufficient conditions for inplace? */
124
0
     || (p->r0 == p->cr))
125
0
    );
126
0
}
127
128
/* TODO: revise this. */
129
static int applicable(const solver *ego_, const problem *p_, 
130
          const planner *plnr, int *rp)
131
0
{
132
0
     const S *ego = (const S *)ego_;
133
134
0
     if (!applicable0(ego_, p_, rp, plnr)) return 0;
135
136
0
     if (NO_RANK_SPLITSP(plnr) && (ego->spltrnk != ego->buddies[0]))
137
0
          return 0;
138
139
0
     if (NO_UGLYP(plnr)) {
140
0
    const problem_rdft2 *p = (const problem_rdft2 *) p_;
141
142
    /* Heuristic: if the vector stride is greater than the transform
143
       size, don't use (prefer to do the vector loop first with a
144
       vrank-geq1 plan). */
145
0
    if (p->vecsz->rnk > 0 &&
146
0
        X(tensor_min_stride)(p->vecsz) 
147
0
        > X(rdft2_tensor_max_index)(p->sz, p->kind))
148
0
         return 0;
149
0
     }
150
151
0
     return 1;
152
0
}
153
154
static plan *mkplan(const solver *ego_, const problem *p_, planner *plnr)
155
0
{
156
0
     const S *ego = (const S *) ego_;
157
0
     const problem_rdft2 *p;
158
0
     P *pln;
159
0
     plan *cldr = 0, *cldc = 0;
160
0
     tensor *sz1, *sz2, *vecszi, *sz2i;
161
0
     int spltrnk;
162
0
     inplace_kind k;
163
0
     problem *cldp;
164
165
0
     static const plan_adt padt = {
166
0
    X(rdft2_solve), awake, print, destroy
167
0
     };
168
169
0
     if (!applicable(ego_, p_, plnr, &spltrnk))
170
0
          return (plan *) 0;
171
172
0
     p = (const problem_rdft2 *) p_;
173
0
     X(tensor_split)(p->sz, &sz1, spltrnk, &sz2);
174
175
0
     k = p->kind == R2HC ? INPLACE_OS : INPLACE_IS;
176
0
     vecszi = X(tensor_copy_inplace)(p->vecsz, k);
177
0
     sz2i = X(tensor_copy_inplace)(sz2, k);
178
179
     /* complex data is ~half of real */
180
0
     sz2i->dims[sz2i->rnk - 1].n = sz2i->dims[sz2i->rnk - 1].n/2 + 1;
181
182
0
     cldr = X(mkplan_d)(plnr, 
183
0
           X(mkproblem_rdft2_d)(X(tensor_copy)(sz2),
184
0
              X(tensor_append)(p->vecsz, sz1),
185
0
              p->r0, p->r1,
186
0
              p->cr, p->ci, p->kind));
187
0
     if (!cldr) goto nada;
188
189
0
     if (p->kind == R2HC)
190
0
    cldp = X(mkproblem_dft_d)(X(tensor_copy_inplace)(sz1, k),
191
0
            X(tensor_append)(vecszi, sz2i),
192
0
            p->cr, p->ci, p->cr, p->ci);
193
0
     else /* HC2R must swap re/im parts to get IDFT */
194
0
    cldp = X(mkproblem_dft_d)(X(tensor_copy_inplace)(sz1, k),
195
0
            X(tensor_append)(vecszi, sz2i),
196
0
            p->ci, p->cr, p->ci, p->cr);
197
0
     cldc = X(mkplan_d)(plnr, cldp);
198
0
     if (!cldc) goto nada;
199
200
0
     pln = MKPLAN_RDFT2(P, &padt, p->kind == R2HC ? apply_r2hc : apply_hc2r);
201
202
0
     pln->cldr = cldr;
203
0
     pln->cldc = cldc;
204
205
0
     pln->solver = ego;
206
0
     X(ops_add)(&cldr->ops, &cldc->ops, &pln->super.super.ops);
207
208
0
     X(tensor_destroy4)(sz2i, vecszi, sz2, sz1);
209
210
0
     return &(pln->super.super);
211
212
0
 nada:
213
0
     X(plan_destroy_internal)(cldr);
214
0
     X(plan_destroy_internal)(cldc);
215
0
     X(tensor_destroy4)(sz2i, vecszi, sz2, sz1);
216
0
     return (plan *) 0;
217
0
}
218
219
static solver *mksolver(int spltrnk, const int *buddies, size_t nbuddies)
220
3
{
221
3
     static const solver_adt sadt = { PROBLEM_RDFT2, mkplan, 0 };
222
3
     S *slv = MKSOLVER(S, &sadt);
223
3
     slv->spltrnk = spltrnk;
224
3
     slv->buddies = buddies;
225
3
     slv->nbuddies = nbuddies;
226
3
     return &(slv->super);
227
3
}
228
229
void X(rdft2_rank_geq2_register)(planner *p)
230
1
{
231
1
     static const int buddies[] = { 1, 0, -2 };
232
1
     size_t i;
233
234
4
     for (i = 0; i < NELEM(buddies); ++i)
235
3
          REGISTER_SOLVER(p, mksolver(buddies[i], buddies, NELEM(buddies)));
236
237
     /* FIXME: Should we try more buddies?  See also dft/rank-geq2. */
238
1
}