ler 0.4.2__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 +1073 -815
  4. ler/gw_source_population/cbc_source_redshift_distribution.py +618 -294
  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 +41 -12
  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 +813 -881
  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 +126 -72
  22. ler/rates/ler.py +218 -111
  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 +492 -125
  29. {ler-0.4.2.dist-info → ler-0.4.3.dist-info}/METADATA +30 -17
  30. ler-0.4.3.dist-info/RECORD +34 -0
  31. {ler-0.4.2.dist-info → ler-0.4.3.dist-info}/WHEEL +1 -1
  32. ler/rates/ler copy.py +0 -2097
  33. ler-0.4.2.dist-info/RECORD +0 -25
  34. {ler-0.4.2.dist-info → ler-0.4.3.dist-info/licenses}/LICENSE +0 -0
  35. {ler-0.4.2.dist-info → ler-0.4.3.dist-info}/top_level.txt +0 -0
ler/utils/utils.py CHANGED
@@ -10,8 +10,9 @@ import numpy as np
10
10
  import json
11
11
  from scipy.interpolate import interp1d
12
12
  from scipy.interpolate import CubicSpline
13
- from scipy.integrate import quad, cumtrapz
13
+ from scipy.integrate import quad
14
14
  from numba import njit
15
+ from importlib import resources
15
16
  # import datetime
16
17
 
17
18
 
@@ -139,9 +140,20 @@ def save_json(file_name, param):
139
140
  json file name for storing the parameters.
140
141
  param : `dict`
141
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")
142
150
  """
143
- with open(file_name, "w", encoding="utf-8") as write_file:
144
- 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)
145
157
 
146
158
  def append_json(file_name, new_dictionary, old_dictionary=None, replace=False):
147
159
  """
@@ -246,6 +258,28 @@ def get_param_from_json(json_file):
246
258
  param[key] = np.array(value)
247
259
  return param
248
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
+
249
283
  def rejection_sample(pdf, xmin, xmax, size=100, chunk_size=10000):
250
284
  """
251
285
  Helper function for rejection sampling from a pdf with maximum and minimum arguments.
@@ -487,7 +521,7 @@ def create_func_pdf_invcdf(x, y, category="function"):
487
521
  pdf = interp1d(x, y, kind="cubic", fill_value="extrapolate")
488
522
  return pdf
489
523
  # cdf
490
- cdf_values = cumtrapz(y, x, initial=0)
524
+ cdf_values = cumulative_trapezoid(y, x, initial=0)
491
525
  idx = np.argwhere(cdf_values > 0)[0][0]
492
526
  cdf_values = cdf_values[idx:]
493
527
  x = x[idx:]
@@ -613,7 +647,7 @@ def create_inv_cdf_array(x, y):
613
647
  idx = np.argwhere(np.isnan(y))
614
648
  x = np.delete(x, idx)
615
649
  y = np.delete(y, idx)
616
- cdf_values = cumtrapz(y, x, initial=0)
650
+ cdf_values = cumulative_trapezoid(y, x, initial=0)
617
651
  cdf_values = cdf_values / cdf_values[-1]
618
652
  # to remove duplicate values on x-axis before interpolation
619
653
  # idx = np.argwhere(cdf_values > 0)[0][0]
@@ -668,8 +702,9 @@ def create_conditioned_inv_cdf_array(x, conditioned_y, pdf_func):
668
702
  """
669
703
 
670
704
  list_ = []
705
+ size = len(x)
671
706
  for y in conditioned_y:
672
- phi = pdf_func(x,y)
707
+ phi = pdf_func(x,y*np.ones(size))
673
708
  list_.append(create_inv_cdf_array(x, phi))
674
709
 
675
710
  return np.array(list_)
@@ -796,11 +831,10 @@ def interpolator_pickle_path(
796
831
  path1 = full_dir + "/init_dict.pickle"
797
832
  if not os.path.exists(path1):
798
833
  dict_list = []
799
- with open(path1, "wb") as handle:
800
- pickle.dump(dict_list, handle, protocol=pickle.HIGHEST_PROTOCOL)
834
+ save_pickle(path1, dict_list)
801
835
 
802
836
  # check if the input dict is the same as one of the dict inside the pickle file
803
- param_dict_stored = pickle.load(open(path1, "rb"))
837
+ param_dict_stored = load_pickle(path1)
804
838
 
805
839
  path2 = full_dir
806
840
  len_ = len(param_dict_stored)
@@ -817,122 +851,34 @@ def interpolator_pickle_path(
817
851
  it_exist = False
818
852
  path_inv_cdf = path2 + "/" + interpolator_name + "_" + str(len_) + ".pickle"
819
853
  param_dict_stored.append(param_dict_given)
820
- with open(path1, "wb") as handle:
821
- pickle.dump(param_dict_stored, handle, protocol=pickle.HIGHEST_PROTOCOL)
854
+ save_pickle(path1, param_dict_stored)
822
855
 
823
856
  return path_inv_cdf, it_exist
824
857
 
825
- def interpolator_pdf_conditioned(x, conditioned_y, y_array, interpolator_list):
826
- """
827
- Function to find the pdf interpolator coefficients from the conditioned y.
828
-
829
- Parameters
830
- ----------
831
- x : `numpy.ndarray`
832
- x values.
833
- conditioned_y : `float`
834
- conditioned y value.
835
- y_array : `numpy.ndarray`
836
- y values.
837
- interpolator_list : `list`
838
- list of interpolators.
839
-
840
- Returns
841
- ----------
842
- interpolator_list[idx](x) : `numpy.ndarray`
843
- samples from the interpolator.
844
- """
845
- # find the index of z in zlist
846
- idx = np.searchsorted(y_array, conditioned_y)
847
-
848
- return interpolator_list[idx](x)
849
-
850
- def interpolator_sampler_conditioned(conditioned_y, y_array, interpolator_list, size=1000):
851
- """
852
- Function to find sampler interpolator coefficients from the conditioned y.
853
-
854
- Parameters
855
- ----------
856
- conditioned_y : `float`
857
- conditioned y value.
858
- y_array : `numpy.ndarray`
859
- y values.
860
- interpolator_list : `list`
861
- list of interpolators.
862
- size : `int`
863
- number of samples.
864
-
865
- Returns
866
- ----------
867
- """
868
-
869
- # find the index of z in zlist
870
- idx = np.searchsorted(y_array, conditioned_y)
871
- u = np.random.uniform(0, 1, size=size)
872
- return interpolator_list[idx](u)
873
-
874
- @njit
875
- def cubic_spline_interpolator(xnew, coefficients, x):
876
- """
877
- Function to interpolate using cubic spline.
878
-
879
- Parameters
880
- ----------
881
- xnew : `numpy.ndarray`
882
- new x values.
883
- coefficients : `numpy.ndarray`
884
- coefficients of the cubic spline.
885
- x : `numpy.ndarray`
886
- x values.
887
-
888
- Returns
889
- ----------
890
- result : `numpy.ndarray`
891
- interpolated values.
892
- """
893
-
894
- # Handling extrapolation
895
- i = np.searchsorted(x, xnew) - 1
896
- idx1 = xnew <= x[0]
897
- idx2 = xnew > x[-1]
898
- i[idx1] = 0
899
- i[idx2] = len(x) - 2
900
-
901
- # Calculate the relative position within the interval
902
- dx = xnew - x[i]
903
-
904
- # Calculate the interpolated value
905
- # Cubic polynomial: a + b*dx + c*dx^2 + d*dx^3
906
- a, b, c, d = coefficients[:, i]
907
- #result = a + b*dx + c*dx**2 + d*dx**3
908
- result = d + c*dx + b*dx**2 + a*dx**3
909
- return result
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.
910
861
 
911
- @njit
912
- def inverse_transform_sampler(size, cdf, x):
913
- """
914
- Function to sample from the inverse transform method.
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.
915
872
 
916
- Parameters
917
- ----------
918
- size : `int`
919
- number of samples.
920
- cdf : `numpy.ndarray`
921
- cdf values.
922
- x : `numpy.ndarray`
923
- x values.
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)
924
880
 
925
- Returns
926
- ----------
927
- samples : `numpy.ndarray`
928
- samples from the cdf.
929
- """
930
-
931
- u = np.random.uniform(0, 1, size)
932
- idx = np.searchsorted(cdf, u)
933
- x1, x0, y1, y0 = cdf[idx], cdf[idx-1], x[idx], x[idx-1]
934
- samples = y0 + (y1 - y0) * (u - x0) / (x1 - x0)
935
- return samples
881
+ # return interpolator_list[idx](x)
936
882
 
937
883
  def batch_handler(size, batch_size, sampling_routine, output_jsonfile, save_batch=True, resume=False, param_name='parameters'):
938
884
  """
@@ -975,11 +921,6 @@ def batch_handler(size, batch_size, sampling_routine, output_jsonfile, save_batc
975
921
  else:
976
922
  num_batches = size // batch_size + 1
977
923
 
978
- print(
979
- f"chosen batch size = {batch_size} with total size = {size}"
980
- )
981
- print(f"There will be {num_batches} batche(s)")
982
-
983
924
  # note frac_batches+(num_batches-1)*batch_size = size
984
925
  if size > batch_size:
985
926
  frac_batches = size - (num_batches - 1) * batch_size
@@ -1075,4 +1016,430 @@ def create_batch_params(sampling_routine, frac_batches, dict_buffer, save_batch,
1075
1016
  # store all params in json file
1076
1017
  dict_buffer = append_json(file_name=output_jsonfile, new_dictionary=param, old_dictionary=dict_buffer, replace=not (resume))
1077
1018
 
1078
- 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
+