S2kit  1.1
Toolkit for working with functions defined on the sphere
cospml.c
Go to the documentation of this file.
1 
9 #include "s2kit/cospml.h"
10 
11 #include <math.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include <fftw3.h>
17 
18 #include "s2kit/chebyshev_nodes.h"
19 #include "s2kit/pml.h"
20 #include "s2kit/pmm.h"
21 
22 #include "util/l2_norms.h"
23 #include "util/vector_funcs.h"
24 
25 const int BW_LIMIT = 512;
39 int TableSize(const int m, const int bw) {
40  int k = bw / 2;
41 
42  int fudge, a1, a2;
43  if (bw % 2) { // if the bandwidth is odd
44  fudge = (m + 1) % 2;
45 
46  a1 = k * (k + 1);
47  a2 = fudge * (k + 1);
48  } else { // bandwidth is even
49  fudge = m % 2;
50 
51  a1 = (k - fudge) * (k - fudge + 1);
52  a2 = fudge * k;
53  }
54 
55  fudge = m / 2;
56  int a3 = fudge * (fudge + 1);
57 
58  return a1 + a2 - a3;
59 }
60 
85 int Spharmonic_TableSize(const int bw) {
86  // TODO check bw > 512 (~750 ok?)
87  if (bw <= BW_LIMIT) {
88  return (((4 * bw * bw * bw) + (6 * bw * bw) - (8 * bw)) / 24) + bw;
89  }
90 
91  return Reduced_SpharmonicTableSize(bw, bw);
92 }
93 
94 /*
95  This is a "reduced" version of `Spharmonic_TableSize(m)`.
96 
97  Returns an integer value for the amount of space necessary
98  to fill out a spharmonic table
99  if interesting in using it only for orders up to (but NOT
100  including) order `m`.
101  This will be used in the hybrid algorithm's call of the
102  semi-naive algorithm (which won't need the full table ...
103  hopefully this'll cut down on the memory usage).
104 
105  Also, the transpose is exactly the same size, obviously.
106  */
107 int Reduced_SpharmonicTableSize(const int bw, const int m) {
108  // TODO optimize? or just use Spharmonic_TableSize? (say no to economy)
109 
110  int sum = 0;
111  for (int i = 0; i < m; ++i)
112  sum += TableSize(i, bw);
113 
114  return sum;
115 }
116 
117 /*
118  Computes the location of the first coefficient of Pml for an array
119  containing cosine series coefficients of Pml or Gml functions.
120 
121  Assumes the table is generated by `GenerateCosPmlTable()`.
122  */
123 int TableOffset(int m, int l) {
124  if (m % 2) {
125  --m;
126  --l;
127  }
128 
129  int offset = ((l / 2) * ((l / 2) + 1)) - ((m / 2) * ((m / 2) + 1));
130  if (l % 2)
131  offset += (l / 2) + 1;
132 
133  return offset;
134 }
135 
136 /*
137  Generates all of the cosine series for L2-normalized Pmls or Gmls for
138  a specified value of `m`. Note especially that since series are
139  zero-striped, all zeroes have been removed.
140 
141  tablespace points to a double array of size TableSize(m,bw);
142 
143  Workspace needs to be `9 * bw`
144 
145  Let P(m,l,j) represent the j-th coefficient of the
146  cosine series representation of Pml. The array
147  stuffed into tablespace is organized as follows:
148 
149  P(m,m,0) P(m,m,2) ... P(m,m,m)
150  P(m,m+1,1) P(m,m+1,3) ... P(m,m+1,m+1)
151  P(m,m+2,0) P(m,m+2,2) ... P(m,m+2,m+2)
152 
153  etc. Appropriate modifications are made for `m` odd (Gml functions).
154 
155  NOTE that the Pmls or Gmls are being sampled at bw-many points,
156  and not 2*bw-many points. I can get away with this. HOWEVER, I
157  need to multiply the coefficients by sqrt(2), because the expected
158  input of the seminaive transform of bandwidth bw will be sampled
159  at 2-bw many points. So the sqrt(2) is a scaling factor.
160  */
161 void GenerateCosPmlTable(const int bw, const int m, double* tablespace, double* workspace) {
162  double* prevprev = workspace;
163  double* prev = prevprev + bw;
164  double* temp1 = prev + bw;
165  double* temp2 = temp1 + bw;
166  double* temp3 = temp2 + bw;
167  double* temp4 = temp3 + bw;
168  double* x_i = temp4 + bw;
169  double* eval_args = x_i + bw;
170  double* cosres = eval_args + bw;
171 
172  double* tableptr = tablespace;
173 
174  // set the initial number of evaluation points to appropriate amount
175 
176  // get the evaluation nodes
177  ChebyshevNodes(bw, x_i);
178  AcosOfChebyshevNodes(bw, eval_args);
179 
180  // set initial values of first two Pmls
181  for (int i = 0; i < bw; ++i)
182  prevprev[i] = 0.0;
183 
184  if (m == 0)
185  for (int i = 0; i < bw; ++i)
186  prev[i] = M_SQRT1_2; // sqrt(1/2)
187  else
188  Pmm_L2(m, eval_args, bw, prev);
189 
190  if (m % 2)
191  for (int i = 0; i < bw; ++i)
192  prev[i] /= sin(eval_args[i]);
193 
194 
195  int k; // set k to highest degree coefficient
196  if ((m % 2) == 0)
197  k = m;
198  else
199  k = m - 1;
200 
201  // compute cosine transform
202  memcpy(temp4, prev, sizeof(double) * bw);
203  fftw_plan plan = fftw_plan_r2r_1d(bw, temp4, cosres, FFTW_REDFT10, FFTW_ESTIMATE);
204  fftw_execute(plan);
205  cosres[0] *= M_SQRT1_2;
206  double fudge = 1. / sqrt(bw);
207  for (int i = 0; i < bw; ++i)
208  cosres[i] *= fudge;
209 
210  // store what we've got so far
211  for (int i = 0; i <= k; i += 2)
212  tableptr[i / 2] = cosres[i];
213 
214  tableptr += k / 2 + 1;
215 
216  // generate remaining Pmls
217  for (int i = 0; i < bw - m - 1; ++i) {
218  vec_mul(L2_cn(m, m + i), prevprev, temp1, bw);
219  vec_dot(prev, x_i, temp2, bw);
220  vec_mul(L2_an(m, m + i), temp2, temp3, bw);
221  vec_add(temp3, temp1, temp4, bw); // temp4 now contains P(m,m+i+1)
222 
223  // compute cosine transform
224  fftw_execute(plan);
225  cosres[0] *= M_SQRT1_2;
226  for (int j = 0; j < bw; ++j)
227  cosres[j] *= fudge;
228 
229  ++k; // update degree counter
230 
231  for (int j = (i % 2) ? 0 : 1; j <= k; j += 2)
232  tableptr[j / 2] = cosres[j];
233 
234  tableptr += k / 2 + 1;
235 
236  // update Pi and P(i+1)
237  memcpy(prevprev, prev, sizeof(double) * bw);
238  memcpy(prev, temp4, sizeof(double) * bw);
239  }
240 
241  fftw_destroy_plan(plan);
242 }
243 
244 /*
245  RowSize returns the number of non-zero coefficients in a row of the
246  cospmltable if were really in matrix form. Helpful in transpose
247  computations. It is helpful to think of the parameter l as
248  the row of the corresponding matrix.
249 */
250 int RowSize(const int m, const int l) {
251  if (l < m)
252  return 0;
253 
254  if (!(m % 2))
255  return (l / 2) + 1;
256 
257  return ((l - 1) / 2) + 1;
258 }
259 
260 /*
261  Transposed row size returns the number of non-zero coefficients
262  in the transposition of the matrix representing a cospmltable.
263  Used for generating arrays for inverse seminaive transform.
264  Unlike `RowSize()`, need to know the bandwidth `bw`. Also, in
265  the cospml array, the first `m+1` rows are empty, but in
266  the transpose, all rows have non-zero entries, and the first
267  `m+1` columns are empty. So the input parameters are a bit different
268  in the you need to specify the row you want.
269 */
270 int Transpose_RowSize(const int row, const int m, const int bw) {
271  if (row >= bw)
272  return 0;
273 
274  if (!(m % 2)) {
275  if (row <= m)
276  return (bw - m) / 2;
277 
278  return ((bw - row - 1) / 2) + 1;
279  }
280 
281  if (row == (bw - 1))
282  return 0;
283 
284  if (row >= m)
285  return Transpose_RowSize(row + 1, m - 1, bw);
286 
287  return Transpose_RowSize(row + 1, m - 1, bw) - (row % 2);
288 }
289 
290 /*
291  Inverse transform is transposition of forward transform.
292  Thus, need to provide transposed version of table
293  returned by `GenerateCosPmlTable()`. This function does that
294  by taking as input a `cos_pml_table` for a particular value
295  of `bw` and `m`, and loads the `result` as a transposed,
296  decimated version of it for use by an inverse seminaive
297  transform computation.
298 
299  `result` needs to be of size `TableSize(m, bw)`
300 */
301 void TransposeCosPmlTable(const int bw, const int m, double* cos_pml_table, double* result) {
302  // Recall that `cos_pml_table` has had all the zeroes stripped out,
303  // and that if `m` is odd, then it is really a Gml function, which affects indexing a bit.
304 
305  // note that the number of non-zero entries is the same as in the non-transposed case
306  double* trans_tableptr = result;
307 
308  // traverse the `cos_pml_table`, loading appropriate values into the rows of transposed array
309  if (m == bw - 1) {
310  memcpy(result, cos_pml_table, sizeof(double) * TableSize(m, bw));
311  return;
312  }
313 
314  for (int row = 0; row < bw; ++row) {
315  // if `m` odd, no need to do last row - all zeroes
316  if (row == (bw - 1) && (m % 2))
317  return;
318 
319  double* tableptr;
320 
321  // compute the starting point for values in `cos_pml_table`
322  if (row <= m) {
323  if (!(row % 2))
324  tableptr = cos_pml_table + (row / 2);
325  else
326  tableptr = cos_pml_table + (m / 2) + 1 + (row / 2);
327  } else {
328  // then the highest degree coefficient of P(m,row) should be the first coefficient loaded
329  // into the transposed array, so figure out where this point is
330  int offset = 0;
331  int end_row = (m % 2) == 0 ? row : row + 1;
332  for (int i = m; i <= end_row; ++i)
333  offset += RowSize(m, i);
334 
335  --offset; // we are pointing one element too far, so decrement
336 
337  tableptr = cos_pml_table + offset;
338  }
339 
340  // `stride` is how far we need to jump between values in `cos_pml_table`, i.e.,
341  // to traverse the columns of the `cos_pml_table`. Need to set initial value.
342  // `stride` always increases by 2 after that.
343  int stride;
344  if (row <= m)
345  stride = m + 2 - (m % 2) + (row % 2);
346  else
347  stride = row + 2;
348 
349  // get the rowsize for the transposed array
350  int rowsize = Transpose_RowSize(row, m, bw);
351 
352  // load up this row of the transposed table
353  int costable_offset = 0;
354  for (int i = 0; i < rowsize; ++i) {
355  trans_tableptr[i] = tableptr[costable_offset];
356  costable_offset += stride;
357  stride += 2;
358  }
359 
360  trans_tableptr += rowsize;
361  }
362 }
363 
364 /*
365  Returns all of the (cosine transforms of) Pmls and Gmls necessary
366  to do a full spherical harmonic transform, i.e., it calls
367  `GenerateCosPmlTable()` for each value of `m` less than `bw`, returning a
368  table of tables (a pointer of type (double**), which points
369  to an array of size `m`, each containing a (double*) pointer
370  to a set of CosPml or CosGml values, which are the (decimated)
371  cosine series representations of Pml (even `m`) or Gml (odd `m`)
372  functions. See `GenerateCosPmlTable()` for further clarification.
373 
374  bw - bandwidth of the problem;
375  resultspace - need to allocate `Spharmonic_TableSize(bw)` for storing results
376  workspace - needs to be `16*bw`
377 
378  Note that `resultspace` is necessary and contains the results/values
379  so one should be careful about when it is OK to re-use this space.
380  workspace, though, does not have any meaning after this function is
381  finished executing.
382 
383 // from FST_semi_memo.c:
384  spharmonic_pml_table will be an array of (double *) pointers
385  the array being of length TableSize(m,bw)
386 */
387 double** Spharmonic_Pml_Table(const int bw, double* resultspace, double* workspace) {
388  double** spharmonic_pml_table = (double**)malloc(sizeof(double*) * bw);
389 
390  spharmonic_pml_table[0] = resultspace;
391  GenerateCosPmlTable(bw, 0, spharmonic_pml_table[0], workspace);
392 
393  // traverse the array, assigning a location in the `resultspace` to each pointer
394  // and load up the array with CosPml and CosGml values
395  for (int i = 1; i < bw; ++i) {
396  spharmonic_pml_table[i] = spharmonic_pml_table[i - 1] + TableSize(i - 1, bw);
397  GenerateCosPmlTable(bw, i, spharmonic_pml_table[i], workspace);
398  }
399 
400  return spharmonic_pml_table;
401 }
402 
403 /*
404  For the inverse semi-naive spharmonic transform, the "transpose"
405  of the `spharmonic_pml_table` is needed. Need to be careful because the
406  entries in the `spharmonic_pml_table` have been decimated, i.e.,
407  the zeroes have been stripped out.
408 
409  spharmonic_pml_table - generated by `Spharmonic_Pml_Table()`;
410  bw - bandwidth of the problem;
411  resultspace - need to allocate Spharmonic_TableSize(bw) for storing results.
412 
413  Allocates memory for the (double**) `resultspace`.
414 */
415 double** Transpose_Spharmonic_Pml_Table(double** spharmonic_pml_table, const int bw, double* resultspace) {
416  double** transpose_spharmonic_pml_table = (double**)malloc(sizeof(double*) * bw);
417 
418  transpose_spharmonic_pml_table[0] = resultspace;
419  TransposeCosPmlTable(bw, 0, spharmonic_pml_table[0], transpose_spharmonic_pml_table[0]);
420 
421  // load up the `transpose_spharmonic_pml_table` by transposing the tables in `spharmonic_pml_table`
422  for (int i = 1; i < bw; ++i) {
423  transpose_spharmonic_pml_table[i] = transpose_spharmonic_pml_table[i - 1] + TableSize(i - 1, bw);
424  TransposeCosPmlTable(bw, i, spharmonic_pml_table[i], transpose_spharmonic_pml_table[i]);
425  }
426 
427  return transpose_spharmonic_pml_table;
428 }
429 
430 /*
431  Returns an integer value for the amount of space necessary to fill out
432  a reduced naive table of Pmls if interested in using it only for orders `m` through `bw-1`.
433 */
434 int Reduced_Naive_TableSize(const int bw, const int m) {
435  int sum = 0;
436  for (int i = m; i < bw; ++i)
437  sum += bw - i;
438 
439  return 2 * bw * sum;
440 }
441 
442 /*
443  Just like Spharmonic_Pml_Table(), except generates a table for use
444  with the semi-naive and naive algorithms.
445 
446  bw - bandwidth of the problem;
447  m - the cutoff order, where to switch from semi-naive to naive algorithms;
448  resultspace - stores results, must be of size
449  `Reduced_Naive_TableSize(bw, m) + Reduced_SpharmonicTableSize(bw, m)`;
450 */
451 double** SemiNaive_Naive_Pml_Table(const int bw, const int m, double* resultspace, double* workspace) {
452  double** seminaive_naive_table = (double**)malloc(sizeof(double) * (bw + 1));
453 
454  // load up the array with CosPml and CosGml values
455  seminaive_naive_table[0] = resultspace;
456  GenerateCosPmlTable(bw, 0, seminaive_naive_table[0], workspace);
457 
458  for (int i = 1; i < m; ++i) {
459  seminaive_naive_table[i] = seminaive_naive_table[i - 1] + TableSize(i - 1, bw);
460  GenerateCosPmlTable(bw, i, seminaive_naive_table[i], workspace);
461  }
462 
463  // load up with Pml values
464  if (m) {
465  seminaive_naive_table[m] = seminaive_naive_table[m - 1] + TableSize(m - 1, bw);
466  GeneratePmlTable(bw, m, seminaive_naive_table[m], workspace);
467  }
468 
469  for (int i = m + 1; i < bw; ++i) {
470  seminaive_naive_table[i] = seminaive_naive_table[i - 1] + (2 * bw * (bw - (i - 1)));
471  GeneratePmlTable(bw, i, seminaive_naive_table[i], workspace);
472  }
473 
474  return seminaive_naive_table;
475 }
476 
477 /*
478  For the inverse seminaive_naive transform, need the "transpose"
479  of the seminaive_naive_pml_table. Need to be careful because the
480  entries in the seminaive portion have been decimated, i.e.,
481  the zeroes have been stripped out.
482 
483  seminaive_naive_pml_table - generated by `SemiNaive_Naive_Pml_Table()`;
484  bw - bandwidth of the problem;
485  m - the cutoff order, where to switch from semi-naive to naive algorithms;
486  resultspace - need to allocate
487  Reduced_Naive_TableSize(bw, m) + Reduced_SpharmonicTableSize(bw, m) for storing results;
488  workspace - size of `16*bw`
489 */
490 double** Transpose_SemiNaive_Naive_Pml_Table(double** seminaive_naive_pml_table, const int bw, const int m,
491  double* resultspace, double* workspace) {
492  double** trans_seminaive_naive_pml_table = (double**)malloc(sizeof(double*) * (bw + 1));
493 
494  // need to load up the `transpose_seminaive_naive_pml_table` by transposing
495  // the tables in the seminaive portion of `seminaive_naive_pml_table`
496 
497  // load up the array with CosPml and CosGml values
498  trans_seminaive_naive_pml_table[0] = resultspace;
499  TransposeCosPmlTable(bw, 0, seminaive_naive_pml_table[0], trans_seminaive_naive_pml_table[0]);
500 
501  for (int i = 1; i < m; ++i) {
502  trans_seminaive_naive_pml_table[i] = trans_seminaive_naive_pml_table[i - 1] + TableSize(i - 1, bw);
503  TransposeCosPmlTable(bw, i, seminaive_naive_pml_table[i], trans_seminaive_naive_pml_table[i]);
504  }
505 
506  // load up with Pml values
507  if (m) {
508  trans_seminaive_naive_pml_table[m] = trans_seminaive_naive_pml_table[m - 1] + TableSize(m - 1, bw);
509  GeneratePmlTable(bw, m, trans_seminaive_naive_pml_table[m], workspace);
510  }
511 
512  for (int i = m + 1; i < bw; ++i) {
513  trans_seminaive_naive_pml_table[i] = trans_seminaive_naive_pml_table[i - 1] + (2 * bw * (bw - (i - 1)));
514  GeneratePmlTable(bw, i, trans_seminaive_naive_pml_table[i], workspace);
515  }
516 
517  return trans_seminaive_naive_pml_table;
518 }
SemiNaive_Naive_Pml_Table
double ** SemiNaive_Naive_Pml_Table(const int bw, const int m, double *resultspace, double *workspace)
Definition: cospml.c:451
BW_LIMIT
const int BW_LIMIT
Limit for bw is used in functions which calculate table size.
Definition: cospml.c:25
RowSize
int RowSize(const int m, const int l)
Definition: cospml.c:250
Pmm_L2
void Pmm_L2(const int, double *, const int, double *)
Generates L2-normed Pmm.
Definition: pmm.c:21
L2_cn
double L2_cn(const int m, const int l)
Definition: l2_norms.c:28
TableSize
int TableSize(const int m, const int bw)
Computes the number of non-zero entries of coeffs in a table for seminaive transform.
Definition: cospml.c:39
Spharmonic_Pml_Table
double ** Spharmonic_Pml_Table(const int bw, double *resultspace, double *workspace)
Definition: cospml.c:387
L2_an
double L2_an(const int m, const int l)
Definition: l2_norms.c:16
chebyshev_nodes.h
GenerateCosPmlTable
void GenerateCosPmlTable(const int bw, const int m, double *tablespace, double *workspace)
Definition: cospml.c:161
Transpose_RowSize
int Transpose_RowSize(const int row, const int m, const int bw)
Definition: cospml.c:270
vector_funcs.h
Reduced_Naive_TableSize
int Reduced_Naive_TableSize(const int bw, const int m)
Definition: cospml.c:434
Spharmonic_TableSize
int Spharmonic_TableSize(const int bw)
Returns the amount of space needed for an entire spharmonic table.
Definition: cospml.c:85
vec_dot
void vec_dot(double *v1, double *v2, double *result, const int len)
Performs dot product of v1 and v2.
Definition: vector_funcs.c:53
vec_mul
void vec_mul(const double scalar, double *v, double *result, const int len)
Multiplies the vector v by scalar.
Definition: vector_funcs.c:33
AcosOfChebyshevNodes
void AcosOfChebyshevNodes(const int, double *)
Generates an array of the angular arguments of n Chebyshev nodes.
Definition: chebyshev_nodes.c:16
ChebyshevNodes
void ChebyshevNodes(const int, double *)
Generates an array of n Chebyshev nodes.
Definition: chebyshev_nodes.c:29
cospml.h
l2_norms.h
GeneratePmlTable
void GeneratePmlTable(const int, const int, double *, double *)
Generates all of the Pmls for a specified value of m.
Definition: pml.c:41
TableOffset
int TableOffset(int m, int l)
Definition: cospml.c:123
Transpose_SemiNaive_Naive_Pml_Table
double ** Transpose_SemiNaive_Naive_Pml_Table(double **seminaive_naive_pml_table, const int bw, const int m, double *resultspace, double *workspace)
Definition: cospml.c:490
vec_add
void vec_add(double *v1, double *v2, double *result, const int len)
Adds two vectors into a third one.
Definition: vector_funcs.c:18
pml.h
Reduced_SpharmonicTableSize
int Reduced_SpharmonicTableSize(const int bw, const int m)
Definition: cospml.c:107
TransposeCosPmlTable
void TransposeCosPmlTable(const int bw, const int m, double *cos_pml_table, double *result)
Definition: cospml.c:301
Transpose_Spharmonic_Pml_Table
double ** Transpose_Spharmonic_Pml_Table(double **spharmonic_pml_table, const int bw, double *resultspace)
Definition: cospml.c:415
pmm.h