ler 0.4.1__py3-none-any.whl → 0.4.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ler might be problematic. Click here for more details.

Files changed (35) hide show
  1. ler/__init__.py +26 -26
  2. ler/gw_source_population/__init__.py +1 -0
  3. ler/gw_source_population/cbc_source_parameter_distribution.py +1076 -818
  4. ler/gw_source_population/cbc_source_redshift_distribution.py +619 -295
  5. ler/gw_source_population/jit_functions.py +484 -9
  6. ler/gw_source_population/sfr_with_time_delay.py +107 -0
  7. ler/image_properties/image_properties.py +44 -13
  8. ler/image_properties/multiprocessing_routine.py +5 -209
  9. ler/lens_galaxy_population/__init__.py +2 -0
  10. ler/lens_galaxy_population/epl_shear_cross_section.py +0 -0
  11. ler/lens_galaxy_population/jit_functions.py +101 -9
  12. ler/lens_galaxy_population/lens_galaxy_parameter_distribution.py +817 -885
  13. ler/lens_galaxy_population/lens_param_data/density_profile_slope_sl.txt +5000 -0
  14. ler/lens_galaxy_population/lens_param_data/external_shear_sl.txt +2 -0
  15. ler/lens_galaxy_population/lens_param_data/number_density_zl_zs.txt +48 -0
  16. ler/lens_galaxy_population/lens_param_data/optical_depth_epl_shear_vd_ewoud.txt +48 -0
  17. ler/lens_galaxy_population/mp copy.py +554 -0
  18. ler/lens_galaxy_population/mp.py +736 -138
  19. ler/lens_galaxy_population/optical_depth.py +2248 -616
  20. ler/rates/__init__.py +1 -2
  21. ler/rates/gwrates.py +129 -75
  22. ler/rates/ler.py +257 -116
  23. ler/utils/__init__.py +2 -0
  24. ler/utils/function_interpolation.py +322 -0
  25. ler/utils/gwsnr_training_data_generator.py +233 -0
  26. ler/utils/plots.py +1 -1
  27. ler/utils/test.py +1078 -0
  28. ler/utils/utils.py +553 -125
  29. {ler-0.4.1.dist-info → ler-0.4.3.dist-info}/METADATA +22 -9
  30. ler-0.4.3.dist-info/RECORD +34 -0
  31. {ler-0.4.1.dist-info → ler-0.4.3.dist-info}/WHEEL +1 -1
  32. ler/rates/ler copy.py +0 -2097
  33. ler-0.4.1.dist-info/RECORD +0 -25
  34. {ler-0.4.1.dist-info → ler-0.4.3.dist-info/licenses}/LICENSE +0 -0
  35. {ler-0.4.1.dist-info → ler-0.4.3.dist-info}/top_level.txt +0 -0
ler/utils/utils.py CHANGED
@@ -5,12 +5,14 @@ This module contains helper routines for other modules in the ler package.
5
5
 
6
6
  import os
7
7
  import pickle
8
+ import h5py
8
9
  import numpy as np
9
10
  import json
10
11
  from scipy.interpolate import interp1d
11
12
  from scipy.interpolate import CubicSpline
12
- from scipy.integrate import quad, cumtrapz
13
+ from scipy.integrate import quad
13
14
  from numba import njit
15
+ from importlib import resources
14
16
  # import datetime
15
17
 
16
18
 
@@ -52,6 +54,66 @@ class NumpyEncoder(json.JSONEncoder):
52
54
  return obj.tolist()
53
55
  return json.JSONEncoder.default(self, obj)
54
56
 
57
+ def load_pickle(file_name):
58
+ """Load a pickle file.
59
+
60
+ Parameters
61
+ ----------
62
+ file_name : `str`
63
+ pickle file name for storing the parameters.
64
+
65
+ Returns
66
+ ----------
67
+ param : `dict`
68
+ """
69
+ with open(file_name, "rb") as handle:
70
+ param = pickle.load(handle)
71
+
72
+ return param
73
+
74
+ def save_pickle(file_name, param):
75
+ """Save a dictionary as a pickle file.
76
+
77
+ Parameters
78
+ ----------
79
+ file_name : `str`
80
+ pickle file name for storing the parameters.
81
+ param : `dict`
82
+ dictionary to be saved as a pickle file.
83
+ """
84
+ with open(file_name, "wb") as handle:
85
+ pickle.dump(param, handle, protocol=pickle.HIGHEST_PROTOCOL)
86
+
87
+ # hdf5
88
+ def load_hdf5(file_name):
89
+ """Load a hdf5 file.
90
+
91
+ Parameters
92
+ ----------
93
+ file_name : `str`
94
+ hdf5 file name for storing the parameters.
95
+
96
+ Returns
97
+ ----------
98
+ param : `dict`
99
+ """
100
+
101
+ return h5py.File(file_name, 'r')
102
+
103
+ def save_hdf5(file_name, param):
104
+ """Save a dictionary as a hdf5 file.
105
+
106
+ Parameters
107
+ ----------
108
+ file_name : `str`
109
+ hdf5 file name for storing the parameters.
110
+ param : `dict`
111
+ dictionary to be saved as a hdf5 file.
112
+ """
113
+ with h5py.File(file_name, 'w') as f:
114
+ for key, value in param.items():
115
+ f.create_dataset(key, data=value)
116
+
55
117
  def load_json(file_name):
56
118
  """Load a json file.
57
119
 
@@ -78,9 +140,20 @@ def save_json(file_name, param):
78
140
  json file name for storing the parameters.
79
141
  param : `dict`
80
142
  dictionary to be saved as a json file.
143
+
144
+ Example
145
+ ----------
146
+ >>> from ler.utils import save_json, load_json
147
+ >>> param = dict_list = [{"name": "lens_mass", "type": "cubic_spline", "x": np.array([0, 1, 2, 3]), "y": [0, 1, 0, 1]},]
148
+ >>> save_json("param.json", param)
149
+ >>> param = load_json("param.json")
81
150
  """
82
- with open(file_name, "w", encoding="utf-8") as write_file:
83
- json.dump(param, write_file)
151
+ try:
152
+ with open(file_name, "w", encoding="utf-8") as write_file:
153
+ json.dump(param, write_file)
154
+ except:
155
+ with open(file_name, "w", encoding="utf-8") as write_file:
156
+ json.dump(param, write_file, indent=4, cls=NumpyEncoder)
84
157
 
85
158
  def append_json(file_name, new_dictionary, old_dictionary=None, replace=False):
86
159
  """
@@ -185,6 +258,28 @@ def get_param_from_json(json_file):
185
258
  param[key] = np.array(value)
186
259
  return param
187
260
 
261
+ def load_txt_from_module(package, directory, filename):
262
+ """
263
+ Function to load a specific dataset from a .txt file within the package
264
+
265
+ Parameters
266
+ ----------
267
+ package : str
268
+ name of the package
269
+ directory : str
270
+ name of the directory within the package
271
+ filename : str
272
+ name of the .txt file
273
+
274
+ Returns
275
+ ----------
276
+ data : `dict`
277
+ Dictionary loaded from the .txt file
278
+ """
279
+
280
+ with resources.path(package + '.' + directory, filename) as txt_path:
281
+ return np.loadtxt(txt_path)
282
+
188
283
  def rejection_sample(pdf, xmin, xmax, size=100, chunk_size=10000):
189
284
  """
190
285
  Helper function for rejection sampling from a pdf with maximum and minimum arguments.
@@ -426,7 +521,7 @@ def create_func_pdf_invcdf(x, y, category="function"):
426
521
  pdf = interp1d(x, y, kind="cubic", fill_value="extrapolate")
427
522
  return pdf
428
523
  # cdf
429
- cdf_values = cumtrapz(y, x, initial=0)
524
+ cdf_values = cumulative_trapezoid(y, x, initial=0)
430
525
  idx = np.argwhere(cdf_values > 0)[0][0]
431
526
  cdf_values = cdf_values[idx:]
432
527
  x = x[idx:]
@@ -552,7 +647,7 @@ def create_inv_cdf_array(x, y):
552
647
  idx = np.argwhere(np.isnan(y))
553
648
  x = np.delete(x, idx)
554
649
  y = np.delete(y, idx)
555
- cdf_values = cumtrapz(y, x, initial=0)
650
+ cdf_values = cumulative_trapezoid(y, x, initial=0)
556
651
  cdf_values = cdf_values / cdf_values[-1]
557
652
  # to remove duplicate values on x-axis before interpolation
558
653
  # idx = np.argwhere(cdf_values > 0)[0][0]
@@ -607,8 +702,9 @@ def create_conditioned_inv_cdf_array(x, conditioned_y, pdf_func):
607
702
  """
608
703
 
609
704
  list_ = []
705
+ size = len(x)
610
706
  for y in conditioned_y:
611
- phi = pdf_func(x,y)
707
+ phi = pdf_func(x,y*np.ones(size))
612
708
  list_.append(create_inv_cdf_array(x, phi))
613
709
 
614
710
  return np.array(list_)
@@ -735,11 +831,10 @@ def interpolator_pickle_path(
735
831
  path1 = full_dir + "/init_dict.pickle"
736
832
  if not os.path.exists(path1):
737
833
  dict_list = []
738
- with open(path1, "wb") as handle:
739
- pickle.dump(dict_list, handle, protocol=pickle.HIGHEST_PROTOCOL)
834
+ save_pickle(path1, dict_list)
740
835
 
741
836
  # check if the input dict is the same as one of the dict inside the pickle file
742
- param_dict_stored = pickle.load(open(path1, "rb"))
837
+ param_dict_stored = load_pickle(path1)
743
838
 
744
839
  path2 = full_dir
745
840
  len_ = len(param_dict_stored)
@@ -756,122 +851,34 @@ def interpolator_pickle_path(
756
851
  it_exist = False
757
852
  path_inv_cdf = path2 + "/" + interpolator_name + "_" + str(len_) + ".pickle"
758
853
  param_dict_stored.append(param_dict_given)
759
- with open(path1, "wb") as handle:
760
- pickle.dump(param_dict_stored, handle, protocol=pickle.HIGHEST_PROTOCOL)
854
+ save_pickle(path1, param_dict_stored)
761
855
 
762
856
  return path_inv_cdf, it_exist
763
857
 
764
- def interpolator_pdf_conditioned(x, conditioned_y, y_array, interpolator_list):
765
- """
766
- Function to find the pdf interpolator coefficients from the conditioned y.
767
-
768
- Parameters
769
- ----------
770
- x : `numpy.ndarray`
771
- x values.
772
- conditioned_y : `float`
773
- conditioned y value.
774
- y_array : `numpy.ndarray`
775
- y values.
776
- interpolator_list : `list`
777
- list of interpolators.
778
-
779
- Returns
780
- ----------
781
- interpolator_list[idx](x) : `numpy.ndarray`
782
- samples from the interpolator.
783
- """
784
- # find the index of z in zlist
785
- idx = np.searchsorted(y_array, conditioned_y)
786
-
787
- return interpolator_list[idx](x)
788
-
789
- def interpolator_sampler_conditioned(conditioned_y, y_array, interpolator_list, size=1000):
790
- """
791
- Function to find sampler interpolator coefficients from the conditioned y.
792
-
793
- Parameters
794
- ----------
795
- conditioned_y : `float`
796
- conditioned y value.
797
- y_array : `numpy.ndarray`
798
- y values.
799
- interpolator_list : `list`
800
- list of interpolators.
801
- size : `int`
802
- number of samples.
803
-
804
- Returns
805
- ----------
806
- """
807
-
808
- # find the index of z in zlist
809
- idx = np.searchsorted(y_array, conditioned_y)
810
- u = np.random.uniform(0, 1, size=size)
811
- return interpolator_list[idx](u)
812
-
813
- @njit
814
- def cubic_spline_interpolator(xnew, coefficients, x):
815
- """
816
- Function to interpolate using cubic spline.
817
-
818
- Parameters
819
- ----------
820
- xnew : `numpy.ndarray`
821
- new x values.
822
- coefficients : `numpy.ndarray`
823
- coefficients of the cubic spline.
824
- x : `numpy.ndarray`
825
- x values.
826
-
827
- Returns
828
- ----------
829
- result : `numpy.ndarray`
830
- interpolated values.
831
- """
832
-
833
- # Handling extrapolation
834
- i = np.searchsorted(x, xnew) - 1
835
- idx1 = xnew <= x[0]
836
- idx2 = xnew > x[-1]
837
- i[idx1] = 0
838
- i[idx2] = len(x) - 2
839
-
840
- # Calculate the relative position within the interval
841
- dx = xnew - x[i]
842
-
843
- # Calculate the interpolated value
844
- # Cubic polynomial: a + b*dx + c*dx^2 + d*dx^3
845
- a, b, c, d = coefficients[:, i]
846
- #result = a + b*dx + c*dx**2 + d*dx**3
847
- result = d + c*dx + b*dx**2 + a*dx**3
848
- return result
849
-
850
- @njit
851
- def inverse_transform_sampler(size, cdf, x):
852
- """
853
- Function to sample from the inverse transform method.
858
+ # def interpolator_pdf_conditioned(x, conditioned_y, y_array, interpolator_list):
859
+ # """
860
+ # Function to find the pdf interpolator coefficients from the conditioned y.
854
861
 
855
- Parameters
856
- ----------
857
- size : `int`
858
- number of samples.
859
- cdf : `numpy.ndarray`
860
- cdf values.
861
- x : `numpy.ndarray`
862
- x values.
862
+ # Parameters
863
+ # ----------
864
+ # x : `numpy.ndarray`
865
+ # x values.
866
+ # conditioned_y : `float`
867
+ # conditioned y value.
868
+ # y_array : `numpy.ndarray`
869
+ # y values.
870
+ # interpolator_list : `list`
871
+ # list of interpolators.
863
872
 
864
- Returns
865
- ----------
866
- samples : `numpy.ndarray`
867
- samples from the cdf.
868
- """
873
+ # Returns
874
+ # ----------
875
+ # interpolator_list[idx](x) : `numpy.ndarray`
876
+ # samples from the interpolator.
877
+ # """
878
+ # # find the index of z in zlist
879
+ # idx = np.searchsorted(y_array, conditioned_y)
869
880
 
870
- u = np.random.uniform(0, 1, size)
871
- idx = np.searchsorted(cdf, u)
872
- x1, x0, y1, y0 = cdf[idx], cdf[idx-1], x[idx], x[idx-1]
873
- samples = y0 + (y1 - y0) * (u - x0) / (x1 - x0)
874
- return samples
881
+ # return interpolator_list[idx](x)
875
882
 
876
883
  def batch_handler(size, batch_size, sampling_routine, output_jsonfile, save_batch=True, resume=False, param_name='parameters'):
877
884
  """
@@ -914,11 +921,6 @@ def batch_handler(size, batch_size, sampling_routine, output_jsonfile, save_batc
914
921
  else:
915
922
  num_batches = size // batch_size + 1
916
923
 
917
- print(
918
- f"chosen batch size = {batch_size} with total size = {size}"
919
- )
920
- print(f"There will be {num_batches} batche(s)")
921
-
922
924
  # note frac_batches+(num_batches-1)*batch_size = size
923
925
  if size > batch_size:
924
926
  frac_batches = size - (num_batches - 1) * batch_size
@@ -1014,4 +1016,430 @@ def create_batch_params(sampling_routine, frac_batches, dict_buffer, save_batch,
1014
1016
  # store all params in json file
1015
1017
  dict_buffer = append_json(file_name=output_jsonfile, new_dictionary=param, old_dictionary=dict_buffer, replace=not (resume))
1016
1018
 
1017
- return track_batches, dict_buffer
1019
+ return track_batches, dict_buffer
1020
+
1021
+ def monte_carlo_integration(function, uniform_prior, size=10000):
1022
+ """
1023
+ Function to perform Monte Carlo integration.
1024
+
1025
+ Parameters
1026
+ ----------
1027
+ function : `function`
1028
+ function to be integrated.
1029
+ prior : `function`
1030
+ prior function.
1031
+
1032
+ Returns
1033
+ ----------
1034
+ integral : `float`
1035
+ integral value.
1036
+ """
1037
+
1038
+ # sample from the prior
1039
+ x = uniform_prior(size=size)
1040
+ # calculate the function
1041
+ y = np.array([function(x[i]) for i in range(size)])
1042
+ # calculate the integral
1043
+ integral = np.mean(y)*(np.max(x)-np.min(x))
1044
+ return integral
1045
+
1046
+ @njit
1047
+ def cubic_spline_interpolator(xnew, coefficients, x):
1048
+ """
1049
+ Function to interpolate using cubic spline.
1050
+
1051
+ Parameters
1052
+ ----------
1053
+ xnew : `numpy.ndarray`
1054
+ new x values.
1055
+ coefficients : `numpy.ndarray`
1056
+ coefficients of the cubic spline.
1057
+ x : `numpy.ndarray`
1058
+ x values.
1059
+
1060
+ Returns
1061
+ ----------
1062
+ result : `numpy.ndarray`
1063
+ interpolated values.
1064
+ """
1065
+
1066
+ # Handling extrapolation
1067
+ i = np.searchsorted(x, xnew) - 1
1068
+ idx1 = xnew <= x[0]
1069
+ idx2 = xnew > x[-1]
1070
+ i[idx1] = 0
1071
+ i[idx2] = len(x) - 2
1072
+ #i = np.array(i, dtype=np.int32)
1073
+ # x = np.array(x, dtype=np.float64)
1074
+
1075
+ # Calculate the relative position within the interval
1076
+ #print(f"i = {i}\n xnew = {xnew}\n x = {x}")
1077
+ #print(x[i])
1078
+ dx = xnew - x[i]
1079
+
1080
+ # Calculate the interpolated value
1081
+ # Cubic polynomial: a + b*dx + c*dx^2 + d*dx^3
1082
+ # coefficients = np.array(coefficients, dtype=np.float64)
1083
+ #print(f"coefficients = {coefficients}")
1084
+ a, b, c, d = coefficients[:, i]
1085
+ #result = a + b*dx + c*dx**2 + d*dx**3
1086
+ result = d + c*dx + b*dx**2 + a*dx**3
1087
+ return result
1088
+
1089
+ @njit
1090
+ def pdf_cubic_spline_interpolator2d_array(xnew_array, ynew_array, norm_array, coefficients, x, y):
1091
+ """
1092
+ Function to calculate the interpolated value of snr_partialscaled given the mass ratio (ynew) and total mass (xnew). This is based off 2D bicubic spline interpolation.
1093
+
1094
+ Parameters
1095
+ ----------
1096
+ xnew_array : `numpy.ndarray`
1097
+ Total mass of the binary.
1098
+ ynew_array : `numpy.ndarray`
1099
+ Mass ratio of the binary.
1100
+ coefficients : `numpy.ndarray`
1101
+ Array of coefficients for the cubic spline interpolation.
1102
+ x : `numpy.ndarray`
1103
+ Array of total mass values for the coefficients.
1104
+ y : `numpy.ndarray`
1105
+ Array of mass ratio values for the coefficients.
1106
+
1107
+ Returns
1108
+ -------
1109
+ result : `float`
1110
+ Interpolated value of snr_partialscaled.
1111
+ """
1112
+ result_array = []
1113
+ for i in range(len(xnew_array)):
1114
+ xnew = xnew_array[i]
1115
+ ynew = ynew_array[i]
1116
+
1117
+ len_y = len(y)
1118
+ # find the index nearest to the ynew in y
1119
+ y_idx = np.searchsorted(y, ynew) - 1 if ynew > y[0] else 0
1120
+
1121
+ if (ynew>y[0]) and (ynew<y[1]):
1122
+ if ynew > y[y_idx] + (y[y_idx+1] - y[y_idx]) / 2:
1123
+ y_idx = y_idx + 1
1124
+ result_array.append(cubic_spline_interpolator(np.array([xnew]), coefficients[y_idx], x[y_idx])[0]/norm_array[y_idx])
1125
+ elif y_idx == 0: # lower end point
1126
+ result_array.append(cubic_spline_interpolator(np.array([xnew]), coefficients[0], x[0])[0]/norm_array[0])
1127
+ # print("a")
1128
+ elif y_idx+1 == len_y: # upper end point
1129
+ result_array.append(cubic_spline_interpolator(np.array([xnew]), coefficients[-1], x[-1])[0]/norm_array[-1])
1130
+ # print("b")
1131
+ elif y_idx+2 == len_y: # upper end point
1132
+ result_array.append(cubic_spline_interpolator(np.array([xnew]), coefficients[-1], x[-1])[0]/norm_array[-1])
1133
+ # print("b")
1134
+ else:
1135
+ y_idx1 = y_idx - 1
1136
+ y_idx2 = y_idx
1137
+ y_idx3 = y_idx + 1
1138
+ y_idx4 = y_idx + 2
1139
+ coeff_low, coeff_high = 4, 8
1140
+ # print("c")
1141
+ y1, y2, y3, y4 = y[y_idx1], y[y_idx2], y[y_idx3], y[y_idx4]
1142
+ z1 = cubic_spline_interpolator(np.array([xnew]), coefficients[y_idx1], x[y_idx1])[0]/norm_array[y_idx1]
1143
+ z2 = cubic_spline_interpolator(np.array([xnew]), coefficients[y_idx2], x[y_idx2])[0]/norm_array[y_idx2]
1144
+ z3 = cubic_spline_interpolator(np.array([xnew]), coefficients[y_idx3], x[y_idx3])[0]/norm_array[y_idx3]
1145
+ z4 = cubic_spline_interpolator(np.array([xnew]), coefficients[y_idx4], x[y_idx4])[0]/norm_array[y_idx4]
1146
+
1147
+ coeff = coefficients_generator_ler(y1, y2, y3, y4, z1, z2, z3, z4)
1148
+ matrixD = coeff[coeff_low:coeff_high]
1149
+ matrixB = np.array([ynew**3, ynew**2, ynew, 1])
1150
+ result_array.append(np.dot(matrixB, matrixD))
1151
+
1152
+ return np.array(result_array)
1153
+
1154
+ @njit
1155
+ def cubic_spline_interpolator2d_array(xnew_array, ynew_array, coefficients, x, y):
1156
+ """
1157
+ Function to calculate the interpolated value of snr_partialscaled given the mass ratio (ynew) and total mass (xnew). This is based off 2D bicubic spline interpolation.
1158
+
1159
+ Parameters
1160
+ ----------
1161
+ xnew_array : `numpy.ndarray`
1162
+ Total mass of the binary.
1163
+ ynew_array : `numpy.ndarray`
1164
+ Mass ratio of the binary.
1165
+ coefficients : `numpy.ndarray`
1166
+ Array of coefficients for the cubic spline interpolation.
1167
+ x : `numpy.ndarray`
1168
+ Array of total mass values for the coefficients.
1169
+ y : `numpy.ndarray`
1170
+ Array of mass ratio values for the coefficients.
1171
+
1172
+ Returns
1173
+ -------
1174
+ result : `float`
1175
+ Interpolated value of snr_partialscaled.
1176
+ """
1177
+ result_array = []
1178
+ for i in range(len(xnew_array)):
1179
+ xnew = xnew_array[i]
1180
+ ynew = ynew_array[i]
1181
+
1182
+ len_y = len(y)
1183
+ # find the index nearest to the ynew in y
1184
+ y_idx = np.searchsorted(y, ynew) - 1 if ynew > y[0] else 0
1185
+
1186
+ if (ynew>y[0]) and (ynew<y[1]):
1187
+ if ynew > y[y_idx] + (y[y_idx+1] - y[y_idx]) / 2:
1188
+ y_idx = y_idx + 1
1189
+ result_array.append(cubic_spline_interpolator(np.array([xnew]), coefficients[y_idx], x[y_idx])[0])
1190
+ # print(f"a) idx = {y_idx}")
1191
+ elif y_idx == 0: # lower end point
1192
+ result_array.append(cubic_spline_interpolator(np.array([xnew]), coefficients[0], x[0])[0])
1193
+ # print(f"a) idx = {y_idx}")
1194
+ elif y_idx+1 == len_y: # upper end point
1195
+ result_array.append(cubic_spline_interpolator(np.array([xnew]), coefficients[-1], x[-1])[0])
1196
+ # print(f"b) idx = {y_idx}")
1197
+ elif y_idx+2 == len_y: # upper end point
1198
+ result_array.append(cubic_spline_interpolator(np.array([xnew]), coefficients[-1], x[-1])[0])
1199
+ # print(f"c) idx = {y_idx}")
1200
+ else:
1201
+ y_idx1 = y_idx - 1
1202
+ y_idx2 = y_idx
1203
+ y_idx3 = y_idx + 1
1204
+ y_idx4 = y_idx + 2
1205
+ coeff_low, coeff_high = 4, 8
1206
+ # print(f"d) idx1, idx2, idx3, idx4 = {y_idx1}, {y_idx2}, {y_idx3}, {y_idx4}")
1207
+ y1, y2, y3, y4 = y[y_idx1], y[y_idx2], y[y_idx3], y[y_idx4]
1208
+ z1 = cubic_spline_interpolator(np.array([xnew]), coefficients[y_idx1], x[y_idx1])[0]
1209
+ z2 = cubic_spline_interpolator(np.array([xnew]), coefficients[y_idx2], x[y_idx2])[0]
1210
+ z3 = cubic_spline_interpolator(np.array([xnew]), coefficients[y_idx3], x[y_idx3])[0]
1211
+ z4 = cubic_spline_interpolator(np.array([xnew]), coefficients[y_idx4], x[y_idx4])[0]
1212
+
1213
+ coeff = coefficients_generator_ler(y1, y2, y3, y4, z1, z2, z3, z4)
1214
+ matrixD = coeff[coeff_low:coeff_high]
1215
+ matrixB = np.array([ynew**3, ynew**2, ynew, 1])
1216
+ result_array.append(np.dot(matrixB, matrixD))
1217
+
1218
+ return result_array
1219
+
1220
+ @njit
1221
+ def coefficients_generator_ler(y1, y2, y3, y4, z1, z2, z3, z4):
1222
+ """
1223
+ Function to generate the coefficients for the cubic spline interpolation of fn(y)=z.
1224
+
1225
+ Parameters
1226
+ ----------
1227
+ y1, y2, y3, y4, z1, z2, z3, z4: `float`
1228
+ Values of y and z for the cubic spline interpolation.
1229
+
1230
+ Returns
1231
+ -------
1232
+ coefficients: `numpy.ndarray`
1233
+ Coefficients for the cubic spline interpolation.
1234
+ """
1235
+ matrixA = np.array([
1236
+ [y1**3, y1**2, y1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
1237
+ [y2**3, y2**2, y2, 1, 0, 0, 0, 0, 0, 0, 0, 0],
1238
+ [0, 0, 0, 0, y2**3, y2**2, y2, 1, 0, 0, 0, 0],
1239
+ [0, 0, 0, 0, y3**3, y3**2, y3, 1, 0, 0, 0, 0],
1240
+ [0, 0, 0, 0, 0, 0, 0, 0, y3**3, y3**2, y3, 1],
1241
+ [0, 0, 0, 0, 0, 0, 0, 0, y4**3, y4**2, y4, 1],
1242
+ [3*y2**2, 2*y2, 1, 0, -3*y2**2, -2*y2, -1, 0, 0, 0, 0, 0],
1243
+ [0, 0, 0, 0, 3*y3**2, 2*y3, 1, 0, -3*y3**2, -2*y3, -1, 0],
1244
+ [6*y2, 2, 0, 0, -6*y2, -2, 0, 0, 0, 0, 0, 0],
1245
+ [0, 0, 0, 0, 6*y3, 2, 0, 0, -6*y3, -2, 0, 0],
1246
+ [6*y1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
1247
+ [0, 0, 0, 0, 0, 0, 0, 0, 6*y4, 2, 0, 0],
1248
+ ])
1249
+ matrixC = np.array([z1, z2, z2, z3, z3, z4, 0, 0, 0, 0, 0, 0])
1250
+ return np.dot(np.linalg.inv(matrixA), matrixC)
1251
+
1252
+ @njit
1253
+ def inverse_transform_sampler2d(size, conditioned_y, cdf2d, x2d, y1d):
1254
+ """
1255
+ Function to find sampler interpolator coefficients from the conditioned y.
1256
+
1257
+ Parameters
1258
+ ----------
1259
+ size: `int`
1260
+ Size of the sample.
1261
+ conditioned_y: `float`
1262
+ Conditioned y value.
1263
+ cdf2d: `numpy.ndarray`
1264
+ 2D array of cdf values.
1265
+ x2d: `numpy.ndarray`
1266
+ 2D array of x values.
1267
+ y1d: `numpy.ndarray`
1268
+ 1D array of y values.
1269
+
1270
+ Returns
1271
+ -------
1272
+ samples: `numpy.ndarray`
1273
+ Samples of the conditioned y.
1274
+ """
1275
+
1276
+ samples = np.zeros(size)
1277
+ for i, y in enumerate(conditioned_y):
1278
+ # find the index nearest to the conditioned_y in y1d
1279
+ y_idx = np.searchsorted(y1d, y) - 1 if y > y1d[0] else 0
1280
+ if y > y1d[y_idx] + (y1d[y_idx+1] - y1d[y_idx]) / 2:
1281
+ y_idx = y_idx + 1
1282
+
1283
+ # linear scaling
1284
+ # new cdf values
1285
+ x1, x0, y1, y0 = y1d[y_idx], y1d[y_idx-1], cdf2d[y_idx], cdf2d[y_idx-1]
1286
+ cdf = y0 + (y1 - y0) * (y - x0) / (x1 - x0)
1287
+ # new x values
1288
+ # x=x2d[0], if all rows are same
1289
+ x1, x0, y1, y0 = y1d[y_idx], y1d[y_idx-1], x2d[y_idx], x2d[y_idx-1]
1290
+ x = y0 + (y1 - y0) * (y - x0) / (x1 - x0)
1291
+
1292
+ while True:
1293
+ u = np.random.uniform(0, 1)
1294
+ idx = np.searchsorted(cdf, u)
1295
+ x1, x0, y1, y0 = cdf[idx], cdf[idx-1], x[idx], x[idx-1]
1296
+ samples[i] = y0 + (y1 - y0) * (u - x0) / (x1 - x0)
1297
+ if not np.isnan(samples[i]):
1298
+ break
1299
+ return samples
1300
+
1301
+
1302
+ @njit
1303
+ def inverse_transform_sampler(size, cdf, x):
1304
+ """
1305
+ Function to sample from the inverse transform method.
1306
+
1307
+ Parameters
1308
+ ----------
1309
+ size : `int`
1310
+ number of samples.
1311
+ cdf : `numpy.ndarray`
1312
+ cdf values.
1313
+ x : `numpy.ndarray`
1314
+ x values.
1315
+
1316
+ Returns
1317
+ ----------
1318
+ samples : `numpy.ndarray`
1319
+ samples from the cdf.
1320
+ """
1321
+
1322
+ u = np.random.uniform(0, 1, size)
1323
+ idx = np.searchsorted(cdf, u)
1324
+ x1, x0, y1, y0 = cdf[idx], cdf[idx-1], x[idx], x[idx-1]
1325
+ samples = y0 + (y1 - y0) * (u - x0) / (x1 - x0)
1326
+ return samples
1327
+
1328
+ @njit
1329
+ def normal_pdf(x, mean=0., std=0.05):
1330
+ """
1331
+ Calculate the value of a normal probability density function.
1332
+
1333
+ Parameters
1334
+ ----------
1335
+ x : `float` or `numpy.ndarray`
1336
+ The value(s) at which to evaluate the PDF.
1337
+ mean : `float`, optional
1338
+ The mean of the normal distribution. Default is 0.
1339
+ std : `float`, optional
1340
+ The standard deviation of the normal distribution. Default is 0.05.
1341
+
1342
+ Returns
1343
+ -------
1344
+ pdf : `float` or `numpy.ndarray`
1345
+ The probability density function value(s) at x.
1346
+ """
1347
+
1348
+ return (1 / (std * np.sqrt(2 * np.pi))) * np.exp(-0.5 * ((x - mean) / std)**2)
1349
+
1350
+
1351
+ @njit
1352
+ def normal_pdf_2d(x, y, mean_x=0., std_x=0.05, mean_y=0., std_y=0.05):
1353
+ """
1354
+ Calculate the value of a 2D normal probability density function.
1355
+
1356
+ Parameters
1357
+ ----------
1358
+ x : `float`
1359
+ The x-coordinate for which the PDF is evaluated.
1360
+ y : `float`
1361
+ The y-coordinate for which the PDF is evaluated.
1362
+ mean_x : `float`, optional
1363
+ The mean of the normal distribution along the x-axis. Default is 0.
1364
+ std_x : `float`, optional
1365
+ The standard deviation of the normal distribution along the x-axis. Default is 0.05.
1366
+ mean_y : `float`, optional
1367
+ The mean of the normal distribution along the y-axis. Default is 0.
1368
+ std_y : `float`, optional
1369
+ The standard deviation of the normal distribution along the y-axis. Default is 0.05.
1370
+
1371
+ Returns
1372
+ -------
1373
+ `float`
1374
+ The probability density function value at the given x and y coordinates.
1375
+ """
1376
+
1377
+ factor_x = (1 / (std_x * np.sqrt(2 * np.pi))) * np.exp(-0.5 * ((x - mean_x) / std_x)**2)
1378
+ factor_y = (1 / (std_y * np.sqrt(2 * np.pi))) * np.exp(-0.5 * ((y - mean_y) / std_y)**2)
1379
+ return factor_x * factor_y
1380
+
1381
+
1382
+ def load_txt_from_module(package, directory, filename):
1383
+ """
1384
+ """
1385
+
1386
+ with resources.path(package + '.' + directory, filename) as txt_path:
1387
+ return np.loadtxt(txt_path)
1388
+
1389
+ @njit
1390
+ def cumulative_trapezoid(y, x=None, dx=1.0, initial=0.0):
1391
+ """
1392
+ Compute the cumulative integral of a function using the trapezoidal rule.
1393
+ """
1394
+ if x is None:
1395
+ x = np.arange(len(y)) * dx
1396
+
1397
+ # Calculate the cumulative integral using trapezoidal rule
1398
+ cumsum = np.zeros_like(y)
1399
+ cumsum[0] = initial
1400
+ for i in range(1, len(y)):
1401
+ cumsum[i] = cumsum[i - 1] + (y[i - 1] + y[i]) * (x[i] - x[i - 1]) / 2.0
1402
+
1403
+ return cumsum
1404
+
1405
+ @njit
1406
+ def sample_from_powerlaw_distribution(size, alphans, mminns, mmaxns):
1407
+ """
1408
+ Inverse transform sampling for a power-law mass distribution:
1409
+ p(m) ∝ m^{-alphans}, m in [mminns, mmaxns]
1410
+
1411
+ Parameters
1412
+ ----------
1413
+ size : int
1414
+ Number of samples to generate.
1415
+ alphans : float
1416
+ Power-law index (α).
1417
+ mminns : float
1418
+ Minimum neutron star mass (lower bound).
1419
+ mmaxns : float
1420
+ Maximum neutron star mass (upper bound).
1421
+ random_state : int, np.random.Generator, or None
1422
+ Seed or random generator for reproducibility.
1423
+
1424
+ Returns
1425
+ -------
1426
+ m : ndarray
1427
+ Array of sampled neutron star masses.
1428
+ """
1429
+
1430
+ u = np.random.uniform(0, 1, size)
1431
+
1432
+ if alphans == 1.0:
1433
+ # Special case α=1
1434
+ m = mminns * (mmaxns / mminns) ** u
1435
+ elif alphans == 0.0:
1436
+ # Special case α=0 (uniform distribution)
1437
+ m = mminns + (mmaxns - mminns) * u
1438
+ else:
1439
+ pow1 = 1.0 - alphans
1440
+ mmin_pow = mminns ** pow1
1441
+ mmax_pow = mmaxns ** pow1
1442
+ m = (u * (mmax_pow - mmin_pow) + mmin_pow) ** (1.0 / pow1)
1443
+
1444
+ return m
1445
+