LALPulsar  6.1.0.1-89842e6
LatticeTiling.c
Go to the documentation of this file.
1 //
2 // Copyright (C) 2007, 2008, 2012, 2014, 2015, 2016, 2017 Karl Wette
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with with program; see the file COPYING. If not, write to the
16 // Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 // MA 02110-1301 USA
18 //
19 
20 #include <config.h>
21 #include <fenv.h>
22 
23 #include <gsl/gsl_math.h>
24 #include <gsl/gsl_blas.h>
25 #include <gsl/gsl_linalg.h>
26 
27 #include <lal/LatticeTiling.h>
28 #include <lal/LALStdio.h>
29 #include <lal/LogPrintf.h>
30 #include <lal/LALHashFunc.h>
31 #include <lal/MetricUtils.h>
32 #include <lal/GSLHelpers.h>
33 
34 #ifdef __GNUC__
35 #define UNUSED __attribute__ ((unused))
36 #else
37 #define UNUSED
38 #endif
39 
40 // Maximum length of arbitrary data
41 #define LT_DATA_MAX_SIZE 65536
42 
43 // Number of cached values which can be stored per dimension
44 #define LT_CACHE_MAX_SIZE 6
45 
46 // Determine if parameter-space bound has strict padding
47 #define STRICT_BOUND_PADDING( b ) \
48  ( ( (b)->lower_bbox_pad == 0 ) && ( (b)->upper_bbox_pad == 0 ) && ( (b)->lower_intp_pad == 0 ) && ( (b)->upper_intp_pad == 0 ) )
49 
50 ///
51 /// Lattice tiling parameter-space bound for one dimension.
52 ///
53 typedef struct tagLT_Bound {
54  char name[32]; ///< Name of the parameter-space dimension
55  bool name_set; ///< True if the name of the parameter-space dimension has been set
56  bool is_tiled; ///< True if the dimension is tiled, false if it is a single point
57  LatticeTilingBound func; ///< Parameter space bound function
58  size_t data_len; ///< Length of arbitrary data describing parameter-space bounds
59  char data_lower[LT_DATA_MAX_SIZE]; ///< Arbitrary data describing lower parameter-space bound
60  char data_upper[LT_DATA_MAX_SIZE]; ///< Arbitrary data describing upper parameter-space bound
61  LatticeTilingBoundCache cache_func; ///< Parameter space bound cache function
62  double lower_bbox_pad; ///< Lower padding as multiple of metric ellipse bounding box
63  double upper_bbox_pad; ///< Upper padding as multiple of metric ellipse bounding box
64  UINT4 lower_intp_pad; ///< Lower padding as integer number of points
65  UINT4 upper_intp_pad; ///< Upper padding as integer number of points
66  bool find_bound_extrema; ///< Whether to find the extrema of the parameter-space bounds
67 } LT_Bound;
68 
69 ///
70 /// Lattice tiling callback function and associated data
71 ///
72 typedef struct tagLT_Callback {
73  LatticeTilingCallback func; ///< Callback function
74  size_t param_len; ///< Length of arbitrary input data for use by callback function
75  char param[LT_DATA_MAX_SIZE]; ///< Arbitrary input data for use by callback function
76  char out[LT_DATA_MAX_SIZE]; ///< Output data to be filled by callback function
77 } LT_Callback;
78 
79 ///
80 /// FITS record for for saving and restoring a lattice tiling iterator.
81 ///
82 typedef struct tagLT_FITSRecord {
83  INT4 checksum; ///< Checksum of various data describing parameter-space bounds
84  REAL8 phys_point; ///< Current lattice point in physical coordinates
85  INT4 int_point; ///< Current lattice point in generating integers
86  INT4 int_lower; ///< Current lower parameter-space bound in generating integers
87  INT4 int_upper; ///< Current upper parameter-space bound in generating integers
88  INT4 direction; ///< Direction of iteration in each tiled parameter-space dimension
90 
91 ///
92 /// Lattice tiling index trie for one dimension.
93 ///
94 typedef struct tagLT_IndexTrie LT_IndexTrie;
96  INT4 int_lower; ///< Lower integer point bound in this dimension
97  INT4 int_upper; ///< Upper integer point bound in this dimension
98  UINT8 index; ///< Sequential lattice tiling index up to this dimension
99  LT_IndexTrie *next; ///< Pointer to array of index tries for the next-highest dimension
100 };
101 
103  size_t ndim; ///< Number of parameter-space dimensions
104  LT_Bound *bounds; ///< Array of parameter-space bound info for each dimension
105  size_t tiled_ndim; ///< Number of tiled parameter-space dimensions
106  size_t *tiled_idx; ///< Index to tiled parameter-space dimensions
107  TilingLattice lattice; ///< Type of lattice to generate tiling with
108  gsl_vector *phys_bbox; ///< Metric ellipse bounding box
109  gsl_vector *phys_origin; ///< Parameter-space origin in physical coordinates
110  gsl_vector *phys_origin_shift_frac; ///< Fraction of step size to shift physical parameter-space origin
111  gsl_matrix *int_from_phys; ///< Transform to generating integers from physical coordinates
112  gsl_matrix *phys_from_int; ///< Transform to physical coordinates from generating integers
113  gsl_matrix *tiled_generator; ///< Lattice generator matrix in tiled dimensions
114  size_t ncallback; ///< Number of registered callbacks
115  LT_Callback **callbacks; ///< Registered callbacks
116  size_t *ncallback_done; ///< Pointer to number of successfully performed callbacks (mutable)
117  const LatticeTilingStats *stats; ///< Lattice tiling statistics computed by default callback
118 };
119 
121  const LatticeTiling *tiling; ///< Lattice tiling
122  size_t itr_ndim; ///< Number of parameter-space dimensions to iterate over
123  size_t tiled_itr_ndim; ///< Number of tiled parameter-space dimensions to iterate over
124  bool alternating; ///< If true, alternate iterator direction after every crossing
125  UINT4 state; ///< Iterator state: 0=initialised, 1=in progress, 2=finished
126  gsl_vector *phys_point; ///< Current lattice point in physical coordinates
127  gsl_matrix *phys_point_cache; ///< Cached values for computing physical bounds on current point
128  gsl_vector *phys_sampl; ///< Copy of physical point for sampling bounds with LT_FindBoundExtrema()
129  gsl_matrix *phys_sampl_cache; ///< Cached values for sampling bounds with LT_FindBoundExtrema()
130  INT4 *int_point; ///< Current lattice point in generating integers
131  INT4 *int_lower; ///< Current lower parameter-space bound in generating integers
132  INT4 *int_upper; ///< Current upper parameter-space bound in generating integers
133  INT4 *direction; ///< Direction of iteration in each tiled parameter-space dimension
134  UINT8 index; ///< Index of current lattice tiling point
135 };
136 
138  const LatticeTiling *tiling; ///< Lattice tiling
139  size_t ndim; ///< Number of parameter-space dimensions
140  size_t tiled_ndim; ///< Number of tiled parameter-space dimensions
141  LT_IndexTrie *index_trie; ///< Trie for locating unique index of nearest point
142 };
143 
145  { TILING_LATTICE_CUBIC, "Zn" },
146  { TILING_LATTICE_CUBIC, "cubic" },
147  { TILING_LATTICE_ANSTAR, "Ans" },
148  { TILING_LATTICE_ANSTAR, "An-star" },
149  { TILING_LATTICE_ANSTAR, "optimal" },
150 };
151 
153 
154 ///
155 /// Zero out the strictly upper triangular part of the matrix \c A.
156 ///
157 static void LT_ZeroStrictUpperTriangle( gsl_matrix *A )
158 {
159  for ( size_t i = 0; i < A->size1; ++i ) {
160  for ( size_t j = i + 1; j < A->size2; ++j ) {
161  gsl_matrix_set( A, i, j, 0.0 );
162  }
163  }
164 }
165 
166 ///
167 /// Reverse the order of both the rows and columns of the matrix \c A.
168 ///
169 static void LT_ReverseOrderRowsCols( gsl_matrix *A )
170 {
171  for ( size_t i = 0; i < A->size1 / 2; ++i ) {
172  gsl_matrix_swap_rows( A, i, A->size1 - i - 1 );
173  }
174  for ( size_t j = 0; j < A->size2 / 2; ++j ) {
175  gsl_matrix_swap_columns( A, j, A->size2 - j - 1 );
176  }
177 }
178 
179 ///
180 /// Call the parameter-space bound function of a given dimension.
181 ///
182 static inline void LT_CallBoundFunc(
183  const LatticeTiling *tiling, ///< [in] Lattice tiling
184  const size_t dim, ///< [in] Dimension on which bound applies
185  const gsl_matrix *phys_point_cache, ///< [in] Cached values for computing physical point bounds
186  const gsl_vector *phys_point, ///< [in] Physical point at which to find bounds
187  double *phys_lower, ///< [out] Lower parameter-space bound
188  double *phys_upper ///< [out] Upper parameter-space bound
189 )
190 {
191 
192  // Get bound information for this dimension
193  const LT_Bound *bound = &tiling->bounds[dim];
194 
195  // Get view of first 'dim' rows of cache
196  gsl_matrix_const_view phys_point_cache_subm_view = gsl_matrix_const_submatrix( phys_point_cache, 0, 0, GSL_MAX( 1, dim ), phys_point_cache->size2 );
197  const gsl_matrix *phys_point_cache_subm = ( dim == 0 ) ? NULL : &phys_point_cache_subm_view.matrix;
198 
199  // Get view of first 'dim' dimensions of physical point
200  gsl_vector_const_view phys_point_subv_view = gsl_vector_const_subvector( phys_point, 0, GSL_MAX( 1, dim ) );
201  const gsl_vector *phys_point_subv = ( dim == 0 ) ? NULL : &phys_point_subv_view.vector;
202 
203  // Get lower parameter-space bound
204  *phys_lower = ( bound->func )( ( const void * ) bound->data_lower, dim, phys_point_cache_subm, phys_point_subv );
205 
206  if ( bound->is_tiled ) {
207 
208  // Get upper parameter-space bound
209  *phys_upper = ( bound->func )( ( const void * ) bound->data_upper, dim, phys_point_cache_subm, phys_point_subv );
210 
211  // Do not allow upper parameter-space bound to be less than lower parameter-space bound
212  if ( *phys_upper < *phys_lower ) {
213  *phys_upper = *phys_lower;
214  }
215 
216  } else if ( phys_upper != NULL ) {
217 
218  // Set upper bound to lower bound
219  *phys_upper = *phys_lower;
220 
221  }
222 
223 }
224 
225 ///
226 /// Set value of physical point in a given dimension, and update cache
227 ///
228 static inline void LT_SetPhysPoint(
229  const LatticeTiling *tiling, ///< [in] Lattice tiling
230  gsl_matrix *phys_point_cache, ///< [out] Cached values for computing physical point bounds
231  gsl_vector *phys_point, ///< [out] Physical point
232  const size_t dim, ///< [in] Dimension on which to set point
233  const double phys_point_dim ///< [in] Value of physical point in this dimension
234 )
235 {
236 
237  // Get bound information for this dimension
238  const LT_Bound *bound = &tiling->bounds[dim];
239 
240  // Set physical point
241  gsl_vector_set( phys_point, dim, phys_point_dim );
242 
243  if ( bound->cache_func != NULL ) {
244 
245  // Get view of 'dim'th row of cache
246  gsl_vector_view phys_point_cache_subv_view = gsl_matrix_row( phys_point_cache, dim );
247  gsl_vector *phys_point_cache_subv = &phys_point_cache_subv_view.vector;
248 
249  // Get view of first 'dim+1' dimensions of physical point
250  gsl_vector_const_view phys_point_subv_view = gsl_vector_const_subvector( phys_point, 0, dim + 1 );
251  const gsl_vector *phys_point_subv = &phys_point_subv_view.vector;
252 
253  // Update cache values required by bound functions
254  ( bound->cache_func )( dim, phys_point_subv, phys_point_cache_subv );
255 
256  }
257 
258 }
259 
260 ///
261 /// Find the extrema of the parameter-space bounds, by sampling the bounds around the current point.
262 ///
264  const LatticeTiling *tiling, ///< [in] Lattice tiling
265  const size_t i, ///< [in] Current dimension in LT_FindBoundExtrema() iteration
266  const size_t dim, ///< [in] Dimension on which bound applies
267  gsl_matrix *phys_point_cache, ///< [in] Cached values for computing physical point bounds
268  gsl_vector *phys_point, ///< [in] Physical point at which to find bounds
269  double *phys_lower_minimum, ///< [out] Minimum lower parameter-space bound
270  double *phys_upper_maximum ///< [out] Maximum upper parameter-space bound
271 )
272 {
273 
274  // Get bound information for this dimension
275  const LT_Bound *bound = &tiling->bounds[i];
276 
277  // If 'i' equals target dimension 'dim', get parameter-space bounds in this dimension
278  if ( i == dim ) {
279  LT_CallBoundFunc( tiling, dim, phys_point_cache, phys_point, phys_lower_minimum, phys_upper_maximum );
280  return;
281  }
282 
283  // Move to higher dimensions if this dimension is not tiled
284  const double phys_point_i = gsl_vector_get( phys_point, i );
285  if ( !bound->is_tiled ) {
286  LT_SetPhysPoint( tiling, phys_point_cache, phys_point, i, phys_point_i );
287  LT_FindBoundExtrema( tiling, i + 1, dim, phys_point_cache, phys_point, phys_lower_minimum, phys_upper_maximum );
288  return;
289  }
290 
291  // Sample parameter-space bounds at +/0/- half the lattice tiling step size
292  const double phys_hstep_i = 0.5 * gsl_matrix_get( tiling->phys_from_int, i, i );
293  const double phys_point_sample_i[] = {
294  phys_point_i - phys_hstep_i,
295  phys_point_i + phys_hstep_i,
296  phys_point_i // Must be last to reset physical point to original value
297  };
298  for ( size_t j = 0; j < XLAL_NUM_ELEM( phys_point_sample_i ); ++j ) {
299  LT_SetPhysPoint( tiling, phys_point_cache, phys_point, i, phys_point_sample_i[j] );
300  double phys_lower = *phys_lower_minimum;
301  double phys_upper = *phys_upper_maximum;
302  LT_FindBoundExtrema( tiling, i + 1, dim, phys_point_cache, phys_point, &phys_lower, &phys_upper );
303  *phys_lower_minimum = GSL_MIN( *phys_lower_minimum, phys_lower );
304  *phys_upper_maximum = GSL_MAX( *phys_upper_maximum, phys_upper );
305  }
306 
307 }
308 
309 ///
310 /// Callback function for computing lattice tiling statistics
311 ///
312 static int LT_StatsCallback(
313  const bool first_call,
314  const LatticeTiling *tiling,
315  const LatticeTilingIterator *itr,
316  const gsl_vector *point,
317  const size_t changed_i,
318  const void *param UNUSED,
319  void *out
320 )
321 {
322 
324 
325  const size_t n = tiling->ndim;
326 
327  // Initialise statistics
328  if ( first_call ) {
329  for ( size_t i = 0; i < n; ++i ) {
330  INT4 left = 0, right = 0;
331  XLAL_CHECK( XLALCurrentLatticeTilingBlock( itr, i, &left, &right ) == XLAL_SUCCESS, XLAL_EFUNC );
332  const UINT4 num_points = right - left + 1;
333  stats[i].name = XLALLatticeTilingBoundName( tiling, i );
334  stats[i].total_points = 0;
335  stats[i].min_points = num_points;
336  stats[i].max_points = num_points;
337  stats[i].min_value = gsl_vector_get( point, i );
338  stats[i].max_value = gsl_vector_get( point, i );
339  }
340  }
341 
342  // Update statistics
343  for ( size_t i = changed_i; i < n; ++i ) {
344  INT4 left = 0, right = 0;
345  XLAL_CHECK( XLALCurrentLatticeTilingBlock( itr, i, &left, &right ) == XLAL_SUCCESS, XLAL_EFUNC );
346  const UINT4 num_points = right - left + 1;
347  if ( i + 1 == n ) {
348  stats[i].total_points += num_points;
349  } else if ( i >= changed_i ) {
350  stats[i].total_points += 1;
351  }
352  stats[i].min_points = GSL_MIN( stats[i].min_points, num_points );
353  stats[i].max_points = GSL_MAX( stats[i].max_points, num_points );
354  const double step = XLALLatticeTilingStepSize( tiling, i );
355  stats[i].min_value = GSL_MIN( stats[i].min_value, gsl_vector_get( point, i ) + left * step );
356  stats[i].max_value = GSL_MAX( stats[i].max_value, gsl_vector_get( point, i ) + right * step );
357  }
358 
359  return XLAL_SUCCESS;
360 
361 }
362 
363 ///
364 /// Initialise FITS table for saving and restoring a lattice tiling iterator
365 ///
367 {
375  return XLAL_SUCCESS;
376 }
377 
378 ///
379 /// Free memory pointed to by an index trie. The trie itself should be freed by the caller.
380 ///
381 static void LT_FreeIndexTrie(
382  LT_IndexTrie *trie ///< [in] Pointer to array of index tries
383 )
384 {
385  LT_IndexTrie *next = trie->next;
386  if ( next != NULL ) {
387  for ( INT4 i = trie->int_lower; i <= trie->int_upper; ++i ) {
388  LT_FreeIndexTrie( next++ );
389  }
390  XLALFree( trie->next );
391  }
392 }
393 
394 ///
395 /// Find the nearest point within the parameter-space bounds of the lattice tiling, by polling
396 /// the neighbours of an 'original' nearest point found by LT_FindNearestPoints().
397 ///
398 static void LT_PollIndexTrie(
399  const LatticeTiling *tiling, ///< [in] Lattice tiling
400  const LT_IndexTrie *trie, ///< [in] Lattice tiling index trie
401  const size_t ti, ///< [in] Current depth of the trie
402  const gsl_vector *point_int, ///< [in] Original point in generating integers
403  INT4 *poll_nearest, ///< [in] Neighbouring point currently being polled
404  double *poll_min_distance, ///< [in] Minimum distance to neighbouring point found so far
405  INT4 *nearest ///< [in] New nearest point found by polling
406 )
407 {
408 
409  const size_t n = tiling->ndim;
410  const size_t tn = tiling->tiled_ndim;
411 
412  // Get integer lower and upper bounds
413  const INT4 int_lower = trie->int_lower;
414  const INT4 int_upper = trie->int_upper;
415 
416  // Poll points within 1 of original nearest point, but within bounds
417  const size_t i = tiling->tiled_idx[ti];
418  const double point_int_i = gsl_vector_get( point_int, i );
419  const INT4 poll_lower = GSL_MAX( int_lower, GSL_MIN( floor( point_int_i ) - 1, int_upper ) );
420  const INT4 poll_upper = GSL_MAX( int_lower, GSL_MIN( ceil( point_int_i ) + 1, int_upper ) );
421 
422  for ( poll_nearest[i] = poll_lower; poll_nearest[i] <= poll_upper; ++poll_nearest[i] ) {
423 
424  // Continue polling in higher dimensions
425  if ( ti + 1 < tn ) {
426  const LT_IndexTrie *next = &trie->next[poll_nearest[i] - trie->int_lower];
427  LT_PollIndexTrie( tiling, next, ti + 1, point_int, poll_nearest, poll_min_distance, nearest );
428  continue;
429  }
430 
431  // Compute distance between original and poll point with respect to lattice generator
432  double poll_distance = 0;
433  for ( size_t tj = 0; tj < tn; ++tj ) {
434  const size_t j = tiling->tiled_idx[tj];
435  const double diff_j = gsl_vector_get( point_int, j ) - poll_nearest[j];
436  for ( size_t tk = 0; tk < tn; ++tk ) {
437  const size_t k = tiling->tiled_idx[tk];
438  const double diff_k = gsl_vector_get( point_int, k ) - poll_nearest[k];
439  const double generator_j_k = gsl_matrix_get( tiling->tiled_generator, tj, tk );
440  poll_distance += generator_j_k * diff_j * diff_k;
441  }
442  }
443 
444  // If distance is smaller than minimum distance, record current poll point as nearest
445  if ( poll_distance < *poll_min_distance ) {
446  *poll_min_distance = poll_distance;
447  memcpy( nearest, poll_nearest, n * sizeof( nearest[0] ) );
448  }
449 
450  }
451 
452 }
453 
454 ///
455 /// Print one level of a lattice tiling index trie.
456 ///
457 static void LT_PrintIndexTrie(
458  const LatticeTiling *tiling, ///< [in] Lattice tiling
459  const LT_IndexTrie *trie, ///< [in] Lattice tiling index trie
460  const size_t ti, ///< [in] Current depth of the trie
461  FILE *file, ///< [in] File pointer to print trie to
462  INT4 int_lower[] ///< [in] Current integer lower bound
463 )
464 {
465 
466  const size_t tn = tiling->tiled_ndim;
467 
468  // Print indentation
469  for ( size_t s = 0; s <= ti; ++s ) {
470  fprintf( file, " " );
471  }
472 
473  // Return if 'trie' is NULL (which should never happen)
474  if ( trie == NULL ) {
475  fprintf( file, "ERROR: 'trie' is NULL\n" );
476  return;
477  }
478 
479  // Set 'i'th integer lower bound to 'trie' lower bound, then
480  // transform to physical coordinates from generating integers
481  int_lower[ti] = trie->int_lower;
482  const size_t i = tiling->tiled_idx[ti];
483  double phys_lower = gsl_vector_get( tiling->phys_origin, i );
484  for ( size_t tj = 0; tj < tn; ++tj ) {
485  const size_t j = tiling->tiled_idx[tj];
486  const double phys_from_int_i_j = gsl_matrix_get( tiling->phys_from_int, i, j );
487  phys_lower += phys_from_int_i_j * int_lower[tj];
488  }
489 
490  // Calculate physical upper bound from physical lower_bound
491  const double phys_from_int_i_i = gsl_matrix_get( tiling->phys_from_int, i, i );
492  double phys_upper = phys_lower + phys_from_int_i_i * ( trie->int_upper - trie->int_lower );
493 
494  // Print information on the current trie trie dimension
495  fprintf( file, "dim: #%zu/%zu int: [%+5" LAL_INT4_FORMAT ",%+5" LAL_INT4_FORMAT "] phys: [%+10g,%+10g] index:%" LAL_UINT8_FORMAT "\n",
496  ti + 1, tn, trie->int_lower, trie->int_upper, phys_lower, phys_upper, trie->index );
497 
498  // If this is not the highest dimension, loop over this dimension
499  LT_IndexTrie *next = trie->next;
500  if ( next != NULL ) {
501  for ( int32_t point = trie->int_lower; point <= trie->int_upper; ++point, ++next ) {
502 
503  // Set 'i'th integer lower bound to this point
504  int_lower[ti] = point;
505 
506  // Print higher dimensions
507  LT_PrintIndexTrie( tiling, next, ti + 1, file, int_lower );
508 
509  }
510  }
511 
512 }
513 
514 ///
515 /// Locate the nearest points in a lattice tiling to a given set of points. Return the nearest
516 /// points in 'nearest_points', and optionally: unique sequential indexes to the nearest points in
517 /// 'nearest_indexes', and indexes of the left/right-most points in the blocks of the nearest points
518 /// relative to the nearest points in 'nearest_left' and 'nearest_right' respectively.
519 ///
521  const LatticeTilingLocator *loc, ///< [in] Lattice tiling locator
522  const gsl_matrix *points, ///< [in] Columns are set of points for which to find nearest points
523  gsl_matrix *nearest_points, ///< [out] Columns are the corresponding nearest points
524  UINT8VectorSequence *nearest_indexes, ///< [out] Vectors are unique sequential indexes of the nearest points
525  INT4VectorSequence *nearest_lefts, ///< [out] Vectors are indexes of left-most points of blocks relative to nearest points
526  INT4VectorSequence *nearest_rights ///< [out] Vectors are indexes of right-most points of blocks relative to nearest points
527 )
528 {
529 
530  // Check input
531  XLAL_CHECK( loc != NULL, XLAL_EFAULT );
532  XLAL_CHECK( points != NULL, XLAL_EFAULT );
533  XLAL_CHECK( points->size1 == loc->ndim, XLAL_EINVAL );
534  XLAL_CHECK( nearest_points != NULL, XLAL_EFAULT );
535  XLAL_CHECK( nearest_points->size1 == loc->ndim, XLAL_EINVAL );
536  XLAL_CHECK( nearest_points->size2 == points->size2, XLAL_EINVAL );
537 
538  const size_t n = loc->ndim;
539  const size_t tn = loc->tiled_ndim;
540  const size_t num_points = points->size2;
541 
542  // Copy 'points' to 'nearest_points'
543  gsl_matrix_memcpy( nearest_points, points );
544 
545  // Transform 'nearest_points' from physical coordinates to generating integers
546  for ( size_t i = 0; i < n; ++i ) {
547  const double phys_origin = gsl_vector_get( loc->tiling->phys_origin, i );
548  gsl_vector_view nearest_points_row = gsl_matrix_row( nearest_points, i );
549  gsl_vector_add_constant( &nearest_points_row.vector, -phys_origin );
550  }
551  gsl_blas_dtrmm( CblasLeft, CblasLower, CblasNoTrans, CblasNonUnit, 1.0, loc->tiling->int_from_phys, nearest_points );
552 
553  // Find the nearest points in the lattice tiling to the points in 'nearest_points'
554  for ( size_t j = 0; j < num_points; ++j ) {
555 
556  // If there are tiled dimensions:
557  INT4 nearest[n];
558  if ( tn > 0 ) {
559 
560  // Find the nearest point to 'nearest_points[:,j]', the tiled dimensions of which are generating integers
561  switch ( loc->tiling->lattice ) {
562 
563  case TILING_LATTICE_CUBIC: // Cubic ( \f$ Z_n \f$ ) lattice
564 
565  {
566 
567  // Round each dimension of 'nearest_points[:,j]' to nearest integer to find the nearest point in Zn
568  feclearexcept( FE_ALL_EXCEPT );
569  for ( size_t ti = 0; ti < tn; ++ti ) {
570  const size_t i = loc->tiling->tiled_idx[ti];
571  nearest[i] = lround( gsl_matrix_get( nearest_points, i, j ) );
572  }
573  if ( fetestexcept( FE_INVALID ) != 0 ) {
574  XLALPrintError( "Rounding failed while finding nearest point #%zu:", j );
575  for ( size_t ti = 0; ti < tn; ++ti ) {
576  const size_t i = loc->tiling->tiled_idx[ti];
577  XLALPrintError( " %0.2e", gsl_matrix_get( nearest_points, i, j ) );
578  }
579  XLALPrintError( "\n" );
581  }
582 
583  }
584  break;
585 
586  case TILING_LATTICE_ANSTAR: // An-star ( \f$ A_n^* \f$ ) lattice
587 
588  {
589 
590  // The nearest point algorithm used below embeds the An* lattice in tn+1 dimensions,
591  // however 'nearest_points[:,j]' has only 'tn' tiled dimensional. The algorithm is only
592  // sensitive to the differences between the 'ti'th and 'ti+1'th dimension, so we can
593  // freely set one of the dimensions to a constant value. We choose to set the 0th
594  // dimension to zero, i.e. the (tn+1)-dimensional lattice point is
595  // y = (0, tiled dimensions of 'nearest_points[:,j]').
596  double y[tn + 1];
597  y[0] = 0;
598  for ( size_t ti = 0; ti < tn; ++ti ) {
599  const size_t i = loc->tiling->tiled_idx[ti];
600  y[ti + 1] = gsl_matrix_get( nearest_points, i, j );
601  }
602 
603  // Find the nearest point in An* to the point 'y', using the O(tn) Algorithm 2 given in:
604  // McKilliam et.al., "A linear-time nearest point algorithm for the lattice An*"
605  // in "International Symposium on Information Theory and Its Applications", ISITA2008,
606  // Auckland, New Zealand, 7-10 Dec. 2008. DOI: 10.1109/ISITA.2008.4895596
607  // Notes:
608  // * Since Algorithm 2 uses 1-based arrays, we have to translate, e.g.:
609  // z_t in paper <---> z[tn-1] in C code
610  // * Line 6 in Algorithm 2 as written in the paper is in error, see correction below.
611  // * We are only interested in 'k', the generating integers of the nearest point
612  // 'x = Q * k', therefore line 26 in Algorithm 2 is not included.
613  INT4 k[tn + 1];
614  {
615 
616  // Lines 1--4, 20
617  double z[tn + 1], alpha = 0, beta = 0;
618  size_t bucket[tn + 1], link[tn + 1];
619  feclearexcept( FE_ALL_EXCEPT );
620  for ( size_t ti = 1; ti <= tn + 1; ++ti ) {
621  k[ti - 1] = lround( y[ti - 1] ); // Line 20, moved here to avoid duplicate round
622  z[ti - 1] = y[ti - 1] - k[ti - 1];
623  alpha += z[ti - 1];
624  beta += z[ti - 1] * z[ti - 1];
625  bucket[ti - 1] = 0;
626  }
627  if ( fetestexcept( FE_INVALID ) != 0 ) {
628  XLALPrintError( "Rounding failed while finding nearest point #%zu:", j );
629  for ( size_t ti = 1; ti <= tn + 1; ++ti ) {
630  XLALPrintError( " %0.2e", y[ti - 1] );
631  }
632  XLALPrintError( "\n" );
634  }
635 
636  // Lines 5--8
637  // Notes:
638  // * Correction to line 6, as as written in McKilliam et.al.:
639  // ti = tn + 1 - (tn + 1)*floor(z_t + 0.5)
640  // should instead read
641  // ti = tn + 1 - floor((tn + 1)*(z_t + 0.5))
642  // * We also convert the floor() operation into an lround():
643  // ti = tn + 1 - lround((tn + 1)*(z_t + 0.5) - 0.5)
644  // to avoid a casting operation. Rewriting the line as:
645  // ti = lround((tn + 1)*(0.5 - z_t) + 0.5)
646  // appears to improve numerical robustness in some cases.
647  // * No floating-point exception checking needed for lround()
648  // here since its argument will be of order 'tn'.
649  for ( size_t tt = 1; tt <= tn + 1; ++tt ) {
650  const INT4 ti = lround( ( tn + 1 ) * ( 0.5 - z[tt - 1] ) + 0.5 );
651  link[tt - 1] = bucket[ti - 1];
652  bucket[ti - 1] = tt;
653  }
654 
655  // Lines 9--10
656  double D = beta - alpha * alpha / ( tn + 1 );
657  size_t tm = 0;
658 
659  // Lines 11--19
660  for ( size_t ti = 1; ti <= tn + 1; ++ti ) {
661  size_t tt = bucket[ti - 1];
662  while ( tt != 0 ) {
663  alpha = alpha - 1;
664  beta = beta - 2 * z[tt - 1] + 1;
665  tt = link[tt - 1];
666  }
667  double d = beta - alpha * alpha / ( tn + 1 );
668  if ( d < D ) {
669  D = d;
670  tm = ti;
671  }
672  }
673 
674  // Lines 21--25
675  for ( size_t ti = 1; ti <= tm; ++ti ) {
676  size_t tt = bucket[ti - 1];
677  while ( tt != 0 ) {
678  k[tt - 1] = k[tt - 1] + 1;
679  tt = link[tt - 1];
680  }
681  }
682 
683  }
684 
685  // The nearest point in An* is the tn differences between k[1]...k[tn] and k[0]
686  for ( size_t ti = 0; ti < tn; ++ti ) {
687  const size_t i = loc->tiling->tiled_idx[ti];
688  nearest[i] = k[ti + 1] - k[0];
689  }
690 
691  }
692  break;
693 
694  default:
695  XLAL_ERROR( XLAL_EFAILED, "Invalid lattice" );
696  }
697 
698  // Bound generating integers
699  {
700  const LT_IndexTrie *trie = loc->index_trie;
701  size_t ti = 0;
702  while ( ti < tn ) {
703  const size_t i = loc->tiling->tiled_idx[ti];
704 
705  // If 'nearest[i]' is outside parameter-space bounds:
706  if ( nearest[i] < trie->int_lower || nearest[i] > trie->int_upper ) {
707  XLALPrintInfo( "%s: failed %" LAL_INT4_FORMAT " <= %" LAL_INT4_FORMAT " <= %" LAL_INT4_FORMAT " in dimension #%zu\n",
708  __func__, trie->int_lower, nearest[i], trie->int_upper, i );
709 
710  // Find the nearest point within the parameter-space bounds of the lattice tiling
711  gsl_vector_view point_int_view = gsl_matrix_column( nearest_points, j );
712  INT4 poll_nearest[n];
713  double poll_min_distance = GSL_POSINF;
714  feclearexcept( FE_ALL_EXCEPT );
715  LT_PollIndexTrie( loc->tiling, loc->index_trie, 0, &point_int_view.vector, poll_nearest, &poll_min_distance, nearest );
716  XLAL_CHECK( fetestexcept( FE_INVALID ) == 0, XLAL_EFAILED, "Rounding failed while calling LT_PollIndexTrie() for nearest point #%zu", j );
717 
718  // Reset 'trie', given that 'nearest' may have changed in any dimension
719  trie = loc->index_trie;
720  ti = 0;
721  continue;
722 
723  }
724 
725  // If we are below the highest dimension, jump to the next dimension based on 'nearest[i]'
726  if ( ti + 1 < tn ) {
727  trie = &trie->next[nearest[i] - trie->int_lower];
728  }
729 
730  ++ti;
731 
732  }
733  }
734 
735  }
736 
737  // Return various outputs
738  {
739  const LT_IndexTrie *trie = loc->index_trie;
740  UINT8 nearest_index = 0;
741  for ( size_t ti = 0, i = 0; i < n; ++i ) {
742  const bool is_tiled = loc->tiling->bounds[i].is_tiled;
743 
744  // Return nearest point
745  if ( is_tiled ) {
746  gsl_matrix_set( nearest_points, i, j, nearest[i] );
747  }
748 
749  // Return sequential indexes of nearest point
750  // - Non-tiled dimensions inherit value of next-lowest dimension
751  if ( is_tiled ) {
752  nearest_index = trie->index + nearest[i] - trie->int_lower;
753  }
754  if ( nearest_indexes != NULL ) {
755  nearest_indexes->data[n * j + i] = nearest_index;
756  }
757 
758  // Return indexes of left/right-most points in block relative to nearest point
759  if ( nearest_lefts != NULL ) {
760  nearest_lefts->data[n * j + i] = is_tiled ? trie->int_lower - nearest[i] : 0;
761  }
762  if ( nearest_rights != NULL ) {
763  nearest_rights->data[n * j + i] = is_tiled ? trie->int_upper - nearest[i] : 0;
764  }
765 
766  // If we are below the highest dimension, jump to the next dimension based on 'nearest[i]'
767  if ( is_tiled ) {
768  if ( ti + 1 < tn ) {
769  trie = &trie->next[nearest[i] - trie->int_lower];
770  }
771  ++ti;
772  }
773 
774  }
775  }
776 
777  }
778 
779  // Transform 'nearest_points' from generating integers to physical coordinates
780  gsl_blas_dtrmm( CblasLeft, CblasLower, CblasNoTrans, CblasNonUnit, 1.0, loc->tiling->phys_from_int, nearest_points );
781  for ( size_t i = 0; i < n; ++i ) {
782  const double phys_origin = gsl_vector_get( loc->tiling->phys_origin, i );
783  gsl_vector_view nearest_points_row = gsl_matrix_row( nearest_points, i );
784  gsl_vector_add_constant( &nearest_points_row.vector, phys_origin );
785  }
786 
787  // Create local cache for computing physical bounds
788  double local_cache_array[n * LT_CACHE_MAX_SIZE];
789  gsl_matrix_view local_cache_view = gsl_matrix_view_array( local_cache_array, n, LT_CACHE_MAX_SIZE );
790  gsl_matrix *local_cache = &local_cache_view.matrix;
791  gsl_matrix_set_all( local_cache, GSL_NAN );
792 
793  // Set any non-tiled dimensions in 'nearest_points'
794  for ( size_t j = 0; j < num_points; ++j ) {
795  gsl_vector_view nearest_points_col = gsl_matrix_column( nearest_points, j );
796  for ( size_t i = 0; i < n; ++i ) {
797  double phys_point = gsl_vector_get( &nearest_points_col.vector, i );
798  if ( !loc->tiling->bounds[i].is_tiled ) {
799  LT_CallBoundFunc( loc->tiling, i, local_cache, &nearest_points_col.vector, &phys_point, NULL );
800  }
801  LT_SetPhysPoint( loc->tiling, local_cache, &nearest_points_col.vector, i, phys_point );
802  }
803  }
804 
805  return XLAL_SUCCESS;
806 
807 }
808 
809 LatticeTiling *XLALCreateLatticeTiling(
810  const size_t ndim
811 )
812 {
813 
814  // Check input
815  XLAL_CHECK_NULL( ndim > 0, XLAL_EINVAL );
816 
817  // Allocate memory
818  LatticeTiling *tiling = XLALCalloc( 1, sizeof( *tiling ) );
819  XLAL_CHECK_NULL( tiling != NULL, XLAL_ENOMEM );
820  tiling->bounds = XLALCalloc( ndim, sizeof( *tiling->bounds ) );
821  XLAL_CHECK_NULL( tiling->bounds != NULL, XLAL_ENOMEM );
822  tiling->ncallback_done = XLALCalloc( 1, sizeof( *tiling->ncallback_done ) );
823  XLAL_CHECK_NULL( tiling->ncallback_done != NULL, XLAL_ENOMEM );
824 
825  // Initialise fields
826  tiling->ndim = ndim;
827  tiling->lattice = TILING_LATTICE_MAX;
828 
829  // Initialise default padding
830  for ( size_t i = 0; i < ndim; ++i ) {
831  XLAL_CHECK_NULL( XLALSetLatticeTilingPadding( tiling, i, -1, -1, -1, -1, -1 ) == XLAL_SUCCESS, XLAL_EFUNC );
832  }
833 
834  // Allocate and initialise vectors and matrices
835  GAVEC_NULL( tiling->phys_bbox, ndim );
836  GAVEC_NULL( tiling->phys_origin, ndim );
837  gsl_vector_set_all( tiling->phys_origin, GSL_NAN );
838  GAMAT_NULL( tiling->int_from_phys, ndim, ndim );
839  gsl_matrix_set_identity( tiling->int_from_phys );
840  GAMAT_NULL( tiling->phys_from_int, ndim, ndim );
841  gsl_matrix_set_identity( tiling->phys_from_int );
842 
843  return tiling;
844 
845 }
846 
848  LatticeTiling *tiling
849 )
850 {
851  if ( tiling != NULL ) {
852  XLALFree( tiling->bounds );
853  XLALFree( tiling->tiled_idx );
854  for ( size_t m = 0; m < tiling->ncallback; ++m ) {
855  XLALFree( tiling->callbacks[m] );
856  }
857  XLALFree( tiling->callbacks );
858  XLALFree( tiling->ncallback_done );
859  GFMAT( tiling->int_from_phys, tiling->phys_from_int, tiling->tiled_generator );
860  GFVEC( tiling->phys_bbox, tiling->phys_origin, tiling->phys_origin_shift_frac );
861  XLALFree( tiling );
862  }
863 }
864 
866  LatticeTiling *tiling,
867  const size_t dim,
868  const LatticeTilingBound func,
869  const size_t data_len,
870  const void *data_lower,
871  const void *data_upper
872 )
873 {
874 
875  // Check input
876  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
877  XLAL_CHECK( tiling->lattice == TILING_LATTICE_MAX, XLAL_EINVAL );
878  XLAL_CHECK( dim < tiling->ndim, XLAL_ESIZE );
879  XLAL_CHECK( func != NULL, XLAL_EFAULT );
880  XLAL_CHECK( data_len > 0, XLAL_EFAULT );
881  XLAL_CHECK( data_len < LT_DATA_MAX_SIZE, XLAL_EFAULT, "Arbitrary data is too long" );
882  XLAL_CHECK( data_lower != NULL, XLAL_EFAULT );
883  XLAL_CHECK( data_upper != NULL, XLAL_EFAULT );
884 
885  // Check that bound has not already been set
886  XLAL_CHECK( tiling->bounds[dim].func == NULL, XLAL_EINVAL, "Lattice tiling dimension #%zu is already bounded", dim );
887 
888  // Determine if this dimension is tiled
889  const BOOLEAN is_tiled = ( memcmp( data_lower, data_upper, data_len ) != 0 ) ? 1 : 0;
890 
891  // Set the parameter-space bound
892  tiling->bounds[dim].is_tiled = is_tiled;
893  tiling->bounds[dim].func = func;
894  tiling->bounds[dim].data_len = data_len;
895  memcpy( tiling->bounds[dim].data_lower, data_lower, data_len );
896  memcpy( tiling->bounds[dim].data_upper, data_upper, data_len );
897 
898  // Set a default parameter-space bound name, if none has yet been set
899  if ( !tiling->bounds[dim].name_set ) {
900  XLAL_CHECK( XLALSetLatticeTilingBoundName( tiling, dim, "dimension #%zu", dim ) == XLAL_SUCCESS, XLAL_EFUNC );
901  }
902 
903  return XLAL_SUCCESS;
904 
905 }
906 
908  LatticeTiling *tiling,
909  const size_t dim,
910  const char *fmt,
911  ...
912 )
913 {
914 
915  // Check input
916  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
917  XLAL_CHECK( tiling->lattice == TILING_LATTICE_MAX, XLAL_EINVAL );
918  XLAL_CHECK( dim < tiling->ndim, XLAL_ESIZE );
919  XLAL_CHECK( fmt != NULL, XLAL_EFAULT );
920 
921  // Check that bound has not already been named
922  XLAL_CHECK( !tiling->bounds[dim].name_set, XLAL_EINVAL, "Lattice tiling dimension #%zu is already named", dim );
923 
924  // Set the parameter-space bound name
925  va_list ap;
926  va_start( ap, fmt );
927  const int retn = vsnprintf( tiling->bounds[dim].name, sizeof( tiling->bounds[dim].name ), fmt, ap );
928  va_end( ap );
929  XLAL_CHECK( retn < ( int ) sizeof( tiling->bounds[dim].name ), XLAL_EINVAL, "Name '%s' for lattice tiling dimension #%zu was truncated", tiling->bounds[dim].name, dim );
930  tiling->bounds[dim].name_set = true;
931 
932  return XLAL_SUCCESS;
933 
934 }
935 
937  LatticeTiling *tiling,
938  const size_t dim,
939  const LatticeTilingBoundCache func
940 )
941 {
942 
943  // Check input
944  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
945  XLAL_CHECK( tiling->lattice == TILING_LATTICE_MAX, XLAL_EINVAL );
946  XLAL_CHECK( dim < tiling->ndim, XLAL_ESIZE );
947  XLAL_CHECK( func != NULL, XLAL_EFAULT );
948 
949  // Check that bound has been set
950  XLAL_CHECK( tiling->bounds[dim].func != NULL, XLAL_EINVAL, "Lattice tiling dimension #%zu is not bounded", dim );
951 
952  // Set the parameter-space bound cache function
953  tiling->bounds[dim].cache_func = func;
954 
955  return XLAL_SUCCESS;
956 
957 }
958 
959 static double ConstantBound(
960  const void *data,
961  const size_t dim UNUSED,
962  const gsl_matrix *cache UNUSED,
963  const gsl_vector *point UNUSED
964 )
965 {
966 
967  // Return bound
968  return *( ( const double * ) data );
969 
970 }
971 
973  LatticeTiling *tiling,
974  const size_t dim,
975  const double bound1,
976  const double bound2
977 )
978 {
979 
980  // Check input
981  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
982  XLAL_CHECK( isfinite( bound1 ), XLAL_EINVAL );
983  XLAL_CHECK( isfinite( bound2 ), XLAL_EINVAL );
984 
985  // Set the parameter-space bound
986  const double data_lower = GSL_MIN( bound1, bound2 );
987  const double data_upper = GSL_MAX( bound1, bound2 );
988  XLAL_CHECK( XLALSetLatticeTilingBound( tiling, dim, ConstantBound, sizeof( data_lower ), &data_lower, &data_upper ) == XLAL_SUCCESS, XLAL_EFUNC );
989 
990  return XLAL_SUCCESS;
991 
992 }
993 
995  LatticeTiling *tiling,
996  const size_t dim,
997  const double lower_bbox_pad,
998  const double upper_bbox_pad,
999  const int lower_intp_pad,
1000  const int upper_intp_pad,
1001  const int find_bound_extrema
1002 )
1003 {
1004 
1005  // Check input
1006  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
1007  XLAL_CHECK( tiling->lattice == TILING_LATTICE_MAX, XLAL_EINVAL );
1008  XLAL_CHECK( dim < tiling->ndim, XLAL_ESIZE );
1009 
1010  // Set parameter-space padding
1011  tiling->bounds[dim].lower_bbox_pad = lower_bbox_pad < 0 ? 0.5 : lower_bbox_pad;
1012  tiling->bounds[dim].upper_bbox_pad = upper_bbox_pad < 0 ? 0.5 : upper_bbox_pad;
1013  tiling->bounds[dim].lower_intp_pad = lower_intp_pad < 0 ? 0 : lower_intp_pad;
1014  tiling->bounds[dim].upper_intp_pad = upper_intp_pad < 0 ? 0 : upper_intp_pad;
1015 
1016  // Set whether to find the extrema of the parameter-space bounds
1017  tiling->bounds[dim].find_bound_extrema = find_bound_extrema < 0 ? true : ( find_bound_extrema ? true : false );
1018 
1019  return XLAL_SUCCESS;
1020 
1021 }
1022 
1024  LatticeTiling *tiling,
1025  const size_t dim,
1026  const double origin
1027 )
1028 {
1029 
1030  // Check input
1031  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
1032  XLAL_CHECK( tiling->lattice == TILING_LATTICE_MAX, XLAL_EINVAL );
1033  XLAL_CHECK( dim < tiling->ndim, XLAL_ESIZE );
1034  XLAL_CHECK( isfinite( origin ), XLAL_EINVAL );
1035 
1036  // Set physical parameter-space origin
1037  gsl_vector_set( tiling->phys_origin, dim, origin );
1038 
1039  return XLAL_SUCCESS;
1040 
1041 }
1042 
1044  LatticeTiling *tiling,
1045  RandomParams *rng
1046 )
1047 {
1048 
1049  // Check input
1050  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
1051  XLAL_CHECK( tiling->lattice == TILING_LATTICE_MAX, XLAL_EINVAL );
1052  XLAL_CHECK( rng != NULL, XLAL_EFAULT );
1053 
1054  const size_t n = tiling->ndim;
1055 
1056  // Allocate memory
1057  GAVEC( tiling->phys_origin_shift_frac, n );
1058 
1059  // Generate random uniform offsets for later use in XLALSetTilingLatticeAndMetric()
1060  // - Only values in tiled dimensions of 'phys_origin_shift_frac' will actually be used
1061  for ( size_t i = 0; i < n; ++i ) {
1062  gsl_vector_set( tiling->phys_origin_shift_frac, i, XLALUniformDeviate( rng ) );
1063  }
1064 
1065  return XLAL_SUCCESS;
1066 
1067 }
1068 
1070  LatticeTiling *tiling,
1071  const LatticeTiling *ref_tiling
1072 )
1073 {
1074 
1075  // Check input
1076  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
1077  XLAL_CHECK( tiling->lattice == TILING_LATTICE_MAX, XLAL_EINVAL );
1078  XLAL_CHECK( ref_tiling != NULL, XLAL_EFAULT );
1079  XLAL_CHECK( ref_tiling->lattice < TILING_LATTICE_MAX, XLAL_EINVAL );
1080  XLAL_CHECK( tiling->ndim == ref_tiling->ndim, XLAL_EINVAL );
1081 
1082  const size_t n = tiling->ndim;
1083 
1084  // Check that all parameter-space dimensions are bounded
1085  for ( size_t i = 0; i < n; ++i ) {
1086  XLAL_CHECK( tiling->bounds[i].func != NULL, XLAL_EFAILED, "Lattice tiling dimension #%zu is unbounded", i );
1087  }
1088 
1089  // Set the tiled dimensions of 'tiling' to match 'ref_tiling'
1090  for ( size_t i = 0; i < n; ++i ) {
1091  tiling->bounds[i].is_tiled = ref_tiling->bounds[i].is_tiled;
1092  }
1093 
1094  return XLAL_SUCCESS;
1095 
1096 }
1097 
1099  LatticeTiling *tiling,
1100  const TilingLattice lattice,
1101  const gsl_matrix *metric,
1102  const double max_mismatch
1103 )
1104 {
1105 
1106  // Check input
1107  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
1108  XLAL_CHECK( tiling->lattice == TILING_LATTICE_MAX, XLAL_EINVAL );
1110  XLAL_CHECK( metric != NULL, XLAL_EFAULT );
1111  XLAL_CHECK( metric->size1 == tiling->ndim && metric->size2 == tiling->ndim, XLAL_EINVAL );
1112  XLAL_CHECK( max_mismatch > 0, XLAL_EINVAL );
1113 
1114  const size_t n = tiling->ndim;
1115 
1116  // Check that all parameter-space dimensions are bounded
1117  for ( size_t i = 0; i < n; ++i ) {
1118  XLAL_CHECK( tiling->bounds[i].func != NULL, XLAL_EFAILED, "Lattice tiling dimension #%zu is unbounded", i );
1119  }
1120 
1121  // Check metric is symmetric and has positive diagonal elements
1122  for ( size_t i = 0; i < n; ++i ) {
1123  XLAL_CHECK( gsl_matrix_get( metric, i, i ) > 0, XLAL_EINVAL, "Parameter-space metric(%zu,%zu) <= 0", i, i );
1124  for ( size_t j = i + 1; j < n; ++j ) {
1125  XLAL_CHECK( gsl_matrix_get( metric, i, j ) == gsl_matrix_get( metric, j, i ), XLAL_EINVAL, "Parameter-space metric(%zu,%zu) != metric(%zu,%zu)", i, j, j, i );
1126  }
1127  }
1128 
1129  // Set type of lattice to generate tiling with
1130  tiling->lattice = lattice;
1131 
1132  // Set physical parameter-space origin to mid-point of parameter-space bounds
1133  gsl_matrix *GAMAT( phys_origin_cache, n, LT_CACHE_MAX_SIZE );
1134  gsl_matrix_set_all( phys_origin_cache, GSL_NAN );
1135  for ( size_t i = 0; i < n; ++i ) {
1136  double phys_origin_i = gsl_vector_get( tiling->phys_origin, i );
1137  if ( !isfinite( phys_origin_i ) ) {
1138  double phys_lower = 0.0, phys_upper = 0.0;
1139  LT_CallBoundFunc( tiling, i, phys_origin_cache, tiling->phys_origin, &phys_lower, &phys_upper );
1140  phys_origin_i = 0.5 * ( phys_lower + phys_upper );
1141  }
1142  LT_SetPhysPoint( tiling, phys_origin_cache, tiling->phys_origin, i, phys_origin_i );
1143  }
1144 
1145  // Register default statistics callback function
1146  tiling->stats = XLALRegisterLatticeTilingCallback( tiling, LT_StatsCallback, 0, NULL, tiling->ndim * sizeof( *tiling->stats ) );
1147  XLAL_CHECK( tiling->stats != NULL, XLAL_EFUNC );
1148 
1149  // Count number of tiled dimensions; if no parameter-space dimensions are tiled, we're done
1150  tiling->tiled_ndim = 0;
1151  for ( size_t i = 0; i < n; ++i ) {
1152  if ( tiling->bounds[i].is_tiled ) {
1153  ++tiling->tiled_ndim;
1154  }
1155  }
1156  if ( tiling->tiled_ndim == 0 ) {
1157  return XLAL_SUCCESS;
1158  }
1159 
1160  const size_t tn = tiling->tiled_ndim;
1161 
1162  // Build index to tiled parameter-space dimensions
1163  tiling->tiled_idx = XLALCalloc( tn, sizeof( *tiling->tiled_idx ) );
1164  XLAL_CHECK( tiling->tiled_idx != NULL, XLAL_ENOMEM );
1165  for ( size_t i = 0, ti = 0; i < n; ++i ) {
1166  if ( tiling->bounds[i].is_tiled ) {
1167  tiling->tiled_idx[ti++] = i;
1168  }
1169  }
1170 
1171  // Calculate normalisation scale from metric diagonal elements
1172  gsl_vector *GAVEC( t_norm, tn );
1173  for ( size_t ti = 0; ti < tn; ++ti ) {
1174  const size_t i = tiling->tiled_idx[ti];
1175  const double metric_i_i = gsl_matrix_get( metric, i, i );
1176  gsl_vector_set( t_norm, ti, sqrt( metric_i_i ) );
1177  }
1178 
1179  // Copy and normalise tiled dimensions of metric
1180  gsl_matrix *GAMAT( t_metric, tn, tn );
1181  for ( size_t ti = 0; ti < tn; ++ti ) {
1182  const size_t i = tiling->tiled_idx[ti];
1183  const double t_norm_ti = gsl_vector_get( t_norm, ti );
1184  for ( size_t tj = 0; tj < tn; ++tj ) {
1185  const size_t j = tiling->tiled_idx[tj];
1186  const double t_norm_tj = gsl_vector_get( t_norm, tj );
1187  gsl_matrix_set( t_metric, ti, tj, gsl_matrix_get( metric, i, j ) / t_norm_ti / t_norm_tj );
1188  }
1189  }
1190 
1191  // Compute metric ellipse bounding box
1192  gsl_vector *t_bbox = XLALMetricEllipseBoundingBox( t_metric, max_mismatch );
1193  XLAL_CHECK( t_bbox != NULL, XLAL_EFUNC );
1194 
1195  // Copy bounding box in physical coordinates to tiled dimensions
1196  for ( size_t ti = 0; ti < tn; ++ti ) {
1197  const size_t i = tiling->tiled_idx[ti];
1198  const double t_norm_ti = gsl_vector_get( t_norm, ti );
1199  gsl_vector_set( tiling->phys_bbox, i, gsl_vector_get( t_bbox, ti ) / t_norm_ti );
1200  }
1201 
1202  // Compute a lower-triangular basis matrix whose columns are orthonormal with respect to the tiled metric
1203  gsl_matrix *GAMAT( t_basis, tn, tn );
1204  {
1205  // We want to find a lower-triangular basis such that:
1206  // basis^T * metric * basis = I
1207  // This is rearranged to give:
1208  // metric^-1 = basis * basis^T
1209  // Hence basis is the Cholesky decomposition of metric^-1
1210  gsl_matrix_memcpy( t_basis, t_metric );
1211  XLAL_CHECK( gsl_linalg_cholesky_decomp( t_basis ) == 0, XLAL_EFAILED, "Parameter-space metric is not positive definite" );
1212  XLAL_CHECK( gsl_linalg_cholesky_invert( t_basis ) == 0, XLAL_EFAILED, "Parameter-space metric cannot be inverted" );
1213  XLAL_CHECK( gsl_linalg_cholesky_decomp( t_basis ) == 0, XLAL_EFAILED, "Inverse of parameter-space metric is not positive definite" );
1214 
1215  // gsl_linalg_cholesky_decomp() stores both basis and basis^T
1216  // in the same matrix; zero out upper triangle to get basis
1217  LT_ZeroStrictUpperTriangle( t_basis );
1218  }
1219 
1220  // Compute a lower-triangular generator matrix for a given lattice type and mismatch
1221  GAMAT( tiling->tiled_generator, tn, tn );
1222  {
1223 
1224  // Compute lattice generator and normalised thickness
1225  double norm_thickness = 0.0;
1226  switch ( tiling->lattice ) {
1227 
1228  case TILING_LATTICE_CUBIC: // Cubic ( \f$ Z_n \f$ ) lattice
1229 
1230  {
1231 
1232  // Zn lattice generator is the identity
1233  gsl_matrix_set_identity( tiling->tiled_generator );
1234 
1235  // Zn normalised thickness
1236  norm_thickness = pow( sqrt( tn ) / 2.0, tn );
1237 
1238  }
1239  break;
1240 
1241  case TILING_LATTICE_ANSTAR: // An-star ( \f$ A_n^* \f$ ) lattice
1242 
1243  {
1244 
1245  // An* lattice generator in tn+1 dimensions, given in:
1246  // McKilliam et.al., "A linear-time nearest point algorithm for the lattice An*"
1247  // in "International Symposium on Information Theory and Its Applications", ISITA2008,
1248  // Auckland, New Zealand, 7-10 Dec. 2008. DOI: 10.1109/ISITA.2008.4895596
1249  gsl_matrix *GAMAT( G, tn + 1, tn + 1 );
1250  gsl_matrix_set_identity( G );
1251  gsl_matrix_add_constant( G, -1.0 / ( tn + 1 ) );
1252 
1253  // Find the QL decomposition of the generator matrix G, excluding 1st column,
1254  // which is linearly dependent on the remaining columns:
1255  // G(:, 2:end) = Gp = Q * L
1256  // where Q is an orthogonal matrix and L an lower-triangular matrix.
1257  // This is found using the more commonly implemented QR decomposition by:
1258  // - reversing the order of the rows/columns of Gp
1259  // - decomposing Gp = Qp * Lp, where Lp is upper triangular
1260  // - reversing the order of the rows/columns of Qp to give Q
1261  // - reversing the order of the rows/columns of Lp to give L
1262  gsl_matrix_view Gp = gsl_matrix_submatrix( G, 0, 1, tn + 1, tn );
1263  LT_ReverseOrderRowsCols( &Gp.matrix );
1264  gsl_vector *GAVEC( tau, tn );
1265  XLAL_CHECK( gsl_linalg_QR_decomp( &Gp.matrix, tau ) == 0, XLAL_EFAILED, "'G' cannot be QR-decomposed" );
1266  gsl_matrix *GAMAT( Q, tn + 1, tn + 1 );
1267  gsl_matrix *GAMAT( L, tn + 1, tn );
1268  gsl_linalg_QR_unpack( &Gp.matrix, tau, Q, L );
1271 
1272  // Discard the first row of L, which is zero, to get the generator in tn dimensions
1273  gsl_matrix_view L_view = gsl_matrix_submatrix( L, 1, 0, tn, tn );
1274  gsl_matrix_memcpy( tiling->tiled_generator, &L_view.matrix );
1275 
1276  // Cleanup
1277  GFMAT( G, L, Q );
1278  GFVEC( tau );
1279 
1280  // An* normalised thickness
1281  norm_thickness = sqrt( tn + 1.0 ) * pow( ( 1.0 * tn * ( tn + 2.0 ) ) / ( 12.0 * ( tn + 1.0 ) ), 0.5 * tn );
1282 
1283  }
1284  break;
1285 
1286  default:
1287  XLAL_ERROR( XLAL_EFAILED, "Invalid lattice" );
1288  }
1289 
1290  // Generator will be lower-triangular, so zero out upper triangle
1291  LT_ZeroStrictUpperTriangle( tiling->tiled_generator );
1292 
1293  // Ensure that the generator has positive diagonal elements, by
1294  // changing the sign of the columns of the matrix, if necessary
1295  for ( size_t tj = 0; tj < tn; ++tj ) {
1296  gsl_vector_view generator_col = gsl_matrix_column( tiling->tiled_generator, tj );
1297  XLAL_CHECK( gsl_vector_get( &generator_col.vector, tj ) != 0, XLAL_ERANGE, "Generator matrix(%zu,%zu) == 0", tj, tj );
1298  if ( gsl_vector_get( &generator_col.vector, tj ) < 0 ) {
1299  gsl_vector_scale( &generator_col.vector, -1 );
1300  }
1301  }
1302 
1303  // Compute generator LU decomposition
1304  gsl_matrix *GAMAT( LU_decomp, tn, tn );
1305  gsl_matrix_memcpy( LU_decomp, tiling->tiled_generator );
1306  gsl_permutation *GAPERM( LU_perm, tn );
1307  int LU_sign = 0;
1308  XLAL_CHECK( gsl_linalg_LU_decomp( LU_decomp, LU_perm, &LU_sign ) == 0, XLAL_EFAILED, "Generator matrix cannot be LU-decomposed" );
1309 
1310  // Compute generator determinant
1311  const double generator_determinant = XLALMetricDeterminant( tiling->tiled_generator );
1312  XLAL_CHECK( !XLAL_IS_REAL8_FAIL_NAN( generator_determinant ), XLAL_EFUNC );
1313 
1314  // Compute generator covering radius
1315  const double generator_covering_radius = pow( norm_thickness * generator_determinant, 1.0 / tn );
1316 
1317  // Normalise so covering spheres have sqrt(max_mismatch) covering radii
1318  gsl_matrix_scale( tiling->tiled_generator, sqrt( max_mismatch ) / generator_covering_radius );
1319 
1320  // Cleanup
1321  GFMAT( LU_decomp );
1322  GFPERM( LU_perm );
1323 
1324  }
1325 
1326  // Compute transform to normalised physical coordinates from generating integers
1327  gsl_matrix *GAMAT( t_norm_from_int, tn, tn );
1328  gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, 1.0, t_basis, tiling->tiled_generator, 0.0, t_norm_from_int );
1329  LT_ZeroStrictUpperTriangle( t_norm_from_int );
1330 
1331  // Compute transform to generating integers from normalised physical coordinates
1332  gsl_matrix *GAMAT( t_int_from_norm, tn, tn );
1333  gsl_matrix_set_identity( t_int_from_norm );
1334  gsl_blas_dtrsm( CblasLeft, CblasLower, CblasNoTrans, CblasNonUnit, 1.0, t_norm_from_int, t_int_from_norm );
1335  LT_ZeroStrictUpperTriangle( t_int_from_norm );
1336 
1337  // Set tiled dimensions of transforms, and convert to unnormalised physical coordinates
1338  for ( size_t ti = 0; ti < tn; ++ti ) {
1339  const size_t i = tiling->tiled_idx[ti];
1340  const double t_norm_ti = gsl_vector_get( t_norm, ti );
1341  for ( size_t tj = 0; tj < tn; ++tj ) {
1342  const size_t j = tiling->tiled_idx[tj];
1343  const double t_norm_tj = gsl_vector_get( t_norm, tj );
1344  gsl_matrix_set( tiling->int_from_phys, i, j, gsl_matrix_get( t_int_from_norm, ti, tj ) * t_norm_tj );
1345  gsl_matrix_set( tiling->phys_from_int, i, j, gsl_matrix_get( t_norm_from_int, ti, tj ) / t_norm_ti );
1346  }
1347  }
1348 
1349  // Round tiled dimensions of physical parameter-space origin to nearest lattice step size, then
1350  // shift by the fraction of a step size 'phys_origin_shift_frac_i', if given, or 0.5 otherwise.
1351  // - The default of 0.5 was to ensure that the tiling will never place a lattice point at zero
1352  // in physical coordinates, since the physical coordinates may not be well-defined at zero.
1353  // This could potentially not be the case if 'phys_origin_shift_frac_i' happens to be zero.
1354  for ( size_t i = 0; i < n; ++i ) {
1355  double phys_origin_i = gsl_vector_get( tiling->phys_origin, i );
1356  if ( tiling->bounds[i].is_tiled ) {
1357  const double int_from_phys_i_i = gsl_matrix_get( tiling->int_from_phys, i, i );
1358  const double phys_from_int_i_i = gsl_matrix_get( tiling->phys_from_int, i, i );
1359  const double phys_origin_shift_frac_i = ( tiling->phys_origin_shift_frac != NULL ) ? gsl_vector_get( tiling->phys_origin_shift_frac, i ) : 0.5;
1360  phys_origin_i = ( round( phys_origin_i * int_from_phys_i_i ) + phys_origin_shift_frac_i ) * phys_from_int_i_i;
1361  }
1362  LT_SetPhysPoint( tiling, phys_origin_cache, tiling->phys_origin, i, phys_origin_i );
1363  }
1364 
1365  // Cleanup
1366  GFMAT( t_metric, t_basis, t_norm_from_int, t_int_from_norm, phys_origin_cache );
1367  GFVEC( t_norm, t_bbox );
1368 
1369  return XLAL_SUCCESS;
1370 
1371 }
1372 
1374  const LatticeTiling *tiling
1375 )
1376 {
1377 
1378  // Check input
1379  XLAL_CHECK_VAL( 0, tiling != NULL, XLAL_EFAULT );
1380 
1381  return tiling->ndim;
1382 
1383 }
1384 
1386  const LatticeTiling *tiling
1387 )
1388 {
1389 
1390  // Check input
1391  XLAL_CHECK_VAL( 0, tiling != NULL, XLAL_EFAULT );
1392  XLAL_CHECK_VAL( 0, tiling->lattice < TILING_LATTICE_MAX, XLAL_EINVAL );
1393 
1394  return tiling->tiled_ndim;
1395 
1396 }
1397 
1399  const LatticeTiling *tiling,
1400  const size_t tiled_dim
1401 )
1402 {
1403 
1404  // Check input
1405  XLAL_CHECK_VAL( 0, tiling != NULL, XLAL_EFAULT );
1406  XLAL_CHECK_VAL( 0, tiling->lattice < TILING_LATTICE_MAX, XLAL_EINVAL );
1407  XLAL_CHECK_VAL( 0, tiled_dim < tiling->tiled_ndim, XLAL_ESIZE );
1408 
1409  return tiling->tiled_idx[tiled_dim];
1410 
1411 }
1412 
1414  const LatticeTiling *tiling,
1415  const size_t dim
1416 )
1417 {
1418 
1419  // Check input
1420  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
1421  XLAL_CHECK( dim < tiling->ndim, XLAL_ESIZE );
1422  XLAL_CHECK( tiling->bounds[dim].func != NULL, XLAL_EINVAL, "Lattice tiling dimension #%zu is not bounded", dim );
1423 
1424  return tiling->bounds[dim].is_tiled ? 1 : 0;
1425 
1426 }
1427 
1429  const LatticeTiling *tiling,
1430  const size_t dim
1431 )
1432 {
1433 
1434  // Check input
1435  XLAL_CHECK_NULL( tiling != NULL, XLAL_EFAULT );
1436  XLAL_CHECK_NULL( tiling->lattice < TILING_LATTICE_MAX, XLAL_EINVAL );
1437  XLAL_CHECK_NULL( dim < tiling->ndim, XLAL_ESIZE );
1438 
1439  return tiling->bounds[dim].name;
1440 
1441 }
1442 
1444  const LatticeTiling *tiling,
1445  const char *bound_name
1446 )
1447 {
1448 
1449  // Check input
1450  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
1451  XLAL_CHECK( bound_name != NULL, XLAL_EFAULT );
1452 
1453  // Find and return index of dimension with matching bound name
1454  for ( int dim = 0; dim < ( int )tiling->ndim; ++dim ) {
1455  if ( strcmp( tiling->bounds[dim].name, bound_name ) == 0 ) {
1456  return dim;
1457  }
1458  }
1459 
1460  return XLAL_FAILURE;
1461 
1462 }
1463 
1465  const LatticeTiling *tiling,
1466  const size_t dim
1467 )
1468 {
1469 
1470  // Check input
1471  XLAL_CHECK_REAL8( tiling != NULL, XLAL_EFAULT );
1472  XLAL_CHECK_REAL8( tiling->lattice < TILING_LATTICE_MAX, XLAL_EINVAL );
1473  XLAL_CHECK_REAL8( dim < tiling->ndim, XLAL_ESIZE );
1474 
1475  // Return 0 for non-tiled dimensions
1476  if ( !tiling->bounds[dim].is_tiled ) {
1477  return 0.0;
1478  }
1479 
1480  // Step size is the (dim)th diagonal element of 'phys_from_int'
1481  return gsl_matrix_get( tiling->phys_from_int, dim, dim );
1482 
1483 }
1484 
1486  const LatticeTiling *tiling,
1487  const size_t dim
1488 )
1489 {
1490 
1491  // Check input
1492  XLAL_CHECK_REAL8( tiling != NULL, XLAL_EFAULT );
1493  XLAL_CHECK_REAL8( tiling->lattice < TILING_LATTICE_MAX, XLAL_EINVAL );
1494  XLAL_CHECK_REAL8( dim < tiling->ndim, XLAL_ESIZE );
1495 
1496  // Return 0 for non-tiled dimensions
1497  if ( !tiling->bounds[dim].is_tiled ) {
1498  return 0.0;
1499  }
1500 
1501  return gsl_vector_get( tiling->phys_bbox, dim );
1502 
1503 }
1504 
1506  LatticeTiling *tiling,
1507  const LatticeTilingCallback func,
1508  const size_t param_len,
1509  const void *param,
1510  const size_t out_len
1511 )
1512 {
1513 
1514  // Check input
1515  XLAL_CHECK_NULL( tiling != NULL, XLAL_EFAULT );
1516  XLAL_CHECK_NULL( tiling->lattice < TILING_LATTICE_MAX, XLAL_EINVAL );
1517  XLAL_CHECK_NULL( func != NULL, XLAL_EFAULT );
1518  XLAL_CHECK_NULL( ( param_len > 0 ) == ( param != NULL ), XLAL_EINVAL );
1520  XLAL_CHECK_NULL( out_len > 0, XLAL_EINVAL );
1522 
1523  // Allocate memory for new callback
1524  ++tiling->ncallback;
1525  tiling->callbacks = XLALRealloc( tiling->callbacks, tiling->ncallback * sizeof( *tiling->callbacks ) );
1526  XLAL_CHECK_NULL( tiling->callbacks != NULL, XLAL_ENOMEM );
1527  LT_Callback *cb = tiling->callbacks[tiling->ncallback - 1] = XLALCalloc( 1, sizeof( *cb ) );
1528  XLAL_CHECK_NULL( cb != NULL, XLAL_ENOMEM );
1529 
1530  // Set fields
1531  cb->func = func;
1532  cb->param_len = param_len;
1533  if ( param_len > 0 ) {
1534  memcpy( cb->param, param, param_len );
1535  }
1536 
1537  return cb->out;
1538 
1539 }
1540 
1542  const LatticeTiling *tiling
1543 )
1544 {
1545 
1546  // Check input
1547  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
1548  XLAL_CHECK( tiling->lattice < TILING_LATTICE_MAX, XLAL_EINVAL );
1549 
1550  // Return immediately if there are no callbacks to perform
1551  if ( *tiling->ncallback_done == tiling->ncallback ) {
1552  return XLAL_SUCCESS;
1553  }
1554 
1555  const size_t n = tiling->ndim;
1556  const size_t tn = tiling->tiled_ndim;
1557 
1558  // Create iterator over tiling (except highest dimension)
1559  LatticeTilingIterator *itr = XLALCreateLatticeTilingIterator( tiling, n - 1 );
1560  XLAL_CHECK( itr != NULL, XLAL_EFUNC );
1561 
1562  // Iterate over all points
1563  double volume_pc_prev = 0;
1564  bool first_call = true;
1565  int changed_ti_p1;
1566  double point_array[n];
1567  gsl_vector_view point_view = gsl_vector_view_array( point_array, n );
1568  while ( ( changed_ti_p1 = XLALNextLatticeTilingPoint( itr, &point_view.vector ) ) > 0 ) {
1569  const size_t changed_i = ( !first_call && tiling->tiled_ndim > 0 ) ? tiling->tiled_idx[changed_ti_p1 - 1] : 0;
1570 
1571  // Call callback functions
1572  for ( size_t m = *tiling->ncallback_done; m < tiling->ncallback; ++m ) {
1573  LT_Callback *cb = tiling->callbacks[m];
1574  XLAL_CHECK( ( cb->func )( first_call, tiling, itr, &point_view.vector, changed_i, cb->param, cb->out ) == XLAL_SUCCESS, XLAL_EFUNC );
1575  }
1576 
1577  // Estimate volume of parameter space covered
1578  double volume_pc = 0.0;
1579  double volume_pc_norm = 1.0;
1580  for ( size_t tj = 0; tj < tn; ++tj ) {
1581  const size_t j = itr->tiling->tiled_idx[tj];
1582  const INT8 count_j = itr->int_point[j] - itr->int_lower[j];
1583  const INT8 total_j = itr->int_upper[j] - itr->int_lower[j] + 1;
1584  volume_pc += 100.0 * ( ( double ) count_j ) / ( ( double ) total_j ) / volume_pc_norm;
1585  volume_pc_norm *= ( double ) total_j;
1586  }
1587 
1588  // Log progress if:
1589  // - at least 1000 points have been counted
1590  // - parameter space covered has increased by ( EITHER a factor of 3 OR 5 percentage points ) since last log
1591  const UINT8 total_points = tiling->stats[n - 1].total_points;
1592  if ( total_points >= 1000 && ( volume_pc >= 3.0 * volume_pc_prev || volume_pc - volume_pc_prev >= 5.0 ) ) {
1593  volume_pc_prev = volume_pc;
1594  LogPrintf( LatticeTilingProgressLogLevel, "LatticeTiling: counted %" LAL_UINT8_FORMAT " templates; estimated %0.2g%% done\n", total_points, volume_pc );
1595  }
1596 
1597  first_call = false;
1598  }
1600 
1601  // Log final progress
1602  {
1603  const UINT8 total_points = tiling->stats[n - 1].total_points;
1604  LogPrintf( LatticeTilingProgressLogLevel, "LatticeTiling: counted %" LAL_UINT8_FORMAT " templates; done\n", total_points );
1605  }
1606 
1607  // Mark callbacks as have been successfully performed
1608  *tiling->ncallback_done = tiling->ncallback;
1609 
1610  // Cleanup
1612 
1613  return XLAL_SUCCESS;
1614 
1615 }
1616 
1618  const LatticeTiling *tiling,
1619  const size_t dim
1620 )
1621 {
1622 
1623  // Check input
1624  XLAL_CHECK_NULL( tiling != NULL, XLAL_EFAULT );
1625  XLAL_CHECK_NULL( tiling->lattice < TILING_LATTICE_MAX, XLAL_EINVAL );
1626  XLAL_CHECK_NULL( tiling->stats != NULL, XLAL_EFUNC );
1627  XLAL_CHECK_NULL( dim < tiling->ndim, XLAL_ESIZE );
1628 
1629  // Ensure statistics have been computed
1631  XLAL_CHECK_NULL( tiling->stats[dim].total_points > 0, XLAL_EFAILED );
1632 
1633  return &tiling->stats[dim];
1634 
1635 }
1636 
1638  const LatticeTiling *tiling,
1639  const double scale,
1640  RandomParams *rng,
1641  gsl_matrix *random_points
1642 )
1643 {
1644 
1645  // Check input
1646  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
1647  XLAL_CHECK( tiling->lattice < TILING_LATTICE_MAX, XLAL_EINVAL );
1648  XLAL_CHECK( scale > -1.0, XLAL_EINVAL );
1649  XLAL_CHECK( rng != NULL, XLAL_EFAULT );
1650  XLAL_CHECK( random_points != NULL, XLAL_EFAULT );
1651  XLAL_CHECK( random_points->size1 == tiling->ndim, XLAL_ESIZE );
1652 
1653  const size_t n = tiling->ndim;
1654 
1655  // Generate random points in parameter space
1656  gsl_matrix *GAMAT( phys_point_cache, n, LT_CACHE_MAX_SIZE );
1657  gsl_matrix_set_all( phys_point_cache, GSL_NAN );
1658  for ( size_t k = 0; k < random_points->size2; ++k ) {
1659  gsl_vector_view phys_point = gsl_matrix_column( random_points, k );
1660  for ( size_t i = 0; i < n; ++i ) {
1661 
1662  // Get the physical bounds on the current dimension
1663  double phys_lower = 0.0, phys_upper = 0.0;
1664  LT_CallBoundFunc( tiling, i, phys_point_cache, &phys_point.vector, &phys_lower, &phys_upper );
1665 
1666  // Generate random number
1667  const double u = ( 1.0 + scale ) * ( XLALUniformDeviate( rng ) - 0.5 ) + 0.5;
1668 
1669  // Set parameter-space point
1670  LT_SetPhysPoint( tiling, phys_point_cache, &phys_point.vector, i, phys_lower + u * ( phys_upper - phys_lower ) );
1671 
1672  }
1673 
1674  }
1675 
1676  // Cleanup
1677  GFMAT( phys_point_cache );
1678 
1679  return XLAL_SUCCESS;
1680 
1681 }
1682 
1684  const LatticeTiling *tiling,
1685  const size_t dim,
1686  const gsl_vector *point,
1687  const bool padding,
1688  double *lower,
1689  double *upper
1690 )
1691 {
1692 
1693  // Check input
1694  XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
1695  XLAL_CHECK( tiling->lattice < TILING_LATTICE_MAX, XLAL_EINVAL );
1696  XLAL_CHECK( dim < tiling->ndim, XLAL_ESIZE );
1697  XLAL_CHECK( point != NULL, XLAL_EFAULT );
1698  XLAL_CHECK( lower != NULL, XLAL_EFAULT );
1699  XLAL_CHECK( upper != NULL, XLAL_EFAULT );
1700 
1701  const size_t n = tiling->ndim;
1702 
1703  // Get bound information for this dimension
1704  const LT_Bound *bound = &tiling->bounds[dim];
1705 
1706  // Get the parameter-space bounds on the current dimension:
1707  // - If tiled, or padding requested, get bounds respecting strict/extrema settings, and add padding
1708  // - Otherwise, get bounds without extrema/padding
1709  gsl_vector *GAVEC( phys_sampl, n );
1710  gsl_matrix *GAMAT( phys_point_cache, n, LT_CACHE_MAX_SIZE );
1711  gsl_matrix *GAMAT( phys_sampl_cache, n, LT_CACHE_MAX_SIZE );
1712  gsl_vector_memcpy( phys_sampl, point );
1713  *lower = GSL_POSINF;
1714  *upper = GSL_NEGINF;
1715  if ( bound->is_tiled && padding ) {
1716  if ( STRICT_BOUND_PADDING( bound ) || !bound->find_bound_extrema ) {
1717  LT_CallBoundFunc( tiling, dim, phys_point_cache, phys_sampl, lower, upper );
1718  } else {
1719  LT_FindBoundExtrema( tiling, 0, dim, phys_sampl_cache, phys_sampl, lower, upper );
1720  }
1721  const double phys_bbox_dim = gsl_vector_get( tiling->phys_bbox, dim );
1722  *lower -= bound->lower_bbox_pad * phys_bbox_dim;
1723  *upper += bound->upper_bbox_pad * phys_bbox_dim;
1724  } else {
1725  LT_CallBoundFunc( tiling, dim, phys_point_cache, phys_sampl, lower, upper );
1726  }
1727 
1728  // Cleanup
1729  GFVEC( phys_sampl );
1730  GFMAT( phys_point_cache );
1731  GFMAT( phys_sampl_cache );
1732 
1733  return XLAL_SUCCESS;
1734 
1735 }
1736 
1737 LatticeTilingIterator *XLALCreateLatticeTilingIterator(
1738  const LatticeTiling *tiling,
1739  const size_t itr_ndim
1740 )
1741 {
1742 
1743  // Check input
1744  XLAL_CHECK_NULL( tiling != NULL, XLAL_EFAULT );
1745  XLAL_CHECK_NULL( tiling->lattice < TILING_LATTICE_MAX, XLAL_EINVAL );
1746  XLAL_CHECK_NULL( itr_ndim <= tiling->ndim, XLAL_EINVAL );
1747 
1748  // Allocate memory
1749  LatticeTilingIterator *itr = XLALCalloc( 1, sizeof( *itr ) );
1750  XLAL_CHECK_NULL( itr != NULL, XLAL_ENOMEM );
1751 
1752  // Store reference to lattice tiling
1753  itr->tiling = tiling;
1754 
1755  // Set fields
1756  itr->itr_ndim = itr_ndim;
1757  itr->alternating = false;
1758  itr->state = 0;
1759  itr->index = 0;
1760 
1761  // Determine the maximum tiled dimension to iterate over
1762  itr->tiled_itr_ndim = 0;
1763  for ( size_t i = 0; i < itr_ndim; ++i ) {
1764  if ( itr->tiling->bounds[i].is_tiled ) {
1765  ++itr->tiled_itr_ndim;
1766  }
1767  }
1768 
1769  const size_t n = itr->tiling->ndim;
1770  const size_t tn = itr->tiling->tiled_ndim;
1771 
1772  // Allocate and initialise vectors and matrices
1773  GAVEC_NULL( itr->phys_point, n );
1774  GAMAT_NULL( itr->phys_point_cache, n, LT_CACHE_MAX_SIZE );
1775  gsl_matrix_set_all( itr->phys_point_cache, GSL_NAN );
1776  GAVEC_NULL( itr->phys_sampl, n );
1777  GAMAT_NULL( itr->phys_sampl_cache, n, LT_CACHE_MAX_SIZE );
1778  gsl_matrix_set_all( itr->phys_sampl_cache, GSL_NAN );
1779  if ( tn > 0 ) {
1780  itr->int_lower = XLALCalloc( tn, sizeof( *itr->int_lower ) );
1781  XLAL_CHECK_NULL( itr->int_lower != NULL, XLAL_EINVAL );
1782  itr->int_point = XLALCalloc( tn, sizeof( *itr->int_point ) );
1783  XLAL_CHECK_NULL( itr->int_point != NULL, XLAL_EINVAL );
1784  itr->int_upper = XLALCalloc( tn, sizeof( *itr->int_upper ) );
1785  XLAL_CHECK_NULL( itr->int_upper != NULL, XLAL_EINVAL );
1786  itr->direction = XLALCalloc( tn, sizeof( *itr->direction ) );
1787  XLAL_CHECK_NULL( itr->direction != NULL, XLAL_EINVAL );
1788  }
1789 
1790  // Set iterator to beginning of lattice tiling
1792 
1793  return itr;
1794 
1795 }
1796 
1798  LatticeTilingIterator *itr
1799 )
1800 {
1801  if ( itr ) {
1802  GFVEC( itr->phys_point, itr->phys_sampl );
1803  GFMAT( itr->phys_point_cache, itr->phys_sampl_cache );
1804  XLALFree( itr->int_lower );
1805  XLALFree( itr->int_point );
1806  XLALFree( itr->int_upper );
1807  XLALFree( itr->direction );
1808  XLALFree( itr );
1809  }
1810 }
1811 
1813  LatticeTilingIterator *itr,
1814  const bool alternating
1815 )
1816 {
1817 
1818  // Check input
1819  XLAL_CHECK( itr != NULL, XLAL_EFAULT );
1820  XLAL_CHECK( itr->state == 0, XLAL_EINVAL );
1821 
1822  // Set alternating iterator
1823  itr->alternating = alternating;
1824 
1825  return XLAL_SUCCESS;
1826 
1827 }
1828 
1830  LatticeTilingIterator *itr
1831 )
1832 {
1833 
1834  // Check input
1835  XLAL_CHECK( itr != NULL, XLAL_EFAULT );
1836 
1837  // Return iterator to initialised state
1838  itr->state = 0;
1839 
1840  return XLAL_SUCCESS;
1841 
1842 }
1843 
1845  LatticeTilingIterator *itr,
1846  gsl_vector *point
1847 )
1848 {
1849 
1850  // Check input
1851  XLAL_CHECK( itr != NULL, XLAL_EFAULT );
1852  XLAL_CHECK( point == NULL || point->size == itr->tiling->ndim, XLAL_EINVAL );
1853 
1854  const size_t n = itr->tiling->ndim;
1855  const size_t tn = itr->tiling->tiled_ndim;
1856 
1857  // If iterator is finished, we're done
1858  if ( itr->state > 1 ) {
1859  return 0;
1860  }
1861 
1862  // Which dimensions have changed?
1863  size_t changed_ti;
1864 
1865  // Which dimensions need to be reset?
1866  size_t reset_ti;
1867 
1868  if ( itr->state == 0 ) { // Iterator has been initialised
1869 
1870  // Initialise lattice point
1871  gsl_vector_set_zero( itr->phys_point );
1872  for ( size_t ti = 0; ti < tn; ++ti ) {
1873  itr->int_point[ti] = 0;
1874  }
1875 
1876  // Initialise iteration direction to 1, i.e. lower to upper bound
1877  for ( size_t ti = 0; ti < tn; ++ti ) {
1878  itr->direction[ti] = 1;
1879  }
1880 
1881  // Initialise index
1882  itr->index = 0;
1883 
1884  // All dimensions have changed
1885  changed_ti = 0;
1886 
1887  // All dimensions need to be reset
1888  reset_ti = 0;
1889 
1890  } else { // Iterator is in progress
1891 
1892  // Start iterating from the maximum tiled dimension specified at iterator creation
1893  size_t ti = itr->tiled_itr_ndim;
1894 
1895  // Find the next lattice point
1896  while ( true ) {
1897 
1898  // If dimension index is now zero, we're done
1899  if ( ti == 0 ) {
1900 
1901  // Iterator is now finished
1902  itr->state = 2;
1903 
1904  return 0;
1905 
1906  }
1907 
1908  // Decrement current dimension index
1909  --ti;
1910 
1911  // Increment integer point in this dimension, in current direction
1912  const INT4 direction = itr->direction[ti];
1913  const INT4 int_point_ti = itr->int_point[ti] + direction;
1914  itr->int_point[ti] = int_point_ti;
1915 
1916  // Increment physical point in this dimension, in current direction
1917  const size_t i = itr->tiling->tiled_idx[ti];
1918  gsl_vector_const_view phys_from_int_i = gsl_matrix_const_column( itr->tiling->phys_from_int, i );
1919  gsl_blas_daxpy( direction, &phys_from_int_i.vector, itr->phys_point );
1920 
1921  // If point is not out of bounds, we have found the next lattice point
1922  const INT4 int_lower_ti = itr->int_lower[ti];
1923  const INT4 int_upper_ti = itr->int_upper[ti];
1924  if ( ( direction > 0 && int_point_ti <= int_upper_ti ) || ( direction < 0 && int_point_ti >= int_lower_ti ) ) {
1925  break;
1926  }
1927 
1928  // Move on to lower dimensions
1929  continue;
1930 
1931  }
1932 
1933  // Point was found, so increase index
1934  ++itr->index;
1935 
1936  // This dimension and higher have changed
1937  changed_ti = ti;
1938 
1939  // Higher dimensions need to be reset
1940  reset_ti = ti + 1;
1941 
1942  }
1943 
1944  // Reset parameter-space bounds and recompute physical point
1945  for ( size_t i = 0, ti = 0; i < n; ++i ) {
1946 
1947  // Get bound information for this dimension
1948  const LT_Bound *bound = &itr->tiling->bounds[i];
1949 
1950  // Get physical parameter-space origin in the current dimension
1951  const double phys_origin_i = gsl_vector_get( itr->tiling->phys_origin, i );
1952 
1953  // If not tiled, set current physical point to non-tiled parameter-space bound
1954  if ( !bound->is_tiled && ti >= reset_ti ) {
1955  double phys_lower = 0, phys_upper = 0;
1956  LT_CallBoundFunc( itr->tiling, i, itr->phys_point_cache, itr->phys_point, &phys_lower, &phys_upper );
1957  LT_SetPhysPoint( itr->tiling, itr->phys_point_cache, itr->phys_point, i, phys_lower );
1958  }
1959 
1960  // If tiled, reset parameter-space bounds
1961  if ( bound->is_tiled && ti >= reset_ti ) {
1962 
1963  // Find the parameter-space bounds on the current dimension
1964  gsl_vector_memcpy( itr->phys_sampl, itr->phys_point );
1965  double phys_lower = GSL_POSINF, phys_upper = GSL_NEGINF;
1966  if ( STRICT_BOUND_PADDING( bound ) || !bound->find_bound_extrema ) {
1967  LT_CallBoundFunc( itr->tiling, i, itr->phys_point_cache, itr->phys_sampl, &phys_lower, &phys_upper );
1968  } else {
1969  LT_FindBoundExtrema( itr->tiling, 0, i, itr->phys_sampl_cache, itr->phys_sampl, &phys_lower, &phys_upper );
1970  }
1971 
1972  // Add padding of a multiple of the extext of the metric ellipse bounding box, if requested
1973  {
1974  const double phys_bbox_i = gsl_vector_get( itr->tiling->phys_bbox, i );
1975  phys_lower -= bound->lower_bbox_pad * phys_bbox_i;
1976  phys_upper += bound->upper_bbox_pad * phys_bbox_i;
1977  }
1978 
1979  // Transform physical point in lower dimensions to generating integer offset
1980  double int_from_phys_point_i = 0;
1981  for ( size_t j = 0; j < i; ++j ) {
1982  const double int_from_phys_i_j = gsl_matrix_get( itr->tiling->int_from_phys, i, j );
1983  const double phys_point_j = gsl_vector_get( itr->phys_point, j );
1984  const double phys_origin_j = gsl_vector_get( itr->tiling->phys_origin, j );
1985  int_from_phys_point_i += int_from_phys_i_j * ( phys_point_j - phys_origin_j );
1986  }
1987 
1988  {
1989  // Transform physical bounds to generating integers
1990  const double int_from_phys_i_i = gsl_matrix_get( itr->tiling->int_from_phys, i, i );
1991  const double dbl_int_lower_i = int_from_phys_point_i + int_from_phys_i_i * ( phys_lower - phys_origin_i );
1992  const double dbl_int_upper_i = int_from_phys_point_i + int_from_phys_i_i * ( phys_upper - phys_origin_i );
1993 
1994  // Compute integer lower/upper bounds, rounded up/down to avoid extra boundary points
1995  feclearexcept( FE_ALL_EXCEPT );
1996  const INT4 int_lower_i = lround( ceil( dbl_int_lower_i ) );
1997  const INT4 int_upper_i = lround( floor( dbl_int_upper_i ) );
1998  XLAL_CHECK( fetestexcept( FE_INVALID ) == 0, XLAL_EFAILED, "Integer bounds on dimension #%zu are too large: %0.2e to %0.2e", i, dbl_int_lower_i, dbl_int_upper_i );
1999 
2000  // Set integer lower/upper bounds
2001  itr->int_lower[ti] = int_lower_i;
2002  itr->int_upper[ti] = GSL_MAX( int_lower_i, int_upper_i );
2003 
2004  // Add padding as a multiple of integer points, if requested
2005  itr->int_lower[ti] -= bound->lower_intp_pad;
2006  itr->int_upper[ti] += bound->upper_intp_pad;
2007  }
2008  const INT4 int_lower_i = itr->int_lower[ti];
2009  const INT4 int_upper_i = itr->int_upper[ti];
2010 
2011  // Get iteration direction
2012  INT4 direction = itr->direction[ti];
2013 
2014  // Only switch iteration direction:
2015  // - if this is an alternating iterator
2016  // - if iterator is in progress
2017  // - for iterated-over dimensions
2018  // - if there is more than one point in this dimension
2019  if ( itr->alternating && ( itr->state > 0 ) && ( ti < itr->tiled_itr_ndim ) && ( int_lower_i < int_upper_i ) ) {
2020  direction = -direction;
2021  itr->direction[ti] = direction;
2022  }
2023 
2024  // Set integer point to:
2025  // - lower or upper bound (depending on current direction) for iterated-over dimensions
2026  // - mid-point of integer bounds for non-iterated dimensions
2027  if ( ti < itr->tiled_itr_ndim ) {
2028  itr->int_point[ti] = ( direction > 0 ) ? int_lower_i : int_upper_i;
2029  } else {
2030  itr->int_point[ti] = ( int_lower_i + int_upper_i ) / 2;
2031  }
2032 
2033  }
2034 
2035  // If tiled, recompute current physical point from integer point
2036  if ( bound->is_tiled && ti >= changed_ti ) {
2037  double phys_point_i = phys_origin_i;
2038  for ( size_t tj = 0; tj < tn; ++tj ) {
2039  const size_t j = itr->tiling->tiled_idx[tj];
2040  const double phys_from_int_i_j = gsl_matrix_get( itr->tiling->phys_from_int, i, j );
2041  const INT4 int_point_tj = itr->int_point[tj];
2042  phys_point_i += phys_from_int_i_j * int_point_tj;
2043  }
2044  LT_SetPhysPoint( itr->tiling, itr->phys_point_cache, itr->phys_point, i, phys_point_i );
2045  }
2046 
2047  // Handle strict parameter space boundaries
2048  if ( STRICT_BOUND_PADDING( bound ) && ti >= changed_ti ) {
2049 
2050  // Get current physical point and bounds
2051  double phys_point_i = gsl_vector_get( itr->phys_point, i );
2052  double phys_lower = 0, phys_upper = 0;
2053  LT_CallBoundFunc( itr->tiling, i, itr->phys_point_cache, itr->phys_point, &phys_lower, &phys_upper );
2054 
2055  // If physical point outside lower bound, try to move just inside
2056  if ( phys_point_i < phys_lower ) {
2057  const double phys_from_int_i_i = gsl_matrix_get( itr->tiling->phys_from_int, i, i );
2058  const INT4 di = lround( ceil( ( phys_lower - phys_point_i ) / phys_from_int_i_i ) );
2059  itr->int_point[ti] += di;
2060  phys_point_i += phys_from_int_i_i * di;
2061  }
2062 
2063  // If physical point now outside upper bound, parameter space is narrower than step size:
2064  // - Set physical point to mid-point of parameter space bounds
2065  // - Set integer point to upper bound, so that next iteration will reset
2066  if ( phys_point_i > phys_upper ) {
2067  phys_point_i = 0.5 * ( phys_lower + phys_upper );
2068  itr->int_point[ti] = itr->int_upper[ti];
2069  }
2070 
2071  // Set physical point
2072  LT_SetPhysPoint( itr->tiling, itr->phys_point_cache, itr->phys_point, i, phys_point_i );
2073 
2074  }
2075 
2076  // Increment tiled dimension index
2077  if ( bound->is_tiled ) {
2078  ++ti;
2079  }
2080 
2081  }
2082 
2083  // Iterator is in progress
2084  itr->state = 1;
2085 
2086  // Optionally, copy current physical point
2087  if ( point != NULL ) {
2088  gsl_vector_memcpy( point, itr->phys_point );
2089  }
2090 
2091  // Return index of changed dimensions (offset from 1, since 0 is used to indicate no more points)
2092  return 1 + changed_ti;
2093 
2094 }
2095 
2097  LatticeTilingIterator *itr,
2098  gsl_matrix **points
2099 )
2100 {
2101 
2102  // Check input
2103  XLAL_CHECK( itr != NULL, XLAL_EFAULT );
2104  XLAL_CHECK( points != NULL && *points != NULL, XLAL_EFAULT );
2105  XLAL_CHECK( ( *points )->size1 == itr->tiling->ndim, XLAL_EINVAL );
2106 
2107  // Fill 'points' with points from XLALNextLatticeTilingPoint(), but stop if there are none left
2108  size_t j = 0;
2109  for ( ; j < ( *points )->size2; ++j ) {
2110  gsl_vector_view point_j = gsl_matrix_column( *points, j );
2111  int retn = XLALNextLatticeTilingPoint( itr, &point_j.vector );
2112  XLAL_CHECK( retn >= 0, XLAL_EFUNC, "XLALNextLatticeTilingPoint() failed at j=%zu", j );
2113  if ( retn == 0 ) {
2114  break;
2115  }
2116  }
2117 
2118  // If there are fewer points than the size of 'points', resize 'points' to fit
2119  if ( 0 < j && j < ( *points )->size2 ) {
2120  gsl_matrix *GAMAT( new_points, ( *points )->size1, j );
2121  gsl_matrix_view points_view = gsl_matrix_submatrix( *points, 0, 0, ( *points )->size1, j );
2122  gsl_matrix_memcpy( new_points, &points_view.matrix );
2123  GFMAT( *points );
2124  *points = new_points;
2125  }
2126 
2127  return j;
2128 
2129 }
2130 
2132  const LatticeTilingIterator *itr
2133 )
2134 {
2135 
2136  // Check input
2137  XLAL_CHECK_VAL( 0, itr != NULL, XLAL_EFAULT );
2138 
2139  // Get lattice tiling statistics
2140  const LatticeTilingStats *stats = XLALLatticeTilingStatistics( itr->tiling, itr->itr_ndim - 1 );
2141  XLAL_CHECK_VAL( 0, stats != NULL, XLAL_EFUNC );
2142  XLAL_CHECK_VAL( 0, stats->total_points > 0, XLAL_EFUNC );
2143 
2144  return stats->total_points;
2145 
2146 }
2147 
2149  const LatticeTilingIterator *itr
2150 )
2151 {
2152 
2153  // Check input
2154  XLAL_CHECK_VAL( 0, itr != NULL, XLAL_EFAULT );
2155  XLAL_CHECK_VAL( 0, itr->state > 0, XLAL_EINVAL );
2156 
2157  return itr->index;
2158 
2159 }
2160 
2162  const LatticeTilingIterator *itr,
2163  const size_t dim,
2164  INT4 *left,
2165  INT4 *right
2166 )
2167 {
2168 
2169  // Check input
2170  XLAL_CHECK( itr != NULL, XLAL_EFAULT );
2171  XLAL_CHECK( itr->state > 0, XLAL_EINVAL );
2172  XLAL_CHECK( dim < itr->tiling->ndim, XLAL_EINVAL );
2173  XLAL_CHECK( left != NULL, XLAL_EFAULT );
2174  XLAL_CHECK( right != NULL, XLAL_EFAULT );
2175 
2176  // Return indexes of left/right-most points in block relative to current point
2177  *left = *right = 0;
2178  for ( size_t ti = 0; ti < itr->tiling->tiled_ndim; ++ti ) {
2179  const size_t i = itr->tiling->tiled_idx[ti];
2180  if ( i == dim ) {
2181  *left = itr->int_lower[ti] - itr->int_point[ti];
2182  *right = itr->int_upper[ti] - itr->int_point[ti];
2183  break;
2184  }
2185  }
2186 
2187  return XLAL_SUCCESS;
2188 
2189 }
2190 
2192  const LatticeTilingIterator *itr,
2193  FITSFile *file,
2194  const char *name
2195 )
2196 {
2197 
2198  // Check input
2199  XLAL_CHECK( itr != NULL, XLAL_EFAULT );
2200  XLAL_CHECK( itr->state > 0, XLAL_EINVAL );
2201  XLAL_CHECK( file != NULL, XLAL_EFAULT );
2202  XLAL_CHECK( name != NULL, XLAL_EFAULT );
2203 
2204  const size_t n = itr->tiling->ndim;
2205 
2206  // Open FITS table for writing
2207  XLAL_CHECK( XLALFITSTableOpenWrite( file, name, "serialised lattice tiling iterator" ) == XLAL_SUCCESS, XLAL_EFUNC );
2209 
2210  // Write FITS records to table
2211  for ( size_t i = 0, ti = 0; i < n; ++i ) {
2212 
2213  // Fill record
2214  LT_FITSRecord XLAL_INIT_DECL( record );
2215  {
2216  INT4 checksum = 0;
2217  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), &itr->tiling->bounds[i].is_tiled, sizeof( itr->tiling->bounds[i].is_tiled ) ) == XLAL_SUCCESS, XLAL_EFUNC );
2218  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), &itr->tiling->bounds[i].data_len, sizeof( itr->tiling->bounds[i].data_len ) ) == XLAL_SUCCESS, XLAL_EFUNC );
2219  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), itr->tiling->bounds[i].data_lower, itr->tiling->bounds[i].data_len ) == XLAL_SUCCESS, XLAL_EFUNC );
2220  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), itr->tiling->bounds[i].data_upper, itr->tiling->bounds[i].data_len ) == XLAL_SUCCESS, XLAL_EFUNC );
2221  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), &itr->tiling->bounds[i].lower_bbox_pad, sizeof( itr->tiling->bounds[i].lower_bbox_pad ) ) == XLAL_SUCCESS, XLAL_EFUNC );
2222  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), &itr->tiling->bounds[i].upper_bbox_pad, sizeof( itr->tiling->bounds[i].upper_bbox_pad ) ) == XLAL_SUCCESS, XLAL_EFUNC );
2223  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), &itr->tiling->bounds[i].lower_intp_pad, sizeof( itr->tiling->bounds[i].lower_intp_pad ) ) == XLAL_SUCCESS, XLAL_EFUNC );
2224  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), &itr->tiling->bounds[i].upper_intp_pad, sizeof( itr->tiling->bounds[i].upper_intp_pad ) ) == XLAL_SUCCESS, XLAL_EFUNC );
2225  record.checksum = checksum;
2226  }
2227  record.phys_point = gsl_vector_get( itr->phys_point, i );
2228  if ( itr->tiling->bounds[i].is_tiled ) {
2229  record.int_point = itr->int_point[ti];
2230  record.int_lower = itr->int_lower[ti];
2231  record.int_upper = itr->int_upper[ti];
2232  record.direction = itr->direction[ti];
2233  ++ti;
2234  }
2235 
2236  // Write record
2238 
2239  }
2240 
2241  // Write tiling properties
2242  {
2243  UINT4 ndim = itr->tiling->ndim;
2244  XLAL_CHECK( XLALFITSHeaderWriteUINT4( file, "ndim", ndim, "number of parameter-space dimensions" ) == XLAL_SUCCESS, XLAL_EFUNC );
2245  } {
2246  UINT4 tiled_ndim = itr->tiling->tiled_ndim;
2247  XLAL_CHECK( XLALFITSHeaderWriteUINT4( file, "tiled_ndim", tiled_ndim, "number of tiled parameter-space dimensions" ) == XLAL_SUCCESS, XLAL_EFUNC );
2248  } {
2249  UINT4 lattice = itr->tiling->lattice;
2250  XLAL_CHECK( XLALFITSHeaderWriteUINT4( file, "lattice", lattice, "type of lattice to generate tiling with" ) == XLAL_SUCCESS, XLAL_EFUNC );
2251  }
2252 
2253  // Write iterator properties
2254  {
2255  UINT4 itr_ndim = itr->itr_ndim;
2256  XLAL_CHECK( XLALFITSHeaderWriteUINT4( file, "itr_ndim", itr_ndim, "number of parameter-space dimensions to iterate over" ) == XLAL_SUCCESS, XLAL_EFUNC );
2257  } {
2258  UINT4 tiled_itr_ndim = itr->tiled_itr_ndim;
2259  XLAL_CHECK( XLALFITSHeaderWriteUINT4( file, "tiled_itr_ndim", tiled_itr_ndim, "number of tiled parameter-space dimensions to iterate over" ) == XLAL_SUCCESS, XLAL_EFUNC );
2260  } {
2261  BOOLEAN alternating = itr->alternating;
2262  XLAL_CHECK( XLALFITSHeaderWriteBOOLEAN( file, "alternating", alternating, "if true, alternate iterator direction after every crossing" ) == XLAL_SUCCESS, XLAL_EFUNC );
2263  } {
2264  UINT4 state = itr->state;
2265  XLAL_CHECK( XLALFITSHeaderWriteUINT4( file, "state", state, "iterator state" ) == XLAL_SUCCESS, XLAL_EFUNC );
2266  } {
2268  XLAL_CHECK( count > 0, XLAL_EFUNC );
2269  XLAL_CHECK( XLALFITSHeaderWriteUINT8( file, "count", count, "total number of lattice tiling points" ) == XLAL_SUCCESS, XLAL_EFUNC );
2270  UINT8 indx = itr->index;
2271  XLAL_CHECK( XLALFITSHeaderWriteUINT8( file, "index", indx, "index of current lattice tiling point" ) == XLAL_SUCCESS, XLAL_EFUNC );
2272  }
2273 
2274  return XLAL_SUCCESS;
2275 
2276 }
2277 
2279  LatticeTilingIterator *itr,
2280  FITSFile *file,
2281  const char *name
2282 )
2283 {
2284 
2285  // Check input
2286  XLAL_CHECK( itr != NULL, XLAL_EFAULT );
2287  XLAL_CHECK( file != NULL, XLAL_EFAULT );
2288  XLAL_CHECK( name != NULL, XLAL_EFAULT );
2289 
2290  const size_t n = itr->tiling->ndim;
2291 
2292  // Open FITS table for reading
2293  UINT8 nrows = 0;
2295  XLAL_CHECK( nrows == ( UINT8 ) n, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2297 
2298  // Read and check tiling properties
2299  {
2300  UINT4 ndim;
2302  XLAL_CHECK( ndim == itr->tiling->ndim, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2303  } {
2304  UINT4 tiled_ndim;
2305  XLAL_CHECK( XLALFITSHeaderReadUINT4( file, "tiled_ndim", &tiled_ndim ) == XLAL_SUCCESS, XLAL_EFUNC );
2306  XLAL_CHECK( tiled_ndim == itr->tiling->tiled_ndim, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2307  } {
2308  UINT4 lattice;
2309  XLAL_CHECK( XLALFITSHeaderReadUINT4( file, "lattice", &lattice ) == XLAL_SUCCESS, XLAL_EFUNC );
2310  XLAL_CHECK( lattice == itr->tiling->lattice, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2311  }
2312 
2313  // Read and check iterator properties
2314  {
2315  UINT4 itr_ndim;
2316  XLAL_CHECK( XLALFITSHeaderReadUINT4( file, "itr_ndim", &itr_ndim ) == XLAL_SUCCESS, XLAL_EFUNC );
2317  XLAL_CHECK( itr_ndim == itr->itr_ndim, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2318  } {
2319  UINT4 tiled_itr_ndim;
2320  XLAL_CHECK( XLALFITSHeaderReadUINT4( file, "tiled_itr_ndim", &tiled_itr_ndim ) == XLAL_SUCCESS, XLAL_EFUNC );
2321  XLAL_CHECK( tiled_itr_ndim == itr->tiled_itr_ndim, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2322  } {
2323  BOOLEAN alternating;
2324  XLAL_CHECK( XLALFITSHeaderReadBOOLEAN( file, "alternating", &alternating ) == XLAL_SUCCESS, XLAL_EFUNC );
2325  XLAL_CHECK( !alternating == !itr->alternating, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2326  } {
2327  UINT4 state;
2328  XLAL_CHECK( XLALFITSHeaderReadUINT4( file, "state", &state ) == XLAL_SUCCESS, XLAL_EFUNC );
2329  itr->state = state;
2330  } {
2331  UINT8 count;
2333  XLAL_CHECK( count > 0, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2334  UINT8 count_ref = XLALTotalLatticeTilingPoints( itr );
2335  XLAL_CHECK( count_ref > 0, XLAL_EFUNC );
2336  XLAL_CHECK( count == count_ref, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2337  UINT8 indx;
2339  XLAL_CHECK( indx < count_ref, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2340  itr->index = indx;
2341  }
2342 
2343  // Read FITS records from table
2344  for ( size_t i = 0, ti = 0; i < n; ++i ) {
2345 
2346  // Read and check record
2347  LT_FITSRecord XLAL_INIT_DECL( record );
2348  XLAL_CHECK( XLALFITSTableReadRow( file, &record, &nrows ) == XLAL_SUCCESS, XLAL_EFUNC );
2349  {
2350  INT4 checksum = 0;
2351  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), &itr->tiling->bounds[i].is_tiled, sizeof( itr->tiling->bounds[i].is_tiled ) ) == XLAL_SUCCESS, XLAL_EFUNC );
2352  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), &itr->tiling->bounds[i].data_len, sizeof( itr->tiling->bounds[i].data_len ) ) == XLAL_SUCCESS, XLAL_EFUNC );
2353  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), itr->tiling->bounds[i].data_lower, itr->tiling->bounds[i].data_len ) == XLAL_SUCCESS, XLAL_EFUNC );
2354  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), itr->tiling->bounds[i].data_upper, itr->tiling->bounds[i].data_len ) == XLAL_SUCCESS, XLAL_EFUNC );
2355  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), &itr->tiling->bounds[i].lower_bbox_pad, sizeof( itr->tiling->bounds[i].lower_bbox_pad ) ) == XLAL_SUCCESS, XLAL_EFUNC );
2356  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), &itr->tiling->bounds[i].upper_bbox_pad, sizeof( itr->tiling->bounds[i].upper_bbox_pad ) ) == XLAL_SUCCESS, XLAL_EFUNC );
2357  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), &itr->tiling->bounds[i].lower_intp_pad, sizeof( itr->tiling->bounds[i].lower_intp_pad ) ) == XLAL_SUCCESS, XLAL_EFUNC );
2358  XLAL_CHECK( XLALPearsonHash( &checksum, sizeof( checksum ), &itr->tiling->bounds[i].upper_intp_pad, sizeof( itr->tiling->bounds[i].upper_intp_pad ) ) == XLAL_SUCCESS, XLAL_EFUNC );
2359  XLAL_CHECK( record.checksum == checksum, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2360  }
2361  LT_SetPhysPoint( itr->tiling, itr->phys_point_cache, itr->phys_point, i, record.phys_point );
2362  if ( itr->tiling->bounds[i].is_tiled ) {
2363  itr->int_point[ti] = record.int_point;
2364  XLAL_CHECK( record.int_lower <= record.int_point, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2365  itr->int_lower[ti] = record.int_lower;
2366  XLAL_CHECK( record.int_point <= record.int_upper, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2367  itr->int_upper[ti] = record.int_upper;
2368  XLAL_CHECK( record.direction != 0, XLAL_EIO, "Could not restore iterator; invalid HDU '%s'", name );
2369  itr->direction[ti] = record.direction;
2370  ++ti;
2371  }
2372 
2373  }
2374 
2375  return XLAL_SUCCESS;
2376 
2377 }
2378 
2379 LatticeTilingLocator *XLALCreateLatticeTilingLocator(
2380  const LatticeTiling *tiling
2381 )
2382 {
2383 
2384  // Check input
2385  XLAL_CHECK_NULL( tiling != NULL, XLAL_EFAULT );
2386  XLAL_CHECK_NULL( tiling->lattice < TILING_LATTICE_MAX, XLAL_EINVAL );
2387 
2388  // Allocate memory
2389  LatticeTilingLocator *loc = XLALCalloc( 1, sizeof( *loc ) );
2390  XLAL_CHECK_NULL( loc != NULL, XLAL_ENOMEM );
2391 
2392  // Store reference to lattice tiling
2393  loc->tiling = tiling;
2394 
2395  // Set fields
2396  loc->ndim = tiling->ndim;
2397  loc->tiled_ndim = tiling->tiled_ndim;
2398 
2399  // Build index trie to enforce parameter-space bounds
2400  if ( loc->tiled_ndim > 0 ) {
2401 
2402  // Create iterator over the bounded dimensions (except for the highest dimension)
2403  LatticeTilingIterator *itr = XLALCreateLatticeTilingIterator( tiling, tiling->tiled_idx[tiling->tiled_ndim - 1] );
2404  XLAL_CHECK_NULL( itr != NULL, XLAL_EFUNC );
2405 
2406  const size_t tn = itr->tiling->tiled_ndim;
2407 
2408  // Allocate array of pointers to the next index trie in each dimension
2409  LT_IndexTrie *next[tn];
2410  memset( next, 0, sizeof( next ) );
2411 
2412  // Allocate array containing sequential indices for every dimension
2413  UINT8 indx[tn];
2414  memset( indx, 0, sizeof( indx ) );
2415 
2416  // Iterate over all points; XLALNextLatticeTilingPoint() returns the index
2417  // (offset from 1) of the lowest dimension where the current point has changed
2418  xlalErrno = 0;
2419  int changed_ti_p1;
2420  while ( ( changed_ti_p1 = XLALNextLatticeTilingPoint( itr, NULL ) ) > 0 ) {
2421  const size_t changed_ti = changed_ti_p1 - 1;
2422 
2423  // Iterate over all dimensions where the current point has changed
2424  for ( size_t tj = changed_ti; tj < tn; ++tj ) {
2425 
2426  // If next index trie pointer is NULL, it needs to be initialised
2427  if ( next[tj] == NULL ) {
2428 
2429  // Get a pointer to the index trie which needs to be built:
2430  // - if 'tj' is non-zero, we should use the struct pointed to by 'next' in the lower dimension
2431  // - otherwise, this is the first point of the tiling, so initialise the base index trie
2432  LT_IndexTrie *trie = NULL;
2433  if ( tj > 0 ) {
2434  trie = next[tj - 1];
2435  } else {
2436  trie = loc->index_trie = XLALCalloc( 1, sizeof( *trie ) );
2437  XLAL_CHECK_NULL( loc->index_trie != NULL, XLAL_ENOMEM );
2438  }
2439 
2440  // Save the lower and upper integer point bounds
2441  trie->int_lower = itr->int_lower[tj];
2442  trie->int_upper = itr->int_upper[tj];
2443 
2444  // Save the sequential index of the current point up to this dimension
2445  trie->index = indx[tj];
2446 
2447  if ( tj + 1 < tn ) {
2448 
2449  // If we are below the highest dimension, allocate a new
2450  // array of index tries for the next highest dimension
2451  const size_t next_length = trie->int_upper - trie->int_lower + 1;
2452  trie->next = XLALCalloc( next_length, sizeof( *trie->next ) );
2453  XLAL_CHECK_NULL( trie->next != NULL, XLAL_ENOMEM );
2454 
2455  // Point 'next[tj]' to this array, for higher dimensions to use
2456  next[tj] = trie->next;
2457 
2458  }
2459 
2460  } else {
2461 
2462  // Otherwise, advance to the next index trie in the array
2463  ++next[tj];
2464 
2465  }
2466 
2467  // If we are below the highest dimension, set 'next' in the next highest
2468  // dimension to NULL, so that on the next loop a new array will be created
2469  if ( tj + 1 < tn ) {
2470  next[tj + 1] = NULL;
2471  }
2472 
2473  }
2474 
2475  // Increment sequential index in every higher dimension
2476  for ( size_t tj = changed_ti; tj < tn; ++tj ) {
2477  ++indx[tj];
2478  }
2479  indx[tn - 1] += itr->int_upper[tn - 1] - itr->int_lower[tn - 1];
2480 
2481  }
2483 
2484  // Cleanup
2486 
2487  }
2488 
2489  return loc;
2490 
2491 }
2492 
2494  LatticeTilingLocator *loc
2495 )
2496 {
2497  if ( loc ) {
2498  if ( loc->index_trie != NULL ) {
2499  LT_FreeIndexTrie( loc->index_trie );
2500  XLALFree( loc->index_trie );
2501  }
2502  XLALFree( loc );
2503  }
2504 }
2505 
2507  const LatticeTilingLocator *loc,
2508  const gsl_vector *point,
2509  gsl_vector *nearest_point,
2510  UINT8Vector *nearest_index
2511 )
2512 {
2513 
2514  // Check input
2515  XLAL_CHECK( loc != NULL, XLAL_EFAULT );
2516  XLAL_CHECK( point != NULL, XLAL_EFAULT );
2517  XLAL_CHECK( point->size == loc->ndim, XLAL_EINVAL );
2518  XLAL_CHECK( nearest_point == NULL || nearest_point->size == loc->ndim, XLAL_EINVAL );
2519  XLAL_CHECK( nearest_index == NULL || nearest_index->data != NULL, XLAL_EFAULT );
2520  XLAL_CHECK( nearest_index == NULL || nearest_index->length == loc->ndim, XLAL_EINVAL );
2521 
2522  const size_t n = loc->ndim;
2523 
2524  // Create local vector/matrix view for 'point'
2525  double local_point_array[n];
2526  gsl_vector_view local_point_vector = gsl_vector_view_array( local_point_array, n );
2527  gsl_matrix_view local_point_matrix = gsl_matrix_view_array( local_point_array, n, 1 );
2528  gsl_vector *local_point = &local_point_vector.vector;
2529  gsl_matrix *local_points = &local_point_matrix.matrix;
2530 
2531  // Create local vector/matrix view for 'nearest_point'
2532  double local_nearest_point_array[n];
2533  gsl_vector_view local_nearest_point_vector = gsl_vector_view_array( local_nearest_point_array, n );
2534  gsl_matrix_view local_nearest_point_matrix = gsl_matrix_view_array( local_nearest_point_array, n, 1 );
2535  gsl_vector *local_nearest_point = &local_nearest_point_vector.vector;
2536  gsl_matrix *local_nearest_points = &local_nearest_point_matrix.matrix;
2537 
2538  // Create local vector sequence view of 'nearest_index', if required
2539  UINT8VectorSequence nearest_indexes;
2540  UINT8VectorSequence *nearest_indexes_ptr = NULL;
2541  if ( nearest_index != NULL ) {
2542  nearest_indexes.vectorLength = n;
2543  nearest_indexes.length = 1;
2544  nearest_indexes.data = nearest_index->data;
2545  nearest_indexes_ptr = &nearest_indexes;
2546  }
2547 
2548  // Call LT_FindNearestPoints()
2549  gsl_vector_memcpy( local_point, point );
2550  XLAL_CHECK( LT_FindNearestPoints( loc, local_points, local_nearest_points, nearest_indexes_ptr, NULL, NULL ) == XLAL_SUCCESS, XLAL_EFUNC );
2551  if ( nearest_point != NULL ) {
2552  gsl_vector_memcpy( nearest_point, local_nearest_point );
2553  }
2554 
2555  return XLAL_SUCCESS;
2556 
2557 }
2558 
2560  const LatticeTilingLocator *loc,
2561  const gsl_matrix *points,
2562  gsl_matrix **nearest_points,
2563  UINT8VectorSequence **nearest_indexes
2564 )
2565 {
2566 
2567  // Check input
2568  XLAL_CHECK( loc != NULL, XLAL_EFAULT );
2569  XLAL_CHECK( points != NULL, XLAL_EFAULT );
2570  XLAL_CHECK( points->size1 == loc->ndim, XLAL_EINVAL );
2571  XLAL_CHECK( nearest_points != NULL, XLAL_EFAULT );
2572 
2573  const size_t n = loc->ndim;
2574  const size_t num_points = points->size2;
2575 
2576  // Resize or allocate nearest points matrix, if required, and create view of correct size
2577  if ( *nearest_points != NULL ) {
2578  if ( ( *nearest_points )->size1 != n || ( *nearest_points )->size2 < num_points ) {
2579  GFMAT( *nearest_points );
2580  *nearest_points = NULL;
2581  }
2582  }
2583  if ( *nearest_points == NULL ) {
2584  GAMAT( *nearest_points, n, num_points );
2585  }
2586  gsl_matrix_view nearest_points_view = gsl_matrix_submatrix( *nearest_points, 0, 0, n, num_points );
2587 
2588  // Resize or allocate nearest sequential index vector sequence, if required
2589  if ( nearest_indexes != NULL ) {
2590  if ( *nearest_indexes != NULL ) {
2591  if ( ( *nearest_indexes )->vectorLength != n || ( *nearest_indexes )->length < num_points ) {
2592  XLALDestroyUINT8VectorSequence( *nearest_indexes );
2593  *nearest_indexes = NULL;
2594  }
2595  }
2596  if ( *nearest_indexes == NULL ) {
2597  *nearest_indexes = XLALCreateUINT8VectorSequence( n, num_points );
2598  XLAL_CHECK( *nearest_indexes != NULL, XLAL_ENOMEM );
2599  }
2600  }
2601  UINT8VectorSequence *nearest_indexes_view = ( nearest_indexes != NULL ) ? *nearest_indexes : NULL;
2602 
2603  // Call LT_FindNearestPoints()
2604  XLAL_CHECK( LT_FindNearestPoints( loc, points, &nearest_points_view.matrix, nearest_indexes_view, NULL, NULL ) == XLAL_SUCCESS, XLAL_EFUNC );
2605 
2606  return XLAL_SUCCESS;
2607 
2608 }
2609 
2611  const LatticeTilingLocator *loc,
2612  const gsl_vector *point,
2613  const size_t dim,
2614  gsl_vector *nearest_point,
2615  UINT8 *nearest_index,
2616  INT4 *nearest_left,
2617  INT4 *nearest_right
2618 )
2619 {
2620 
2621  // Check input
2622  XLAL_CHECK( loc != NULL, XLAL_EFAULT );
2623  XLAL_CHECK( point != NULL, XLAL_EFAULT );
2624  XLAL_CHECK( point->size == loc->ndim, XLAL_EINVAL );
2625  XLAL_CHECK( dim < loc->ndim, XLAL_EINVAL );
2626  XLAL_CHECK( nearest_point != NULL, XLAL_EFAULT );
2627  XLAL_CHECK( nearest_point->size == loc->ndim, XLAL_EINVAL );
2628  XLAL_CHECK( nearest_index != NULL, XLAL_EFAULT );
2629  XLAL_CHECK( nearest_left != NULL, XLAL_EFAULT );
2630  XLAL_CHECK( nearest_right != NULL, XLAL_EFAULT );
2631 
2632  const size_t n = loc->ndim;
2633 
2634  // Create local vector/matrix view for 'point'
2635  double local_point_array[n];
2636  gsl_vector_view local_point_vector = gsl_vector_view_array( local_point_array, n );
2637  gsl_matrix_view local_point_matrix = gsl_matrix_view_array( local_point_array, n, 1 );
2638  gsl_vector *local_point = &local_point_vector.vector;
2639  gsl_matrix *local_points = &local_point_matrix.matrix;
2640 
2641  // Create local vector/matrix view for 'nearest_point'
2642  double local_nearest_point_array[n];
2643  gsl_vector_view local_nearest_point_vector = gsl_vector_view_array( local_nearest_point_array, n );
2644  gsl_matrix_view local_nearest_point_matrix = gsl_matrix_view_array( local_nearest_point_array, n, 1 );
2645  gsl_vector *local_nearest_point = &local_nearest_point_vector.vector;
2646  gsl_matrix *local_nearest_points = &local_nearest_point_matrix.matrix;
2647 
2648  // Create local vectors for sequential indexes and number of left/right points
2649  UINT8 nearest_indexes_data[n];
2650  UINT8VectorSequence nearest_indexes = { .length = 1, .vectorLength = n, .data = &nearest_indexes_data[0] };
2651  INT4 nearest_lefts_data[n];
2652  INT4VectorSequence nearest_lefts = { .length = 1, .vectorLength = n, .data = &nearest_lefts_data[0] };
2653  INT4 nearest_rights_data[n];
2654  INT4VectorSequence nearest_rights = { .length = 1, .vectorLength = n, .data = &nearest_rights_data[0] };
2655 
2656  // Call LT_FindNearestPoints()
2657  gsl_vector_memcpy( local_point, point );
2658  XLAL_CHECK( LT_FindNearestPoints( loc, local_points, local_nearest_points, &nearest_indexes, &nearest_lefts, &nearest_rights ) == XLAL_SUCCESS, XLAL_EFUNC );
2659  gsl_vector_memcpy( nearest_point, local_nearest_point );
2660  *nearest_index = ( dim > 0 ) ? nearest_indexes_data[dim - 1] : 0;
2661  *nearest_left = nearest_lefts_data[dim];
2662  *nearest_right = nearest_rights_data[dim];
2663 
2664  return XLAL_SUCCESS;
2665 
2666 }
2667 
2669  const LatticeTilingLocator *loc,
2670  FILE *file
2671 )
2672 {
2673 
2674  // Check input
2675  XLAL_CHECK( loc != NULL, XLAL_EFAULT );
2676  XLAL_CHECK( file != NULL, XLAL_EFAULT );
2677 
2678  const size_t tn = loc->tiled_ndim;
2679 
2680  // Return if index trie is NULL
2681  if ( tn == 0 || loc->index_trie == NULL ) {
2682  fprintf( file, "WARNING: index trie is NULL\n" );
2683  return XLAL_SUCCESS;
2684  }
2685 
2686  // Print index trie
2687  INT4 int_lower[tn];
2688  LT_PrintIndexTrie( loc->tiling, loc->index_trie, 0, file, int_lower );
2689 
2690  return XLAL_SUCCESS;
2691 
2692 }
2693 
2694 // Local Variables:
2695 // c-file-style: "linux"
2696 // c-basic-offset: 2
2697 // End:
#define __func__
log an I/O error, i.e.
int XLALFITSHeaderWriteUINT4(FITSFile UNUSED *file, const CHAR UNUSED *key, const UINT4 UNUSED value, const CHAR UNUSED *comment)
Definition: FITSFileIO.c:762
int XLALFITSTableWriteRow(FITSFile UNUSED *file, const void UNUSED *record)
Definition: FITSFileIO.c:2550
int XLALFITSHeaderWriteBOOLEAN(FITSFile UNUSED *file, const CHAR UNUSED *key, const BOOLEAN UNUSED value, const CHAR UNUSED *comment)
Definition: FITSFileIO.c:635
int XLALFITSTableReadRow(FITSFile UNUSED *file, void UNUSED *record, UINT8 UNUSED *rem_nrows)
Definition: FITSFileIO.c:2621
int XLALFITSHeaderReadBOOLEAN(FITSFile UNUSED *file, const CHAR UNUSED *key, BOOLEAN UNUSED *value)
Definition: FITSFileIO.c:668
int XLALFITSTableOpenWrite(FITSFile UNUSED *file, const CHAR UNUSED *name, const CHAR UNUSED *comment)
Definition: FITSFileIO.c:2162
int XLALFITSHeaderWriteUINT8(FITSFile UNUSED *file, const CHAR UNUSED *key, const UINT8 UNUSED value, const CHAR UNUSED *comment)
Definition: FITSFileIO.c:827
int XLALFITSHeaderReadUINT8(FITSFile UNUSED *file, const CHAR UNUSED *key, UINT8 UNUSED *value)
Definition: FITSFileIO.c:863
int XLALFITSHeaderReadUINT4(FITSFile UNUSED *file, const CHAR UNUSED *key, UINT4 UNUSED *value)
Definition: FITSFileIO.c:797
int XLALFITSTableOpenRead(FITSFile UNUSED *file, const CHAR UNUSED *name, UINT8 UNUSED *nrows)
Definition: FITSFileIO.c:2198
int j
int k
static void LT_SetPhysPoint(const LatticeTiling *tiling, gsl_matrix *phys_point_cache, gsl_vector *phys_point, const size_t dim, const double phys_point_dim)
Set value of physical point in a given dimension, and update cache.
static void LT_PrintIndexTrie(const LatticeTiling *tiling, const LT_IndexTrie *trie, const size_t ti, FILE *file, INT4 int_lower[])
Print one level of a lattice tiling index trie.
static void LT_FindBoundExtrema(const LatticeTiling *tiling, const size_t i, const size_t dim, gsl_matrix *phys_point_cache, gsl_vector *phys_point, double *phys_lower_minimum, double *phys_upper_maximum)
Find the extrema of the parameter-space bounds, by sampling the bounds around the current point.
static void LT_ReverseOrderRowsCols(gsl_matrix *A)
Reverse the order of both the rows and columns of the matrix A.
#define LT_CACHE_MAX_SIZE
Definition: LatticeTiling.c:44
static int LT_FindNearestPoints(const LatticeTilingLocator *loc, const gsl_matrix *points, gsl_matrix *nearest_points, UINT8VectorSequence *nearest_indexes, INT4VectorSequence *nearest_lefts, INT4VectorSequence *nearest_rights)
Locate the nearest points in a lattice tiling to a given set of points.
#define LT_DATA_MAX_SIZE
Definition: LatticeTiling.c:41
int XLALSetLatticeTilingBoundName(LatticeTiling *tiling, const size_t dim, const char *fmt,...)
static void LT_PollIndexTrie(const LatticeTiling *tiling, const LT_IndexTrie *trie, const size_t ti, const gsl_vector *point_int, INT4 *poll_nearest, double *poll_min_distance, INT4 *nearest)
Find the nearest point within the parameter-space bounds of the lattice tiling, by polling the neighb...
static void LT_CallBoundFunc(const LatticeTiling *tiling, const size_t dim, const gsl_matrix *phys_point_cache, const gsl_vector *phys_point, double *phys_lower, double *phys_upper)
Call the parameter-space bound function of a given dimension.
static void LT_ZeroStrictUpperTriangle(gsl_matrix *A)
Zero out the strictly upper triangular part of the matrix A.
static double ConstantBound(const void *data, const size_t dim UNUSED, const gsl_matrix *cache UNUSED, const gsl_vector *point UNUSED)
#define STRICT_BOUND_PADDING(b)
Definition: LatticeTiling.c:47
static void LT_FreeIndexTrie(LT_IndexTrie *trie)
Free memory pointed to by an index trie.
static int LT_InitFITSRecordTable(FITSFile *file)
Initialise FITS table for saving and restoring a lattice tiling iterator.
static int LT_StatsCallback(const bool first_call, const LatticeTiling *tiling, const LatticeTilingIterator *itr, const gsl_vector *point, const size_t changed_i, const void *param UNUSED, void *out)
Callback function for computing lattice tiling statistics.
#define D(j)
#define L(i, j)
UINT2 A
Definition: SFTnaming.c:46
const char * name
Definition: SearchTiming.c:93
#define fprintf
const double scale
multiplicative scaling factor of the coordinate
int s
const double Q
const double u
UINT8VectorSequence * XLALCreateUINT8VectorSequence(UINT4 length, UINT4 veclen)
void XLALDestroyUINT8VectorSequence(UINT8VectorSequence *vecseq)
#define XLAL_FITS_TABLE_COLUMN_BEGIN(record_type)
Definition: FITSFileIO.h:239
#define XLAL_FITS_TABLE_COLUMN_ADD(file, type, field)
Definition: FITSFileIO.h:243
struct tagFITSFile FITSFile
Representation of a FITS file.
Definition: FITSFileIO.h:54
unsigned char BOOLEAN
#define XLAL_NUM_ELEM(x)
uint64_t UINT8
double REAL8
#define XLAL_INIT_DECL(var,...)
int64_t INT8
uint32_t UINT4
int32_t INT4
int XLALPearsonHash(void *hval, const size_t hval_len, const void *data, const size_t data_len)
void * XLALCalloc(size_t m, size_t n)
void * XLALRealloc(void *p, size_t n)
void XLALFree(void *p)
#define LAL_INT4_FORMAT
#define LAL_UINT8_FORMAT
int XLALIsTiledLatticeTilingDimension(const LatticeTiling *tiling, const size_t dim)
Return >0 if a lattice tiling dimension is tiled (i.e.
int XLALSetLatticeTilingBound(LatticeTiling *tiling, const size_t dim, const LatticeTilingBound func, const size_t data_len, const void *data_lower, const void *data_upper)
Set a parameter-space bound on a dimension of the lattice tiling.
size_t XLALTotalLatticeTilingDimensions(const LatticeTiling *tiling)
Return the total number of dimensions of the lattice tiling.
int XLALNearestLatticeTilingPoint(const LatticeTilingLocator *loc, const gsl_vector *point, gsl_vector *nearest_point, UINT8Vector *nearest_index)
Locate the nearest point in a lattice tiling to a given point.
UINT8 XLALTotalLatticeTilingPoints(const LatticeTilingIterator *itr)
Return the total number of points covered by the lattice tiling iterator.
int XLALSetLatticeTilingConstantBound(LatticeTiling *tiling, const size_t dim, const double bound1, const double bound2)
Set a constant lattice tiling parameter-space bound, given by the minimum and maximum of the two supp...
int XLALSetTiledLatticeDimensionsFromTiling(LatticeTiling *tiling, const LatticeTiling *ref_tiling)
Set the tiled (i.e.
const LatticeTilingStats * XLALLatticeTilingStatistics(const LatticeTiling *tiling, const size_t dim)
Return statistics related to the number/value of lattice tiling points in a dimension.
int XLALSaveLatticeTilingIterator(const LatticeTilingIterator *itr, FITSFile *file, const char *name)
Save the state of a lattice tiling iterator to a FITS file.
REAL8 XLALLatticeTilingStepSize(const LatticeTiling *tiling, const size_t dim)
Return the step size of the lattice tiling in a given dimension, or 0 for non-tiled dimensions.
int XLALGetLatticeTilingBound(const LatticeTiling *tiling, const size_t dim, const gsl_vector *point, const bool padding, double *lower, double *upper)
Get a parameter-space bound on a dimension of the lattice tiling.
int XLALLatticeTilingDimensionByName(const LatticeTiling *tiling, const char *bound_name)
Return the index of the lattice tiling dimension which has the given name.
int XLALSetTilingLatticeAndMetric(LatticeTiling *tiling, const TilingLattice lattice, const gsl_matrix *metric, const double max_mismatch)
Set the tiling lattice, parameter-space metric, and maximum prescribed mismatch.
void(* LatticeTilingBoundCache)(const size_t dim, const gsl_vector *point, gsl_vector *cache)
Function which caches values required by a lattice tiling bound function.
Definition: LatticeTiling.h:92
double(* LatticeTilingBound)(const void *data, const size_t dim, const gsl_matrix *cache, const gsl_vector *point)
Function which returns a bound on a dimension of the lattice tiling.
Definition: LatticeTiling.h:82
int XLALNearestLatticeTilingBlock(const LatticeTilingLocator *loc, const gsl_vector *point, const size_t dim, gsl_vector *nearest_point, UINT8 *nearest_index, INT4 *nearest_left, INT4 *nearest_right)
Locate the nearest block in a lattice tiling to a given point.
void XLALDestroyLatticeTilingLocator(LatticeTilingLocator *loc)
Destroy a lattice tiling locator.
UINT8 XLALCurrentLatticeTilingIndex(const LatticeTilingIterator *itr)
Return the index of the current point in the lattice tiling iterator.
size_t XLALTiledLatticeTilingDimensions(const LatticeTiling *tiling)
Return the number of tiled dimensions of the lattice tiling.
size_t XLALLatticeTilingTiledDimension(const LatticeTiling *tiling, const size_t tiled_dim)
Return the dimension of the tiled lattice tiling dimension indexed by 'tiled_dim'.
int XLALRestoreLatticeTilingIterator(LatticeTilingIterator *itr, FITSFile *file, const char *name)
Restore the state of a lattice tiling iterator from a FITS file.
LatticeTilingIterator * XLALCreateLatticeTilingIterator(const LatticeTiling *tiling, const size_t itr_ndim)
Create a new lattice tiling iterator.
int XLALPerformLatticeTilingCallbacks(const LatticeTiling *tiling)
Perform all registered lattice tiling callbacks.
void XLALDestroyLatticeTilingIterator(LatticeTilingIterator *itr)
Destroy a lattice tiling iterator.
void XLALDestroyLatticeTiling(LatticeTiling *tiling)
Destroy a lattice tiling.
int XLALNextLatticeTilingPoint(LatticeTilingIterator *itr, gsl_vector *point)
Advance lattice tiling iterator, and optionally return the next point in point.
LatticeTilingLocator * XLALCreateLatticeTilingLocator(const LatticeTiling *tiling)
Create a new lattice tiling locator.
int XLALSetLatticeTilingAlternatingIterator(LatticeTilingIterator *itr, const bool alternating)
Set whether the lattice tiling iterator should alternate its iteration direction (i....
int XLALNearestLatticeTilingPoints(const LatticeTilingLocator *loc, const gsl_matrix *points, gsl_matrix **nearest_points, UINT8VectorSequence **nearest_indexes)
Locate the nearest points in a lattice tiling to a given set of points.
int LatticeTilingProgressLogLevel
Log level at which to print progress messages when counting templates and performing callbacks.
int XLALSetLatticeTilingBoundCacheFunction(LatticeTiling *tiling, const size_t dim, const LatticeTilingBoundCache func)
Set bound cache function for a lattice tiling parameter-space dimension.
int XLALCurrentLatticeTilingBlock(const LatticeTilingIterator *itr, const size_t dim, INT4 *left, INT4 *right)
Return indexes of the left-most and right-most points in the current block of points in the given dim...
LatticeTiling * XLALCreateLatticeTiling(const size_t ndim)
Create a new lattice tiling.
const UserChoices TilingLatticeChoices
Static array of all :tagTilingLattice choices, for use by the UserInput module parsing routines.
const void * XLALRegisterLatticeTilingCallback(LatticeTiling *tiling, const LatticeTilingCallback func, const size_t param_len, const void *param, const size_t out_len)
Register a callback function which can be used to compute properties of a lattice tiling.
int XLALPrintLatticeTilingIndexTrie(const LatticeTilingLocator *loc, FILE *file)
Print the internal index trie of a lattice tiling locator to the given file pointer.
int XLALSetLatticeTilingOrigin(LatticeTiling *tiling, const size_t dim, const double origin)
Set the physical parameter-space origin of the lattice tiling in the given dimension.
int XLALSetLatticeTilingRandomOriginOffsets(LatticeTiling *tiling, RandomParams *rng)
Offset the physical parameter-space origin of the lattice tiling by a random fraction of the lattice ...
int XLALResetLatticeTilingIterator(LatticeTilingIterator *itr)
Reset an iterator to the beginning of a lattice tiling.
const char * XLALLatticeTilingBoundName(const LatticeTiling *tiling, const size_t dim)
Get the name of a lattice tiling parameter-space dimension.
TilingLattice
Type of lattice to generate tiling with.
Definition: LatticeTiling.h:63
int XLALSetLatticeTilingPadding(LatticeTiling *tiling, const size_t dim, const double lower_bbox_pad, const double upper_bbox_pad, const int lower_intp_pad, const int upper_intp_pad, const int find_bound_extrema)
Control the padding of lattice tiling parameter-space bounds in the given dimension.
int XLALNextLatticeTilingPoints(LatticeTilingIterator *itr, gsl_matrix **points)
Advance lattice tiling iterator, and optionally return the next set of points in points.
int XLALRandomLatticeTilingPoints(const LatticeTiling *tiling, const double scale, RandomParams *rng, gsl_matrix *random_points)
Generate random points within the parameter space of the lattice tiling.
REAL8 XLALLatticeTilingBoundingBox(const LatticeTiling *tiling, const size_t dim)
Return the bounding box extent of the lattice tiling in a given dimension, or 0 for non-tiled dimensi...
int(* LatticeTilingCallback)(const bool first_call, const LatticeTiling *tiling, const LatticeTilingIterator *itr, const gsl_vector *point, const size_t changed_i, const void *param, void *out)
Callback function which can be used to compute properties of a lattice tiling.
@ TILING_LATTICE_ANSTAR
An-star ( ) lattice.
Definition: LatticeTiling.h:65
@ TILING_LATTICE_MAX
Definition: LatticeTiling.h:66
@ TILING_LATTICE_CUBIC
Cubic ( ) lattice.
Definition: LatticeTiling.h:64
void LogPrintf(LogLevel_t, const char *format,...) _LAL_GCC_PRINTF_FORMAT_(2
LOG_DEBUG
double XLALMetricDeterminant(const gsl_matrix *g_ij)
Compute the determinant of a metric .
Definition: MetricUtils.c:98
gsl_vector * XLALMetricEllipseBoundingBox(const gsl_matrix *g_ij, const double max_mismatch)
Compute the extent of the bounding box of the mismatch ellipse of a metric .
Definition: MetricUtils.c:131
static const INT4 m
REAL4 XLALUniformDeviate(RandomParams *params)
#define xlalErrno
int int int XLALPrintInfo(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
#define XLAL_CHECK_VAL(val, assertion,...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_CHECK_REAL8(assertion,...)
#define XLAL_CHECK_NULL(assertion,...)
#define XLAL_IS_REAL8_FAIL_NAN(val)
XLAL_ENOMEM
XLAL_SUCCESS
XLAL_EFAULT
XLAL_ERANGE
XLAL_EFUNC
XLAL_ESIZE
XLAL_EIO
XLAL_EINVAL
XLAL_EFAILED
XLAL_FAILURE
long long count
Definition: hwinject.c:371
list y
def func(x)
n
out
double alpha
Lattice tiling parameter-space bound for one dimension.
Definition: LatticeTiling.c:53
LatticeTilingBoundCache cache_func
Parameter space bound cache function.
Definition: LatticeTiling.c:61
bool name_set
True if the name of the parameter-space dimension has been set.
Definition: LatticeTiling.c:55
double upper_bbox_pad
Upper padding as multiple of metric ellipse bounding box.
Definition: LatticeTiling.c:63
double lower_bbox_pad
Lower padding as multiple of metric ellipse bounding box.
Definition: LatticeTiling.c:62
char data_upper[LT_DATA_MAX_SIZE]
Arbitrary data describing upper parameter-space bound.
Definition: LatticeTiling.c:60
bool is_tiled
True if the dimension is tiled, false if it is a single point.
Definition: LatticeTiling.c:56
char data_lower[LT_DATA_MAX_SIZE]
Arbitrary data describing lower parameter-space bound.
Definition: LatticeTiling.c:59
LatticeTilingBound func
Parameter space bound function.
Definition: LatticeTiling.c:57
size_t data_len
Length of arbitrary data describing parameter-space bounds.
Definition: LatticeTiling.c:58
bool find_bound_extrema
Whether to find the extrema of the parameter-space bounds.
Definition: LatticeTiling.c:66
UINT4 lower_intp_pad
Lower padding as integer number of points.
Definition: LatticeTiling.c:64
UINT4 upper_intp_pad
Upper padding as integer number of points.
Definition: LatticeTiling.c:65
Lattice tiling callback function and associated data.
Definition: LatticeTiling.c:72
char out[LT_DATA_MAX_SIZE]
Output data to be filled by callback function.
Definition: LatticeTiling.c:76
LatticeTilingCallback func
Callback function.
Definition: LatticeTiling.c:73
char param[LT_DATA_MAX_SIZE]
Arbitrary input data for use by callback function.
Definition: LatticeTiling.c:75
size_t param_len
Length of arbitrary input data for use by callback function.
Definition: LatticeTiling.c:74
FITS record for for saving and restoring a lattice tiling iterator.
Definition: LatticeTiling.c:82
INT4 int_lower
Current lower parameter-space bound in generating integers.
Definition: LatticeTiling.c:86
REAL8 phys_point
Current lattice point in physical coordinates.
Definition: LatticeTiling.c:84
INT4 int_point
Current lattice point in generating integers.
Definition: LatticeTiling.c:85
INT4 int_upper
Current upper parameter-space bound in generating integers.
Definition: LatticeTiling.c:87
INT4 checksum
Checksum of various data describing parameter-space bounds.
Definition: LatticeTiling.c:83
INT4 direction
Direction of iteration in each tiled parameter-space dimension.
Definition: LatticeTiling.c:88
Statistics related to the number/value of lattice tiling points in a dimension.
UINT4 min_points
Minimum number of points in this dimension.
double min_value
Minimum value of points in this dimension.
UINT8 total_points
Total number of points up to this dimension.
double max_value
Maximum value of points in this dimension.
const char * name
Name of parameter-space dimension.
UINT4 max_points
Maximum number of points in this dimension.
UINT8 * data
Lattice tiling index trie for one dimension.
Definition: LatticeTiling.c:95
UINT8 index
Sequential lattice tiling index up to this dimension.
Definition: LatticeTiling.c:98
INT4 int_upper
Upper integer point bound in this dimension.
Definition: LatticeTiling.c:97
LT_IndexTrie * next
Pointer to array of index tries for the next-highest dimension.
Definition: LatticeTiling.c:99
INT4 int_lower
Lower integer point bound in this dimension.
Definition: LatticeTiling.c:96
Describes a lattice tiling parameter-space bounds and metric.
gsl_matrix * int_from_phys
Transform to generating integers from physical coordinates.
gsl_matrix * phys_from_int
Transform to physical coordinates from generating integers.
size_t * tiled_idx
Index to tiled parameter-space dimensions.
gsl_vector * phys_bbox
Metric ellipse bounding box.
LT_Callback ** callbacks
Registered callbacks.
const LatticeTilingStats * stats
Lattice tiling statistics computed by default callback.
size_t ncallback
Number of registered callbacks.
TilingLattice lattice
Type of lattice to generate tiling with.
size_t * ncallback_done
Pointer to number of successfully performed callbacks (mutable)
LT_Bound * bounds
Array of parameter-space bound info for each dimension.
size_t ndim
Number of parameter-space dimensions.
size_t tiled_ndim
Number of tiled parameter-space dimensions.
gsl_vector * phys_origin
Parameter-space origin in physical coordinates.
gsl_vector * phys_origin_shift_frac
Fraction of step size to shift physical parameter-space origin.
gsl_matrix * tiled_generator
Lattice generator matrix in tiled dimensions.
Iterates over all points in a lattice tiling.
const LatticeTiling * tiling
Lattice tiling.
size_t itr_ndim
Number of parameter-space dimensions to iterate over.
bool alternating
If true, alternate iterator direction after every crossing.
UINT8 index
Index of current lattice tiling point.
gsl_vector * phys_point
Current lattice point in physical coordinates.
INT4 * int_upper
Current upper parameter-space bound in generating integers.
INT4 * int_lower
Current lower parameter-space bound in generating integers.
gsl_matrix * phys_sampl_cache
Cached values for sampling bounds with LT_FindBoundExtrema()
size_t tiled_itr_ndim
Number of tiled parameter-space dimensions to iterate over.
INT4 * int_point
Current lattice point in generating integers.
gsl_matrix * phys_point_cache
Cached values for computing physical bounds on current point.
gsl_vector * phys_sampl
Copy of physical point for sampling bounds with LT_FindBoundExtrema()
INT4 * direction
Direction of iteration in each tiled parameter-space dimension.
UINT4 state
Iterator state: 0=initialised, 1=in progress, 2=finished.
Locates the nearest point in a lattice tiling.
size_t ndim
Number of parameter-space dimensions.
LT_IndexTrie * index_trie
Trie for locating unique index of nearest point.
const LatticeTiling * tiling
Lattice tiling.
size_t tiled_ndim
Number of tiled parameter-space dimensions.