kNNpy.HelperFunctions
1#################################################################################################### 2 3#------------------- These libraries are required for evaluating the functions ------------------- 4 5import numpy as np 6from scipy import interpolate 7import time 8import pyfftw 9import warnings 10import smoothing_library as SL 11import MAS_library as MASL 12 13#################################################################################################### 14 15#-------------------------------------- Function Definitions ------------------------------------- 16 17#################################################################################################### 18 19def cdf_vol_knn(vol): 20 r''' 21 Returns interpolating functions for empirical CDFs of the given $k$-nearest neighbour distances. 22 23 Parameters 24 ---------- 25 vol : numpy float array of shape ``(n_query, n_kNN)`` 26 Sorted array of nearest neighbour distances, where 'n_query' is the number of query points and 'n_kNN' is the number of nearest neighbours queried. 27 28 Returns 29 ------- 30 cdf: list of function objects 31 list of interpolated empirical CDF functions that can be evaluated at desired distance bins. 32 ''' 33 34 #----------------------------------------------------------------------------------------------- 35 36 #Initialising a list to contain the interpolating functions 37 cdf = [] 38 39 #----------------------------------------------------------------------------------------------- 40 41 #Inferring the number of query points and nearest neighbours 42 n = vol.shape[0] 43 l = vol.shape[1] 44 45 #----------------------------------------------------------------------------------------------- 46 47 #Calculating the empirical CDF 48 gof = ((np.arange(0, n) + 1) / (n*1.0)) 49 for c in range(l): 50 ind = np.argsort(vol[:, c]) 51 s_vol= vol[ind, c] 52 #Calculating the interpolating function 53 cdf.append(interpolate.interp1d(s_vol, gof, kind='linear', bounds_error=False)) 54 55 return cdf 56 57#################################################################################################### 58 59def calc_kNN_CDF(vol, bins): 60 r''' 61 Returns the kNN-CDFs for the given nearest-neighbour distances, evaluated at the given distance bins. 62 63 Parameters 64 ---------- 65 vol : numpy float array of shape ``(n_query, n_kNN)`` 66 2D array containing sorted 1D arrays of nearest-neighbour distances, where 'n_query' is the number of query points and 'n_kNN' is the number of nearest-neighbours queried. `vol[:, i]` should be the array with the sorted $k_i^{th}$ nearest-neighbour distances. 67 bins : list of numpy float array 68 list of distance scale arrays at which the CDFs need to be evaluated (units must be same as in `vol`). 69 70 Returns 71 ------- 72 data : list of numpy float array 73 kNN-CDFs evaluated at the desired distance bins. ``data[i]`` is the $k_i$NN-CDF if ``vol[:, i]`` containts the $k_i^{th}$ nearest-neigbour distances. 74 ''' 75 76 #----------------------------------------------------------------------------------------------- 77 78 #Initialising the list of kNN-CDFs 79 data = [] 80 81 #----------------------------------------------------------------------------------------------- 82 83 #Computing the interpolated empirical CDFs using the nearest-neighbour distances 84 cdfs = cdf_vol_knn(vol) 85 86 #----------------------------------------------------------------------------------------------- 87 88 #Looping over the nearest-neighbour indices 89 for i in range(vol.shape[1]): 90 91 #------------------------------------------------------------------------------------------- 92 93 #Finding the minimum and maximum values of the NN distances 94 min_dist = np.min(vol[:, i]) 95 max_dist = np.max(vol[:, i]) 96 97 #------------------------------------------------------------------------------------------- 98 99 #Finding if any of the user-input bins lie outside the range spanned by the NN distances 100 bin_mask = np.searchsorted(bins[i], [min_dist, max_dist]) 101 if bin_mask[1]!=len(bins[i]): 102 if bins[i][bin_mask[1]] == max_dist: 103 bin_mask[1] += 1 104 105 #------------------------------------------------------------------------------------------- 106 107 NNcdf = np.zeros(len(bins[i])) 108 109 #Setting the value of the CDFs at scales smaller than the smallest NN distance to 0 110 NNcdf[:bin_mask[0]] = 0 111 112 NNcdf[bin_mask[0]:bin_mask[1]] = cdfs[i](bins[i][bin_mask[0]:bin_mask[1]]) 113 114 #Setting the value of the CDFs at scales larger than the largest NN distance to 1 115 NNcdf[bin_mask[1]:] = 1 116 117 data.append(NNcdf) 118 119 return data 120 121#################################################################################################### 122 123def create_query_3D(query_type, query_grid, BoxSize): 124 ''' 125 Generates an array of query points; can be either randomly drawn from a uniform distribution defined over the box or put on a uniform grid. 126 127 Parameters 128 ---------- 129 query_type : {'grid', 'random'}, str 130 the type of query points to be generated; should be 'grid' for query points defined on a uniform grid and 'random' for query points drawn from a uniform random distribution. 131 query_grid : int 132 the 1D size of the query points array; the total number of query points generated will be ``query_grid**3``. 133 BoxSize : float 134 the size of the 3D box of the input density field, in Mpc/h. Must be a positive real number, and must not be ``np.inf`` or ``np.nan``. 135 136 Returns 137 ------- 138 query_pos : numpy float array of shape ``(query_grid**3, 3)`` 139 array of query point positions. For each query point in the array, the first, second and third entries are the x, y and z coordinates respectively, in Mpc/h. 140 141 Raises 142 ------ 143 ValueError 144 if `BoxSize` is not a positive real number less than infinity. 145 ValueError 146 if an unknown query type is provided. 147 148 See Also 149 -------- 150 kNNpy.HelperFunctions.create_query_2DA : generates query points in 2D angular coordinates. 151 ''' 152 153 #Validating inputs 154 155 if np.isnan(BoxSize) or np.isinf(BoxSize) or BoxSize<=0.0: 156 raise ValueError(f"Invalid box size: {BoxSize}; please provide a positive real number less than infinity!") 157 158 #Creating the query points 159 160 if query_type == 'grid': 161 162 #Creating a grid of query points 163 x_ = np.linspace(0., BoxSize, query_grid, endpoint=False) 164 y_ = np.linspace(0., BoxSize, query_grid, endpoint=False) 165 z_ = np.linspace(0., BoxSize, query_grid, endpoint=False) 166 167 x, y, z = np.array(np.meshgrid(x_, y_, z_, indexing='xy')) 168 169 query_pos = np.zeros((query_grid**3, 3)) 170 query_pos[:, 0] = np.reshape(x, query_grid**3) 171 query_pos[:, 1] = np.reshape(y, query_grid**3) 172 query_pos[:, 2] = np.reshape(z, query_grid**3) 173 174 elif query_type == 'random': 175 176 #Creating a set of randomly distributed query points 177 query_pos = np.random.rand(query_grid**3, 3)*BoxSize 178 179 else: 180 raise ValueError(f"Unknown query type: {query_type}; please provide a valid query type") 181 182 return query_pos 183 184#################################################################################################### 185 186def smoothing_3D(field, Filter, grid, BoxSize, R=None, kmin=None, kmax=None, thickness=None, Verbose=False): 187 r''' 188 Smooths the given map at the given scale using a window function of choice in real or k-space. 189 190 Parameters 191 ---------- 192 field : numpy float array 193 the 3D array of the continuous field that needs to be smoothed. 194 Filter : string 195 the filter to be used for smoothing. 'Top-Hat', 'Gaussian', 'Shell' are for real space, and 'Top-Hat-k' is a top-hat filter in k-space. 196 grid : int 197 the grid size of the input density field, which should be field.shape[0] assuming a cubical box. 198 BoxSize : float 199 the size of the 3D box of the input density field, in Mpc/h. 200 R : float, optional 201 radial scale (in Mpc/h) at which the field is to be smoothed. Only use this parameter for real space smoothing. 202 kmin : float, optional 203 the minimum value of the wavenumber. Only use this parameter when 'Top-Hat-k' filter is used. 204 kmax : float, optional 205 the maximum value of the wavenumber. Only use this parameter when 'Top-Hat-k' filter is used. 206 thickness : float, optional 207 the thickness of the shell used for smoothing. Only use this parameter when 'Shell' filter is used. The smoothing is done using a shell with inner radius R-thickness/2 and outer radius R+thickness/2. 208 Verbose : bool, optional 209 if set to True, the time taken to complete each step of the calculation will be printed, by default False. 210 211 Returns 212 ------- 213 smoothed_field : numpy float array of shape field.shape 214 the smoothed field. 215 216 Raises 217 ------ 218 ValueError 219 If required parameters (like R, kmin, kmax, or thickness) are missing for the specified filter type. 220 ValueError 221 If the input field dimensions do not form a cubical box. 222 ValueError 223 If the grid size does not match the field dimensions. 224 ValueError 225 If an unknown filter name is provided. 226 227 Notes 228 ----- 229 - For real-space filters ('Top-Hat', 'Gaussian', 'Shell'), the radial scale R must be specified. 230 - For the 'Shell' filter, thickness must also be specified. 231 - For the 'Top-Hat-k' filter in Fourier space, kmin and kmax must be specified, while R and thickness are ignored. 232 - Any unused parameters will trigger warnings but not stop execution. 233 ''' 234 235 #----------------------------------------------------------------------------------------------- 236 237 if Verbose: 238 total_start_time = time.perf_counter() 239 print("\nStarting smoothing ...") 240 241 #----------------------------------------------------------------------------------------------- 242 243 if not (field.shape[0] == field.shape[1] == field.shape[2]): 244 raise ValueError("The box provided is not cubical.") 245 elif field.shape[0] != grid: 246 raise ValueError("Grid size provided does not match with dimensions of the cubical box.") 247 248 #----------------------------------------------------------------------------------------------- 249 250 if Filter in ['Top-Hat', 'Gaussian']: 251 if R is None: 252 raise ValueError(f"R must be provided for {Filter} filter.") 253 if kmin is not None or kmax is not None: 254 warnings.warn("kmin and kmax are not used for real-space filters and will be ignored.") 255 if thickness is not None: 256 warnings.warn("thickness is not used for real-space filters and will be ignored.") 257 258 W_k = SL.FT_filter(BoxSize, R, grid, Filter, threads=1) 259 field_k = pyfftw.interfaces.numpy_fft.rfftn(field) 260 smoothed_field_k = field_k * W_k 261 smoothed_field = pyfftw.interfaces.numpy_fft.irfftn(smoothed_field_k) 262 263 #----------------------------------------------------------------------------------------------- 264 265 elif Filter == 'Top-Hat-k': 266 if kmin is None or kmax is None: 267 raise ValueError("Both kmin and kmax must be provided for 'Top-Hat-k' filter.") 268 if R is not None: 269 warnings.warn("R is not used for 'Top-Hat-k' filter and will be ignored.") 270 if thickness is not None: 271 warnings.warn("thickness is not used for 'Top-Hat-k' filter and will be ignored.") 272 273 R = 0.0 274 W_k = SL.FT_filter(BoxSize, R, grid, Filter, kmin=kmin, kmax=kmax, threads=1) 275 field_k = pyfftw.interfaces.numpy_fft.rfftn(field) 276 smoothed_field_k = field_k * W_k 277 smoothed_field = pyfftw.interfaces.numpy_fft.irfftn(smoothed_field_k) 278 279 #----------------------------------------------------------------------------------------------- 280 281 elif Filter == 'Shell': 282 if R is None or thickness is None: 283 raise ValueError("Both R and thickness must be provided for 'Shell' filter.") 284 if kmin is not None or kmax is not None: 285 warnings.warn("kmin and kmax are not used for 'Shell' filter and will be ignored.") 286 287 if Verbose: 288 print("\nGenerating shell-smoothed field ...") 289 290 grid_cell_size = BoxSize / grid 291 field_k = pyfftw.interfaces.numpy_fft.rfftn(field) 292 293 x = np.fft.fftfreq(grid) * grid 294 y = np.fft.fftfreq(grid) * grid 295 z = np.fft.fftfreq(grid) * grid 296 X, Y, Z = np.meshgrid(x, y, z, indexing='ij') 297 r_grid = grid_cell_size * np.sqrt(X**2 + Y**2 + Z**2) 298 299 W = np.zeros((grid, grid, grid), dtype=np.float32) 300 W[(r_grid >= R - thickness/2) & (r_grid <= R + thickness/2)] = 1.0 301 W_k = pyfftw.interfaces.numpy_fft.rfftn(W) 302 303 smoothed_field_k = field_k * W_k 304 smoothed_field = pyfftw.interfaces.numpy_fft.irfftn(smoothed_field_k) / np.sum(W) 305 306 #----------------------------------------------------------------------------------------------- 307 308 else: 309 raise ValueError(f"Unknown filter: {Filter}") 310 311 #----------------------------------------------------------------------------------------------- 312 313 if Verbose: 314 print("Smoothing completed.") 315 print(f'Total time taken: {time.perf_counter() - total_start_time:.2e} s.') 316 317 #----------------------------------------------------------------------------------------------- 318 319 return smoothed_field 320 321#################################################################################################### 322 323def create_smoothed_field_dict_3D(field, Filter, grid, BoxSize, bins, thickness=None, Verbose=False): 324 r''' 325 Creates a dictionary containing the continuous field smoothed at various radial distance scales. 326 327 Parameters 328 ---------- 329 field : numpy float array 330 the 3D array of the continuous field that needs to be smoothed. 331 Filter : string 332 the filter to be used for smoothing. Valid filter types are: 'Top-Hat', 'Gaussian', 'Shell'. 333 grid : int 334 the grid size of the input density field, which should be field.shape[0] assuming a cubical box. 335 BoxSize : float 336 the size of the 3D box of the input density field, in Mpc/h. 337 bins : list of numpy float array 338 list of distances for each nearest neighbour. The $i^{th}$ element of the list should contain a numpy array of the desired distance scales for the $k_i^{th}$ nearest neighbour. 339 thickness : float, optional 340 the thickness of the shell used for smoothing. Only use this parameter when 'Shell' filter is used. The smoothing is done using a shell with inner radius R-thickness/2 and outer radius R+thickness/2. 341 Verbose : bool, optional 342 if set to True, the time taken to complete each step of the calculation will be printed, by default False. 343 344 Returns 345 ------- 346 SmoothedFieldDict : dict 347 dictionary containing the continuous field smoothed at various radial distance scales. For example, `SmoothedFieldDict['50.0']` represents the continuous map smoothed at a scale of 50 Mpc/h. 348 349 Raises 350 ------ 351 ValueError 352 If required parameters (like bins or thickness) are missing for the specified filter type. 353 ValueError 354 If the input field dimensions do not form a cubical box. 355 ValueError 356 If the grid size does not match the field dimensions. 357 ValueError 358 If an unknown filter name is provided. 359 360 Notes 361 ----- 362 - This function only works for the real space filters, so 'Top-Hat-k' is not a valid filter for this function. 363 - For the 'Shell' filter, thickness must be specified. 364 - Any unused parameters will trigger warnings but not stop execution. 365 ''' 366 367 #----------------------------------------------------------------------------------------------- 368 369 # This function is only for smoothing in real space 370 371 if Filter == 'Top-Hat-k': 372 raise ValueError(f"Unknown filter: {Filter}") 373 374 kmin = None 375 kmax = None 376 377 #----------------------------------------------------------------------------------------------- 378 379 if Verbose: 380 total_start = time.perf_counter() 381 print(f'\nSmoothing the density field over the radial distance scales...\n') 382 383 #----------------------------------------------------------------------------------------------- 384 385 #Initializing the dictionary 386 SmoothedFieldDict = {} 387 388 #----------------------------------------------------------------------------------------------- 389 390 #Looping over the nearest neighbour indices as inferred from the length of 'bins' 391 for i in range(len(bins)): 392 393 #------------------------------------------------------------------------------------------- 394 395 if Verbose: start = time.perf_counter() 396 397 #------------------------------------------------------------------------------------------- 398 399 for j, R in enumerate(bins[i]): 400 SmoothedFieldDict[str(R)] = \ 401 smoothing_3D(field, Filter, grid, BoxSize, R, kmin, kmax, thickness, Verbose) 402 403 #----------------------------------------------------------------------------------------------- 404 405 if Verbose: print('\nTotal time taken for all scales: {:.2e} s.'.format(time.perf_counter()-total_start)) 406 407 return SmoothedFieldDict 408 409#################################################################################################### 410 411def CIC_3D_Interp(pos, field, Boxsize): 412 r''' 413 Interpolates a 3D field onto particle positions using Cloud-In-Cell (CIC) interpolation. 414 415 Parameters 416 ---------- 417 field : numpy.ndarray of shape ``(Ng, Ng, Ng)`` 418 The 3D scalar field defined on a cubic grid with resolution 'Ng^3'. 419 420 pos : numpy.ndarray of shape ``(Np, 3)`` 421 The positions of 'Np' particles. The columns represent x, y, and z coordinates. Units in Mpc/h 422 423 Boxsize: float 424 The side length of the cubic volume in the same units as `pos`. 425 Returns 426 ------- 427 fieldI : numpy.ndarray of shape ``(Np,)`` 428 The interpolated field values at the given particle positions. 429 ''' 430 431 #----------------------------------------------------------------------------------------------- 432 433 # define the array containing the value of the density field at positions pos 434 density_interpolated = np.zeros(pos.shape[0], dtype=np.float32) 435 436 #----------------------------------------------------------------------------------------------- 437 438 # find the value of the density field at the positions pos 439 MASL.CIC_interp(field, Boxsize, pos, density_interpolated) 440 441 #----------------------------------------------------------------------------------------------- 442 443 return density_interpolated 444 445#################################################################################################### 446 447def kNN_excess_cross_corr(auto_cdf_list_1, auto_cdf_list_2, joint_cdf_list, k1_k2_list=None): 448 r''' 449 Computes the excess spatial cross-correlation (Banerjee & Abel 2023)[^1] between two tracers (discrete or continuous) from their joint kNN distributions (`joint_cdf_list`) and their respective kNN-CDFs (`auto_cdf_list_1`, `auto_cdf_list_2`). 450 451 Parameters 452 ---------- 453 auto_cdf_list_1 : list of numpy float array 454 auto kNN-CDFs of the first set of tracers. If `k1_k2_list` is not ``None``, The $i^{th}$ element should be the $k_1^i$NN-CDF if the $i^{th}$ element of `k1_k2_list` is ($k_1^i$, $k_2^i$). 455 auto_cdf_list_2 : list of numpy float array 456 auto kNN-CDFs of the second set of tracers. If `k1_k2_list` is not ``None``, The $i^{th}$ element should be the $k_2^i$NN-CDF if where the $i^{th}$ element of `k1_k2_list` is ($k_1^i$, $k_2^i$). 457 joint_cdf_list : list of numpy float array 458 joint kNN distributions of the two tracer sets. If `k1_k2_list` is not ``None``, The $i^{th}$ element should be the joint {$k_1^i$, $k_2^i$}NN-CDF, where the $i^{th}$ element of `k1_k2_list` is ($k_1^i$, $k_2^i$). 459 460 k1_k2_list : list of int tuples 461 describes the kind of cross-correlations being computed (see notes for more details), by default `None`. Should be not None only if dealing with tracer-tracer cross-correlations 462 463 Returns 464 ------- 465 psi_list : list of numpy float array 466 excess spatial cross-correlation between the two tracer sets. 467 468 Raises 469 ------ 470 ValueError 471 if `k1_k2_list` is not `None` and `len(joint_cdf_list)!=len(k1_k2_list)` 472 ValueError 473 if `k1_k2_list` is `None` and `len(joint_cdf_list)!=len(auto_cdf_list_1) or len(joint_cdf_list)!=len(auto_cdf_list_2)` 474 475 Notes 476 ----- 477 The parameter `k1_k2_list` describes the kind of cross-correlations being computed. It should be set to `None` for every scenario other than tracer-tracer cross-correlation, in which case it should provide the combinations of NN indices for the two tracers sets being cross-correlated. 478 479 For example, if you wish to compute the excess cross correlation for the joint {1,1}, {1,2} and {2,1}NN-CDFs, then set 480 481 k1_k2_list = [(1,1), (1,2), (2,1)] 482 483 Note that the inputs must be self-consistent, which means the following must be ``True`` 484 485 len(joint_cdf_list)==len(auto_cdf_list_1) and len(joint_cdf_list)==len(auto_cdf_list_2) and len(joint_cdf_list)==len(k1_k2_list) 486 487 For example, if 488 489 k1_k2_list = [(1,1), (1,2)] 490 491 then 492 493 len(auto_cdf_list_1) == 2 and len(auto_cdf_list_2) == 2 and len(joint_cdf_list) == 2 494 495 must hold, and the first (second) element of `joint_cdf_list` should be the joint {1,1}NN-CDF ({1,2}NN-CDF). 496 497 If `None` is passed for tracer-tracer cross-correlations, the correlations are assumed to be between the same NN indices (eg. {1,1}NN-CDF, {2,2}NN-CDF). 498 499 References 500 ---------- 501 [^1]: Arka Banerjee, Tom Abel, Tracer-field cross-correlations with k-nearest neighbour distributions, [Monthly Notices of the Royal Astronomical Society](https://doi.org/10.1093/mnras/stac3813), Volume 519, Issue 4, March 2023, Pages 4856–4868 502 ''' 503 504 #------------------------------------------------------------------------------------------- 505 506 psi_list = [] 507 508 #------------------------------------------------------------------------------------------- 509 510 #Check for consistency: 511 if k1_k2_list: 512 if len(joint_cdf_list)!=len(k1_k2_list) or len(joint_cdf_list)!=len(auto_cdf_list_1) or len(joint_cdf_list)!=len(auto_cdf_list_2): 513 raise ValueError('Inconsistent input shapes') 514 else: 515 if len(joint_cdf_list)!=len(auto_cdf_list_1) or len(joint_cdf_list)!=len(auto_cdf_list_2): 516 raise ValueError('Inconsistent input: shapes not compatible with each other') 517 518 for k in range(len(joint_cdf_list)): 519 psi_list.append(joint_cdf_list[k]/(auto_cdf_list_1[k]*auto_cdf_list_2[k])) 520 521 return psi_list 522 523#################################################################################################### 524 525#---------------------------------------- END OF PROGRAM! ---------------------------------------- 526 527####################################################################################################
20def cdf_vol_knn(vol): 21 r''' 22 Returns interpolating functions for empirical CDFs of the given $k$-nearest neighbour distances. 23 24 Parameters 25 ---------- 26 vol : numpy float array of shape ``(n_query, n_kNN)`` 27 Sorted array of nearest neighbour distances, where 'n_query' is the number of query points and 'n_kNN' is the number of nearest neighbours queried. 28 29 Returns 30 ------- 31 cdf: list of function objects 32 list of interpolated empirical CDF functions that can be evaluated at desired distance bins. 33 ''' 34 35 #----------------------------------------------------------------------------------------------- 36 37 #Initialising a list to contain the interpolating functions 38 cdf = [] 39 40 #----------------------------------------------------------------------------------------------- 41 42 #Inferring the number of query points and nearest neighbours 43 n = vol.shape[0] 44 l = vol.shape[1] 45 46 #----------------------------------------------------------------------------------------------- 47 48 #Calculating the empirical CDF 49 gof = ((np.arange(0, n) + 1) / (n*1.0)) 50 for c in range(l): 51 ind = np.argsort(vol[:, c]) 52 s_vol= vol[ind, c] 53 #Calculating the interpolating function 54 cdf.append(interpolate.interp1d(s_vol, gof, kind='linear', bounds_error=False)) 55 56 return cdf
Returns interpolating functions for empirical CDFs of the given $k$-nearest neighbour distances.
Parameters
- vol (numpy float array of shape
(n_query, n_kNN)
): Sorted array of nearest neighbour distances, where 'n_query' is the number of query points and 'n_kNN' is the number of nearest neighbours queried.
Returns
- cdf (list of function objects): list of interpolated empirical CDF functions that can be evaluated at desired distance bins.
60def calc_kNN_CDF(vol, bins): 61 r''' 62 Returns the kNN-CDFs for the given nearest-neighbour distances, evaluated at the given distance bins. 63 64 Parameters 65 ---------- 66 vol : numpy float array of shape ``(n_query, n_kNN)`` 67 2D array containing sorted 1D arrays of nearest-neighbour distances, where 'n_query' is the number of query points and 'n_kNN' is the number of nearest-neighbours queried. `vol[:, i]` should be the array with the sorted $k_i^{th}$ nearest-neighbour distances. 68 bins : list of numpy float array 69 list of distance scale arrays at which the CDFs need to be evaluated (units must be same as in `vol`). 70 71 Returns 72 ------- 73 data : list of numpy float array 74 kNN-CDFs evaluated at the desired distance bins. ``data[i]`` is the $k_i$NN-CDF if ``vol[:, i]`` containts the $k_i^{th}$ nearest-neigbour distances. 75 ''' 76 77 #----------------------------------------------------------------------------------------------- 78 79 #Initialising the list of kNN-CDFs 80 data = [] 81 82 #----------------------------------------------------------------------------------------------- 83 84 #Computing the interpolated empirical CDFs using the nearest-neighbour distances 85 cdfs = cdf_vol_knn(vol) 86 87 #----------------------------------------------------------------------------------------------- 88 89 #Looping over the nearest-neighbour indices 90 for i in range(vol.shape[1]): 91 92 #------------------------------------------------------------------------------------------- 93 94 #Finding the minimum and maximum values of the NN distances 95 min_dist = np.min(vol[:, i]) 96 max_dist = np.max(vol[:, i]) 97 98 #------------------------------------------------------------------------------------------- 99 100 #Finding if any of the user-input bins lie outside the range spanned by the NN distances 101 bin_mask = np.searchsorted(bins[i], [min_dist, max_dist]) 102 if bin_mask[1]!=len(bins[i]): 103 if bins[i][bin_mask[1]] == max_dist: 104 bin_mask[1] += 1 105 106 #------------------------------------------------------------------------------------------- 107 108 NNcdf = np.zeros(len(bins[i])) 109 110 #Setting the value of the CDFs at scales smaller than the smallest NN distance to 0 111 NNcdf[:bin_mask[0]] = 0 112 113 NNcdf[bin_mask[0]:bin_mask[1]] = cdfs[i](bins[i][bin_mask[0]:bin_mask[1]]) 114 115 #Setting the value of the CDFs at scales larger than the largest NN distance to 1 116 NNcdf[bin_mask[1]:] = 1 117 118 data.append(NNcdf) 119 120 return data
Returns the kNN-CDFs for the given nearest-neighbour distances, evaluated at the given distance bins.
Parameters
- vol (numpy float array of shape
(n_query, n_kNN)
): 2D array containing sorted 1D arrays of nearest-neighbour distances, where 'n_query' is the number of query points and 'n_kNN' is the number of nearest-neighbours queried.vol[:, i]
should be the array with the sorted $k_i^{th}$ nearest-neighbour distances. - bins (list of numpy float array):
list of distance scale arrays at which the CDFs need to be evaluated (units must be same as in
vol
).
Returns
- data (list of numpy float array):
kNN-CDFs evaluated at the desired distance bins.
data[i]
is the $k_i$NN-CDF ifvol[:, i]
containts the $k_i^{th}$ nearest-neigbour distances.
124def create_query_3D(query_type, query_grid, BoxSize): 125 ''' 126 Generates an array of query points; can be either randomly drawn from a uniform distribution defined over the box or put on a uniform grid. 127 128 Parameters 129 ---------- 130 query_type : {'grid', 'random'}, str 131 the type of query points to be generated; should be 'grid' for query points defined on a uniform grid and 'random' for query points drawn from a uniform random distribution. 132 query_grid : int 133 the 1D size of the query points array; the total number of query points generated will be ``query_grid**3``. 134 BoxSize : float 135 the size of the 3D box of the input density field, in Mpc/h. Must be a positive real number, and must not be ``np.inf`` or ``np.nan``. 136 137 Returns 138 ------- 139 query_pos : numpy float array of shape ``(query_grid**3, 3)`` 140 array of query point positions. For each query point in the array, the first, second and third entries are the x, y and z coordinates respectively, in Mpc/h. 141 142 Raises 143 ------ 144 ValueError 145 if `BoxSize` is not a positive real number less than infinity. 146 ValueError 147 if an unknown query type is provided. 148 149 See Also 150 -------- 151 kNNpy.HelperFunctions.create_query_2DA : generates query points in 2D angular coordinates. 152 ''' 153 154 #Validating inputs 155 156 if np.isnan(BoxSize) or np.isinf(BoxSize) or BoxSize<=0.0: 157 raise ValueError(f"Invalid box size: {BoxSize}; please provide a positive real number less than infinity!") 158 159 #Creating the query points 160 161 if query_type == 'grid': 162 163 #Creating a grid of query points 164 x_ = np.linspace(0., BoxSize, query_grid, endpoint=False) 165 y_ = np.linspace(0., BoxSize, query_grid, endpoint=False) 166 z_ = np.linspace(0., BoxSize, query_grid, endpoint=False) 167 168 x, y, z = np.array(np.meshgrid(x_, y_, z_, indexing='xy')) 169 170 query_pos = np.zeros((query_grid**3, 3)) 171 query_pos[:, 0] = np.reshape(x, query_grid**3) 172 query_pos[:, 1] = np.reshape(y, query_grid**3) 173 query_pos[:, 2] = np.reshape(z, query_grid**3) 174 175 elif query_type == 'random': 176 177 #Creating a set of randomly distributed query points 178 query_pos = np.random.rand(query_grid**3, 3)*BoxSize 179 180 else: 181 raise ValueError(f"Unknown query type: {query_type}; please provide a valid query type") 182 183 return query_pos
Generates an array of query points; can be either randomly drawn from a uniform distribution defined over the box or put on a uniform grid.
Parameters
- query_type ({'grid', 'random'}, str): the type of query points to be generated; should be 'grid' for query points defined on a uniform grid and 'random' for query points drawn from a uniform random distribution.
- query_grid (int):
the 1D size of the query points array; the total number of query points generated will be
query_grid**3
. - BoxSize (float):
the size of the 3D box of the input density field, in Mpc/h. Must be a positive real number, and must not be
np.inf
ornp.nan
.
Returns
- query_pos (numpy float array of shape
(query_grid**3, 3)
): array of query point positions. For each query point in the array, the first, second and third entries are the x, y and z coordinates respectively, in Mpc/h.
Raises
- ValueError: if
BoxSize
is not a positive real number less than infinity. - ValueError: if an unknown query type is provided.
See Also
kNNpy.HelperFunctions.create_query_2DA
: generates query points in 2D angular coordinates.
187def smoothing_3D(field, Filter, grid, BoxSize, R=None, kmin=None, kmax=None, thickness=None, Verbose=False): 188 r''' 189 Smooths the given map at the given scale using a window function of choice in real or k-space. 190 191 Parameters 192 ---------- 193 field : numpy float array 194 the 3D array of the continuous field that needs to be smoothed. 195 Filter : string 196 the filter to be used for smoothing. 'Top-Hat', 'Gaussian', 'Shell' are for real space, and 'Top-Hat-k' is a top-hat filter in k-space. 197 grid : int 198 the grid size of the input density field, which should be field.shape[0] assuming a cubical box. 199 BoxSize : float 200 the size of the 3D box of the input density field, in Mpc/h. 201 R : float, optional 202 radial scale (in Mpc/h) at which the field is to be smoothed. Only use this parameter for real space smoothing. 203 kmin : float, optional 204 the minimum value of the wavenumber. Only use this parameter when 'Top-Hat-k' filter is used. 205 kmax : float, optional 206 the maximum value of the wavenumber. Only use this parameter when 'Top-Hat-k' filter is used. 207 thickness : float, optional 208 the thickness of the shell used for smoothing. Only use this parameter when 'Shell' filter is used. The smoothing is done using a shell with inner radius R-thickness/2 and outer radius R+thickness/2. 209 Verbose : bool, optional 210 if set to True, the time taken to complete each step of the calculation will be printed, by default False. 211 212 Returns 213 ------- 214 smoothed_field : numpy float array of shape field.shape 215 the smoothed field. 216 217 Raises 218 ------ 219 ValueError 220 If required parameters (like R, kmin, kmax, or thickness) are missing for the specified filter type. 221 ValueError 222 If the input field dimensions do not form a cubical box. 223 ValueError 224 If the grid size does not match the field dimensions. 225 ValueError 226 If an unknown filter name is provided. 227 228 Notes 229 ----- 230 - For real-space filters ('Top-Hat', 'Gaussian', 'Shell'), the radial scale R must be specified. 231 - For the 'Shell' filter, thickness must also be specified. 232 - For the 'Top-Hat-k' filter in Fourier space, kmin and kmax must be specified, while R and thickness are ignored. 233 - Any unused parameters will trigger warnings but not stop execution. 234 ''' 235 236 #----------------------------------------------------------------------------------------------- 237 238 if Verbose: 239 total_start_time = time.perf_counter() 240 print("\nStarting smoothing ...") 241 242 #----------------------------------------------------------------------------------------------- 243 244 if not (field.shape[0] == field.shape[1] == field.shape[2]): 245 raise ValueError("The box provided is not cubical.") 246 elif field.shape[0] != grid: 247 raise ValueError("Grid size provided does not match with dimensions of the cubical box.") 248 249 #----------------------------------------------------------------------------------------------- 250 251 if Filter in ['Top-Hat', 'Gaussian']: 252 if R is None: 253 raise ValueError(f"R must be provided for {Filter} filter.") 254 if kmin is not None or kmax is not None: 255 warnings.warn("kmin and kmax are not used for real-space filters and will be ignored.") 256 if thickness is not None: 257 warnings.warn("thickness is not used for real-space filters and will be ignored.") 258 259 W_k = SL.FT_filter(BoxSize, R, grid, Filter, threads=1) 260 field_k = pyfftw.interfaces.numpy_fft.rfftn(field) 261 smoothed_field_k = field_k * W_k 262 smoothed_field = pyfftw.interfaces.numpy_fft.irfftn(smoothed_field_k) 263 264 #----------------------------------------------------------------------------------------------- 265 266 elif Filter == 'Top-Hat-k': 267 if kmin is None or kmax is None: 268 raise ValueError("Both kmin and kmax must be provided for 'Top-Hat-k' filter.") 269 if R is not None: 270 warnings.warn("R is not used for 'Top-Hat-k' filter and will be ignored.") 271 if thickness is not None: 272 warnings.warn("thickness is not used for 'Top-Hat-k' filter and will be ignored.") 273 274 R = 0.0 275 W_k = SL.FT_filter(BoxSize, R, grid, Filter, kmin=kmin, kmax=kmax, threads=1) 276 field_k = pyfftw.interfaces.numpy_fft.rfftn(field) 277 smoothed_field_k = field_k * W_k 278 smoothed_field = pyfftw.interfaces.numpy_fft.irfftn(smoothed_field_k) 279 280 #----------------------------------------------------------------------------------------------- 281 282 elif Filter == 'Shell': 283 if R is None or thickness is None: 284 raise ValueError("Both R and thickness must be provided for 'Shell' filter.") 285 if kmin is not None or kmax is not None: 286 warnings.warn("kmin and kmax are not used for 'Shell' filter and will be ignored.") 287 288 if Verbose: 289 print("\nGenerating shell-smoothed field ...") 290 291 grid_cell_size = BoxSize / grid 292 field_k = pyfftw.interfaces.numpy_fft.rfftn(field) 293 294 x = np.fft.fftfreq(grid) * grid 295 y = np.fft.fftfreq(grid) * grid 296 z = np.fft.fftfreq(grid) * grid 297 X, Y, Z = np.meshgrid(x, y, z, indexing='ij') 298 r_grid = grid_cell_size * np.sqrt(X**2 + Y**2 + Z**2) 299 300 W = np.zeros((grid, grid, grid), dtype=np.float32) 301 W[(r_grid >= R - thickness/2) & (r_grid <= R + thickness/2)] = 1.0 302 W_k = pyfftw.interfaces.numpy_fft.rfftn(W) 303 304 smoothed_field_k = field_k * W_k 305 smoothed_field = pyfftw.interfaces.numpy_fft.irfftn(smoothed_field_k) / np.sum(W) 306 307 #----------------------------------------------------------------------------------------------- 308 309 else: 310 raise ValueError(f"Unknown filter: {Filter}") 311 312 #----------------------------------------------------------------------------------------------- 313 314 if Verbose: 315 print("Smoothing completed.") 316 print(f'Total time taken: {time.perf_counter() - total_start_time:.2e} s.') 317 318 #----------------------------------------------------------------------------------------------- 319 320 return smoothed_field
Smooths the given map at the given scale using a window function of choice in real or k-space.
Parameters
- field (numpy float array): the 3D array of the continuous field that needs to be smoothed.
- Filter (string): the filter to be used for smoothing. 'Top-Hat', 'Gaussian', 'Shell' are for real space, and 'Top-Hat-k' is a top-hat filter in k-space.
- grid (int): the grid size of the input density field, which should be field.shape[0] assuming a cubical box.
- BoxSize (float): the size of the 3D box of the input density field, in Mpc/h.
- R (float, optional): radial scale (in Mpc/h) at which the field is to be smoothed. Only use this parameter for real space smoothing.
- kmin (float, optional): the minimum value of the wavenumber. Only use this parameter when 'Top-Hat-k' filter is used.
- kmax (float, optional): the maximum value of the wavenumber. Only use this parameter when 'Top-Hat-k' filter is used.
- thickness (float, optional): the thickness of the shell used for smoothing. Only use this parameter when 'Shell' filter is used. The smoothing is done using a shell with inner radius R-thickness/2 and outer radius R+thickness/2.
- Verbose (bool, optional): if set to True, the time taken to complete each step of the calculation will be printed, by default False.
Returns
- smoothed_field (numpy float array of shape field.shape): the smoothed field.
Raises
- ValueError: If required parameters (like R, kmin, kmax, or thickness) are missing for the specified filter type.
- ValueError: If the input field dimensions do not form a cubical box.
- ValueError: If the grid size does not match the field dimensions.
- ValueError: If an unknown filter name is provided.
Notes
- For real-space filters ('Top-Hat', 'Gaussian', 'Shell'), the radial scale R must be specified.
- For the 'Shell' filter, thickness must also be specified.
- For the 'Top-Hat-k' filter in Fourier space, kmin and kmax must be specified, while R and thickness are ignored.
- Any unused parameters will trigger warnings but not stop execution.
324def create_smoothed_field_dict_3D(field, Filter, grid, BoxSize, bins, thickness=None, Verbose=False): 325 r''' 326 Creates a dictionary containing the continuous field smoothed at various radial distance scales. 327 328 Parameters 329 ---------- 330 field : numpy float array 331 the 3D array of the continuous field that needs to be smoothed. 332 Filter : string 333 the filter to be used for smoothing. Valid filter types are: 'Top-Hat', 'Gaussian', 'Shell'. 334 grid : int 335 the grid size of the input density field, which should be field.shape[0] assuming a cubical box. 336 BoxSize : float 337 the size of the 3D box of the input density field, in Mpc/h. 338 bins : list of numpy float array 339 list of distances for each nearest neighbour. The $i^{th}$ element of the list should contain a numpy array of the desired distance scales for the $k_i^{th}$ nearest neighbour. 340 thickness : float, optional 341 the thickness of the shell used for smoothing. Only use this parameter when 'Shell' filter is used. The smoothing is done using a shell with inner radius R-thickness/2 and outer radius R+thickness/2. 342 Verbose : bool, optional 343 if set to True, the time taken to complete each step of the calculation will be printed, by default False. 344 345 Returns 346 ------- 347 SmoothedFieldDict : dict 348 dictionary containing the continuous field smoothed at various radial distance scales. For example, `SmoothedFieldDict['50.0']` represents the continuous map smoothed at a scale of 50 Mpc/h. 349 350 Raises 351 ------ 352 ValueError 353 If required parameters (like bins or thickness) are missing for the specified filter type. 354 ValueError 355 If the input field dimensions do not form a cubical box. 356 ValueError 357 If the grid size does not match the field dimensions. 358 ValueError 359 If an unknown filter name is provided. 360 361 Notes 362 ----- 363 - This function only works for the real space filters, so 'Top-Hat-k' is not a valid filter for this function. 364 - For the 'Shell' filter, thickness must be specified. 365 - Any unused parameters will trigger warnings but not stop execution. 366 ''' 367 368 #----------------------------------------------------------------------------------------------- 369 370 # This function is only for smoothing in real space 371 372 if Filter == 'Top-Hat-k': 373 raise ValueError(f"Unknown filter: {Filter}") 374 375 kmin = None 376 kmax = None 377 378 #----------------------------------------------------------------------------------------------- 379 380 if Verbose: 381 total_start = time.perf_counter() 382 print(f'\nSmoothing the density field over the radial distance scales...\n') 383 384 #----------------------------------------------------------------------------------------------- 385 386 #Initializing the dictionary 387 SmoothedFieldDict = {} 388 389 #----------------------------------------------------------------------------------------------- 390 391 #Looping over the nearest neighbour indices as inferred from the length of 'bins' 392 for i in range(len(bins)): 393 394 #------------------------------------------------------------------------------------------- 395 396 if Verbose: start = time.perf_counter() 397 398 #------------------------------------------------------------------------------------------- 399 400 for j, R in enumerate(bins[i]): 401 SmoothedFieldDict[str(R)] = \ 402 smoothing_3D(field, Filter, grid, BoxSize, R, kmin, kmax, thickness, Verbose) 403 404 #----------------------------------------------------------------------------------------------- 405 406 if Verbose: print('\nTotal time taken for all scales: {:.2e} s.'.format(time.perf_counter()-total_start)) 407 408 return SmoothedFieldDict
Creates a dictionary containing the continuous field smoothed at various radial distance scales.
Parameters
- field (numpy float array): the 3D array of the continuous field that needs to be smoothed.
- Filter (string): the filter to be used for smoothing. Valid filter types are: 'Top-Hat', 'Gaussian', 'Shell'.
- grid (int): the grid size of the input density field, which should be field.shape[0] assuming a cubical box.
- BoxSize (float): the size of the 3D box of the input density field, in Mpc/h.
- bins (list of numpy float array): list of distances for each nearest neighbour. The $i^{th}$ element of the list should contain a numpy array of the desired distance scales for the $k_i^{th}$ nearest neighbour.
- thickness (float, optional): the thickness of the shell used for smoothing. Only use this parameter when 'Shell' filter is used. The smoothing is done using a shell with inner radius R-thickness/2 and outer radius R+thickness/2.
- Verbose (bool, optional): if set to True, the time taken to complete each step of the calculation will be printed, by default False.
Returns
- SmoothedFieldDict (dict):
dictionary containing the continuous field smoothed at various radial distance scales. For example,
SmoothedFieldDict['50.0']
represents the continuous map smoothed at a scale of 50 Mpc/h.
Raises
- ValueError: If required parameters (like bins or thickness) are missing for the specified filter type.
- ValueError: If the input field dimensions do not form a cubical box.
- ValueError: If the grid size does not match the field dimensions.
- ValueError: If an unknown filter name is provided.
Notes
- This function only works for the real space filters, so 'Top-Hat-k' is not a valid filter for this function.
- For the 'Shell' filter, thickness must be specified.
- Any unused parameters will trigger warnings but not stop execution.
412def CIC_3D_Interp(pos, field, Boxsize): 413 r''' 414 Interpolates a 3D field onto particle positions using Cloud-In-Cell (CIC) interpolation. 415 416 Parameters 417 ---------- 418 field : numpy.ndarray of shape ``(Ng, Ng, Ng)`` 419 The 3D scalar field defined on a cubic grid with resolution 'Ng^3'. 420 421 pos : numpy.ndarray of shape ``(Np, 3)`` 422 The positions of 'Np' particles. The columns represent x, y, and z coordinates. Units in Mpc/h 423 424 Boxsize: float 425 The side length of the cubic volume in the same units as `pos`. 426 Returns 427 ------- 428 fieldI : numpy.ndarray of shape ``(Np,)`` 429 The interpolated field values at the given particle positions. 430 ''' 431 432 #----------------------------------------------------------------------------------------------- 433 434 # define the array containing the value of the density field at positions pos 435 density_interpolated = np.zeros(pos.shape[0], dtype=np.float32) 436 437 #----------------------------------------------------------------------------------------------- 438 439 # find the value of the density field at the positions pos 440 MASL.CIC_interp(field, Boxsize, pos, density_interpolated) 441 442 #----------------------------------------------------------------------------------------------- 443 444 return density_interpolated
Interpolates a 3D field onto particle positions using Cloud-In-Cell (CIC) interpolation.
Parameters
- field (numpy.ndarray of shape
(Ng, Ng, Ng)
): The 3D scalar field defined on a cubic grid with resolution 'Ng^3'. - pos (numpy.ndarray of shape
(Np, 3)
): The positions of 'Np' particles. The columns represent x, y, and z coordinates. Units in Mpc/h - Boxsize (float):
The side length of the cubic volume in the same units as
pos
.
Returns
- fieldI (numpy.ndarray of shape
(Np,)
): The interpolated field values at the given particle positions.
448def kNN_excess_cross_corr(auto_cdf_list_1, auto_cdf_list_2, joint_cdf_list, k1_k2_list=None): 449 r''' 450 Computes the excess spatial cross-correlation (Banerjee & Abel 2023)[^1] between two tracers (discrete or continuous) from their joint kNN distributions (`joint_cdf_list`) and their respective kNN-CDFs (`auto_cdf_list_1`, `auto_cdf_list_2`). 451 452 Parameters 453 ---------- 454 auto_cdf_list_1 : list of numpy float array 455 auto kNN-CDFs of the first set of tracers. If `k1_k2_list` is not ``None``, The $i^{th}$ element should be the $k_1^i$NN-CDF if the $i^{th}$ element of `k1_k2_list` is ($k_1^i$, $k_2^i$). 456 auto_cdf_list_2 : list of numpy float array 457 auto kNN-CDFs of the second set of tracers. If `k1_k2_list` is not ``None``, The $i^{th}$ element should be the $k_2^i$NN-CDF if where the $i^{th}$ element of `k1_k2_list` is ($k_1^i$, $k_2^i$). 458 joint_cdf_list : list of numpy float array 459 joint kNN distributions of the two tracer sets. If `k1_k2_list` is not ``None``, The $i^{th}$ element should be the joint {$k_1^i$, $k_2^i$}NN-CDF, where the $i^{th}$ element of `k1_k2_list` is ($k_1^i$, $k_2^i$). 460 461 k1_k2_list : list of int tuples 462 describes the kind of cross-correlations being computed (see notes for more details), by default `None`. Should be not None only if dealing with tracer-tracer cross-correlations 463 464 Returns 465 ------- 466 psi_list : list of numpy float array 467 excess spatial cross-correlation between the two tracer sets. 468 469 Raises 470 ------ 471 ValueError 472 if `k1_k2_list` is not `None` and `len(joint_cdf_list)!=len(k1_k2_list)` 473 ValueError 474 if `k1_k2_list` is `None` and `len(joint_cdf_list)!=len(auto_cdf_list_1) or len(joint_cdf_list)!=len(auto_cdf_list_2)` 475 476 Notes 477 ----- 478 The parameter `k1_k2_list` describes the kind of cross-correlations being computed. It should be set to `None` for every scenario other than tracer-tracer cross-correlation, in which case it should provide the combinations of NN indices for the two tracers sets being cross-correlated. 479 480 For example, if you wish to compute the excess cross correlation for the joint {1,1}, {1,2} and {2,1}NN-CDFs, then set 481 482 k1_k2_list = [(1,1), (1,2), (2,1)] 483 484 Note that the inputs must be self-consistent, which means the following must be ``True`` 485 486 len(joint_cdf_list)==len(auto_cdf_list_1) and len(joint_cdf_list)==len(auto_cdf_list_2) and len(joint_cdf_list)==len(k1_k2_list) 487 488 For example, if 489 490 k1_k2_list = [(1,1), (1,2)] 491 492 then 493 494 len(auto_cdf_list_1) == 2 and len(auto_cdf_list_2) == 2 and len(joint_cdf_list) == 2 495 496 must hold, and the first (second) element of `joint_cdf_list` should be the joint {1,1}NN-CDF ({1,2}NN-CDF). 497 498 If `None` is passed for tracer-tracer cross-correlations, the correlations are assumed to be between the same NN indices (eg. {1,1}NN-CDF, {2,2}NN-CDF). 499 500 References 501 ---------- 502 [^1]: Arka Banerjee, Tom Abel, Tracer-field cross-correlations with k-nearest neighbour distributions, [Monthly Notices of the Royal Astronomical Society](https://doi.org/10.1093/mnras/stac3813), Volume 519, Issue 4, March 2023, Pages 4856–4868 503 ''' 504 505 #------------------------------------------------------------------------------------------- 506 507 psi_list = [] 508 509 #------------------------------------------------------------------------------------------- 510 511 #Check for consistency: 512 if k1_k2_list: 513 if len(joint_cdf_list)!=len(k1_k2_list) or len(joint_cdf_list)!=len(auto_cdf_list_1) or len(joint_cdf_list)!=len(auto_cdf_list_2): 514 raise ValueError('Inconsistent input shapes') 515 else: 516 if len(joint_cdf_list)!=len(auto_cdf_list_1) or len(joint_cdf_list)!=len(auto_cdf_list_2): 517 raise ValueError('Inconsistent input: shapes not compatible with each other') 518 519 for k in range(len(joint_cdf_list)): 520 psi_list.append(joint_cdf_list[k]/(auto_cdf_list_1[k]*auto_cdf_list_2[k])) 521 522 return psi_list
Computes the excess spatial cross-correlation (Banerjee & Abel 2023)1 between two tracers (discrete or continuous) from their joint kNN distributions (joint_cdf_list
) and their respective kNN-CDFs (auto_cdf_list_1
, auto_cdf_list_2
).
Parameters
- auto_cdf_list_1 (list of numpy float array):
auto kNN-CDFs of the first set of tracers. If
k1_k2_list
is notNone
, The $i^{th}$ element should be the $k_1^i$NN-CDF if the $i^{th}$ element ofk1_k2_list
is ($k_1^i$, $k_2^i$). - auto_cdf_list_2 (list of numpy float array):
auto kNN-CDFs of the second set of tracers. If
k1_k2_list
is notNone
, The $i^{th}$ element should be the $k_2^i$NN-CDF if where the $i^{th}$ element ofk1_k2_list
is ($k_1^i$, $k_2^i$). - joint_cdf_list (list of numpy float array):
joint kNN distributions of the two tracer sets. If
k1_k2_list
is notNone
, The $i^{th}$ element should be the joint {$k_1^i$, $k_2^i$}NN-CDF, where the $i^{th}$ element ofk1_k2_list
is ($k_1^i$, $k_2^i$). - k1_k2_list (list of int tuples):
describes the kind of cross-correlations being computed (see notes for more details), by default
None
. Should be not None only if dealing with tracer-tracer cross-correlations
Returns
- psi_list (list of numpy float array): excess spatial cross-correlation between the two tracer sets.
Raises
- ValueError: if
k1_k2_list
is notNone
andlen(joint_cdf_list)!=len(k1_k2_list)
- ValueError: if
k1_k2_list
isNone
andlen(joint_cdf_list)!=len(auto_cdf_list_1) or len(joint_cdf_list)!=len(auto_cdf_list_2)
Notes
The parameter k1_k2_list
describes the kind of cross-correlations being computed. It should be set to None
for every scenario other than tracer-tracer cross-correlation, in which case it should provide the combinations of NN indices for the two tracers sets being cross-correlated.
For example, if you wish to compute the excess cross correlation for the joint {1,1}, {1,2} and {2,1}NN-CDFs, then set
k1_k2_list = [(1,1), (1,2), (2,1)]
Note that the inputs must be self-consistent, which means the following must be True
len(joint_cdf_list)==len(auto_cdf_list_1) and len(joint_cdf_list)==len(auto_cdf_list_2) and len(joint_cdf_list)==len(k1_k2_list)
For example, if
k1_k2_list = [(1,1), (1,2)]
then
len(auto_cdf_list_1) == 2 and len(auto_cdf_list_2) == 2 and len(joint_cdf_list) == 2
must hold, and the first (second) element of joint_cdf_list
should be the joint {1,1}NN-CDF ({1,2}NN-CDF).
If None
is passed for tracer-tracer cross-correlations, the correlations are assumed to be between the same NN indices (eg. {1,1}NN-CDF, {2,2}NN-CDF).
References
-
Arka Banerjee, Tom Abel, Tracer-field cross-correlations with k-nearest neighbour distributions, Monthly Notices of the Royal Astronomical Society, Volume 519, Issue 4, March 2023, Pages 4856–4868 ↩