archeo 2.0.0.dev7__tar.gz → 2.0.1__tar.gz
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.
- {archeo-2.0.0.dev7 → archeo-2.0.1}/PKG-INFO +1 -1
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/bayesian/importance_sampling/resampler/generic.py +6 -4
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/constants/physics.py +5 -2
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/data_structures/physics/black_hole.py +2 -1
- archeo-2.0.1/archeo/postprocessing/eval_utils/bias.py +13 -0
- archeo-2.0.1/archeo/postprocessing/eval_utils/kl.py +26 -0
- archeo-2.0.1/archeo/postprocessing/evaluation.py +29 -0
- archeo-2.0.1/archeo/utils/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/visualization/estimation.py +4 -4
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo.egg-info/PKG-INFO +1 -1
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo.egg-info/SOURCES.txt +4 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/pyproject.toml +2 -2
- {archeo-2.0.0.dev7 → archeo-2.0.1}/LICENSE +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/README.md +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/__main__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/bayesian/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/bayesian/ancestral_posterior.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/bayesian/importance_sampling/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/bayesian/importance_sampling/bayes_factor_curve.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/bayesian/importance_sampling/resampler/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/bayesian/importance_sampling/resampler/assume_independence.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/bayesian/importance_sampling/resampler/base.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/bayesian/importance_sampling/resampler/interface.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/constants/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/constants/bayesian.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/constants/enum.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/data_structures/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/data_structures/annotation.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/data_structures/bayesian/bayes_factor.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/data_structures/distribution.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/data_structures/math.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/data_structures/physics/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/data_structures/physics/binary.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/data_structures/physics/mahapatra.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/data_structures/physics/simulation.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/data_structures/visualization.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/postprocessing/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/postprocessing/dataframe.py +0 -0
- {archeo-2.0.0.dev7/archeo/preset/forward → archeo-2.0.1/archeo/postprocessing/eval_utils}/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/preset/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/preset/cli.py +0 -0
- {archeo-2.0.0.dev7/archeo/simulation → archeo-2.0.1/archeo/preset/forward}/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/preset/forward/compute_bayes_factor_curve.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/preset/simulation/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/preset/simulation/agnostic.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/preset/simulation/n_generation.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/preset/simulation/second_generation.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/py.typed +0 -0
- {archeo-2.0.0.dev7/archeo/utils → archeo-2.0.1/archeo/simulation}/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/simulation/simulate_merger.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/utils/decorator.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/utils/env.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/utils/fs.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/utils/logger.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/utils/parallel.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/version.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/visualization/__init__.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/visualization/animation.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/visualization/base.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/visualization/distribution.py +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo.egg-info/dependency_links.txt +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo.egg-info/requires.txt +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/archeo.egg-info/top_level.txt +0 -0
- {archeo-2.0.0.dev7 → archeo-2.0.1}/setup.cfg +0 -0
|
@@ -48,8 +48,9 @@ class ISDataGeneric(ImportanceSamplingDataBase):
|
|
|
48
48
|
weights_matrix = self._safe_divide(1.0, prior_hist)
|
|
49
49
|
|
|
50
50
|
def _get_pdf(row: pd.Series):
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
indices = tuple(np.searchsorted(edges[col], row[col], side="right") - 1 for col in self.common_columns)
|
|
52
|
+
indices = tuple(max(0, min(weights_matrix.shape[i] - 1, idx)) for i, idx in enumerate(indices))
|
|
53
|
+
return weights_matrix[indices]
|
|
53
54
|
|
|
54
55
|
weights = self.posterior_samples.apply(_get_pdf, axis=1)
|
|
55
56
|
|
|
@@ -73,8 +74,9 @@ class ISDataGeneric(ImportanceSamplingDataBase):
|
|
|
73
74
|
weights_matrix = self._safe_divide(new_prior_hist, prior_hist)
|
|
74
75
|
|
|
75
76
|
def _get_pdf(row: pd.Series):
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
indices = tuple(np.searchsorted(edges[col], row[col], side="right") - 1 for col in self.common_columns)
|
|
78
|
+
indices = tuple(max(0, min(weights_matrix.shape[i] - 1, idx)) for i, idx in enumerate(indices))
|
|
79
|
+
return weights_matrix[indices]
|
|
78
80
|
|
|
79
81
|
return self.posterior_samples.apply(_get_pdf, axis=1)
|
|
80
82
|
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import enum
|
|
2
|
+
import os
|
|
2
3
|
|
|
3
4
|
import pandas as pd
|
|
4
5
|
from pydantic import BaseModel, NonNegativeFloat
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
SPEED_OF_LIGHT = 299792.458 # km/s
|
|
9
|
+
BH_MASS_LB: float = float(os.environ.get("BH_MASS_LB", 5.0)) # Solar masses
|
|
10
|
+
PISN_LB: float = float(os.environ.get("PISN_LB", 65.0)) # Solar masses
|
|
11
|
+
PISN_UB: float = float(os.environ.get("PISN_UB", 130.0)) # Solar masses
|
|
8
12
|
|
|
9
13
|
|
|
10
14
|
class _TypicalHostEscapeVelocityMeta(BaseModel):
|
|
@@ -36,7 +40,6 @@ class TypicalHostEscapeVelocity(enum.Enum):
|
|
|
36
40
|
def compute_p2g(
|
|
37
41
|
self,
|
|
38
42
|
df: pd.DataFrame,
|
|
39
|
-
max_mass: float = 65.0,
|
|
40
43
|
kf_col: str = "k_f",
|
|
41
44
|
m1_col: str = "m_1",
|
|
42
45
|
m2_col: str = "m_2",
|
|
@@ -46,7 +49,7 @@ class TypicalHostEscapeVelocity(enum.Enum):
|
|
|
46
49
|
if df.empty:
|
|
47
50
|
return 0.0
|
|
48
51
|
|
|
49
|
-
mask = (df[kf_col] <= self.v_esc) & (df[m1_col] <=
|
|
52
|
+
mask = (df[kf_col] <= self.v_esc) & (df[m1_col] <= PISN_LB) & (df[m2_col] <= PISN_LB)
|
|
50
53
|
return mask.mean() * 100.0
|
|
51
54
|
|
|
52
55
|
@classmethod
|
|
@@ -4,6 +4,7 @@ import numpy as np
|
|
|
4
4
|
import pandas as pd
|
|
5
5
|
from pydantic import BaseModel, NonNegativeFloat, PositiveFloat, field_validator
|
|
6
6
|
|
|
7
|
+
from archeo.constants.physics import BH_MASS_LB, PISN_LB
|
|
7
8
|
from archeo.data_structures.annotation import Distribution
|
|
8
9
|
from archeo.data_structures.distribution import Uniform
|
|
9
10
|
|
|
@@ -35,7 +36,7 @@ BlackHoles: TypeAlias = list[BlackHole]
|
|
|
35
36
|
class BlackHoleGenerator(BaseModel, frozen=True):
|
|
36
37
|
"""Black hole generator data class."""
|
|
37
38
|
|
|
38
|
-
mass_distribution: Distribution = Uniform(low=
|
|
39
|
+
mass_distribution: Distribution = Uniform(low=BH_MASS_LB, high=PISN_LB)
|
|
39
40
|
spin_magnitude_distribution: Distribution = Uniform(low=0, high=1)
|
|
40
41
|
phi_distribution: Distribution = Uniform(low=0, high=2 * np.pi)
|
|
41
42
|
theta_distribution: Distribution = Uniform(low=0, high=np.pi)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def compute_bias_for_remnant_spin(df_samples: pd.DataFrame) -> float:
|
|
5
|
+
"""Compute the bias in remnant spin for a given set of samples."""
|
|
6
|
+
|
|
7
|
+
return (df_samples["a_f"] - df_samples["spin_measure"]).sum() / df_samples.shape[0]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def compute_bias_for_remnant_mass(df_samples: pd.DataFrame) -> float:
|
|
11
|
+
"""Compute the bias in remnant mass for a given set of samples."""
|
|
12
|
+
|
|
13
|
+
return (df_samples["m_f"] - df_samples["mass_measure"]).sum() / df_samples.shape[0]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def compute_kl_divergence_from_samples(X: np.ndarray, Y: np.ndarray) -> float:
|
|
5
|
+
"""
|
|
6
|
+
Compute KL(N0 || N1) where N0 and N1 are multivariate Gaussians.
|
|
7
|
+
X and Y should be 2D arrays of shape (n, d) and (m, d) respectively.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
if X.shape[1] != Y.shape[1]:
|
|
11
|
+
raise ValueError("X and Y must have the same dimension.")
|
|
12
|
+
|
|
13
|
+
k = X.shape[1]
|
|
14
|
+
mu0 = X.mean(axis=0)
|
|
15
|
+
mu1 = Y.mean(axis=0)
|
|
16
|
+
S0 = np.cov(X.T, bias=False)
|
|
17
|
+
S1 = np.cov(Y.T, bias=False)
|
|
18
|
+
|
|
19
|
+
invS1 = np.linalg.inv(S1)
|
|
20
|
+
diff = (mu1 - mu0).reshape(k, 1)
|
|
21
|
+
|
|
22
|
+
term_trace = np.trace(invS1 @ S0)
|
|
23
|
+
term_quad = float((diff.T @ invS1 @ diff)[0, 0])
|
|
24
|
+
term_logdet = np.log(np.linalg.det(S1) / np.linalg.det(S0))
|
|
25
|
+
|
|
26
|
+
return 0.5 * (term_trace + term_quad - k + term_logdet)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
from archeo.postprocessing.eval_utils.bias import compute_bias_for_remnant_mass, compute_bias_for_remnant_spin
|
|
4
|
+
from archeo.postprocessing.eval_utils.kl import compute_kl_divergence_from_samples
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def evaluate_ancestral_inference(df_samples: pd.DataFrame) -> dict[str, float]:
|
|
8
|
+
"""Evaluate the validity of the estimated posterior samples by computing:
|
|
9
|
+
1. Estimation bias: the average difference between the estimated parameters and the true parameters
|
|
10
|
+
2. KL divergence between the Gaussian fitted to the estimated samples and the Gaussian fitted to
|
|
11
|
+
the true parameters.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
bias_spin = compute_bias_for_remnant_spin(df_samples)
|
|
15
|
+
bias_mass = compute_bias_for_remnant_mass(df_samples)
|
|
16
|
+
|
|
17
|
+
mask = df_samples[["a_f", "m_f"]].notna().all(axis=1)
|
|
18
|
+
# Here we need to apply the mask to the inferred samples.
|
|
19
|
+
# The reason is that there could be no similar samples
|
|
20
|
+
# in the ancestral prior, resulting in NaN values.
|
|
21
|
+
# Those NaN values would cause the KL divergence computation to fail.
|
|
22
|
+
kl_div = compute_kl_divergence_from_samples(
|
|
23
|
+
df_samples.loc[mask, ["a_f", "m_f"]].values, df_samples[["spin_measure", "mass_measure"]].values
|
|
24
|
+
)
|
|
25
|
+
return {
|
|
26
|
+
"bias_spin": bias_spin,
|
|
27
|
+
"bias_mass": bias_mass,
|
|
28
|
+
"kl_divergence": kl_div,
|
|
29
|
+
}
|
|
File without changes
|
|
@@ -8,7 +8,7 @@ import numpy as np
|
|
|
8
8
|
import pandas as pd
|
|
9
9
|
import seaborn as sns
|
|
10
10
|
|
|
11
|
-
from archeo.constants.physics import TypicalHostEscapeVelocity
|
|
11
|
+
from archeo.constants.physics import PISN_LB, PISN_UB, TypicalHostEscapeVelocity
|
|
12
12
|
from archeo.data_structures.visualization import Labels, Padding
|
|
13
13
|
from archeo.utils.logger import get_logger
|
|
14
14
|
from archeo.visualization import base
|
|
@@ -87,8 +87,8 @@ def _add_pisn_gap(ax, color: str) -> None:
|
|
|
87
87
|
color (str): Color.
|
|
88
88
|
"""
|
|
89
89
|
|
|
90
|
-
ax.axvline(
|
|
91
|
-
ax.axvline(
|
|
90
|
+
ax.axvline(PISN_LB, color=color, linewidth=0.9, linestyle="--", label="PISN Gap")
|
|
91
|
+
ax.axvline(PISN_UB, color=color, linewidth=0.9, linestyle="--")
|
|
92
92
|
|
|
93
93
|
|
|
94
94
|
def corner_estimates( # pylint: disable=dangerous-default-value
|
|
@@ -235,7 +235,7 @@ def second_generation_probability_curve(
|
|
|
235
235
|
# Calculate the CDF
|
|
236
236
|
y = []
|
|
237
237
|
for kick in x:
|
|
238
|
-
df_samples = df.loc[(df["k_f"] <= kick) & (df["m_1"] <=
|
|
238
|
+
df_samples = df.loc[(df["k_f"] <= kick) & (df["m_1"] <= PISN_LB) & (df["m_2"] <= PISN_LB)]
|
|
239
239
|
if df_samples.empty:
|
|
240
240
|
y.append(0.0)
|
|
241
241
|
else:
|
|
@@ -36,6 +36,10 @@ archeo/data_structures/physics/mahapatra.py
|
|
|
36
36
|
archeo/data_structures/physics/simulation.py
|
|
37
37
|
archeo/postprocessing/__init__.py
|
|
38
38
|
archeo/postprocessing/dataframe.py
|
|
39
|
+
archeo/postprocessing/evaluation.py
|
|
40
|
+
archeo/postprocessing/eval_utils/__init__.py
|
|
41
|
+
archeo/postprocessing/eval_utils/bias.py
|
|
42
|
+
archeo/postprocessing/eval_utils/kl.py
|
|
39
43
|
archeo/preset/__init__.py
|
|
40
44
|
archeo/preset/cli.py
|
|
41
45
|
archeo/preset/forward/__init__.py
|
|
@@ -16,7 +16,7 @@ classifiers = [
|
|
|
16
16
|
"Operating System :: OS Independent"
|
|
17
17
|
]
|
|
18
18
|
keywords = ["black-holes", "gravitational-waves", "black-hole-archeology"]
|
|
19
|
-
version = "2.0.
|
|
19
|
+
version = "2.0.1"
|
|
20
20
|
readme = "README.md"
|
|
21
21
|
requires-python = ">=3.11,<3.14"
|
|
22
22
|
dependencies = [
|
|
@@ -43,7 +43,7 @@ documentation = "https://wyhwong.github.io/archeo/"
|
|
|
43
43
|
|
|
44
44
|
[dependency-groups]
|
|
45
45
|
dev = [
|
|
46
|
-
"ipykernel>=
|
|
46
|
+
"ipykernel>=7.2.0",
|
|
47
47
|
"pre-commit>=4.5.1",
|
|
48
48
|
"pyarrow>=23.0.1",
|
|
49
49
|
"pyinstrument>=5.1.2",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/bayesian/importance_sampling/bayes_factor_curve.py
RENAMED
|
File without changes
|
{archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/bayesian/importance_sampling/resampler/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{archeo-2.0.0.dev7 → archeo-2.0.1}/archeo/bayesian/importance_sampling/resampler/interface.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|