Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fe1d8f162 | ||
|
|
8ac3060814 | ||
|
|
c0cbc99231 |
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
[tool.bumpversion]
|
[tool.bumpversion]
|
||||||
current_version = "0.3.24"
|
current_version = "0.3.25"
|
||||||
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
||||||
serialize = ["{major}.{minor}.{patch}"]
|
serialize = ["{major}.{minor}.{patch}"]
|
||||||
search = "{current_version}"
|
search = "{current_version}"
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.3.25] - 2026-09-09
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Fix(analysis): keep pre_E alive through prediction range subsampling
|
||||||
|
|
||||||
## [0.3.24] - 2026-09-09
|
## [0.3.24] - 2026-09-09
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ calorimeter showers.
|
|||||||
|
|
||||||
[](pyproject.toml)
|
[](pyproject.toml)
|
||||||
[](pyproject.toml)
|
[](pyproject.toml)
|
||||||
[](CHANGELOG.md)
|
[](CHANGELOG.md)
|
||||||
[](tests/)
|
[](tests/)
|
||||||
[](https://git.larsbogner.de/lars/giant/actions)
|
[](https://git.larsbogner.de/lars/giant/actions)
|
||||||
[](#license)
|
[](#license)
|
||||||
|
|
||||||
|
|||||||
+26
-13
@@ -85,7 +85,11 @@ _LO_Q, _HI_Q = 0.001, 0.999
|
|||||||
|
|
||||||
|
|
||||||
def _row_subsample(lf: pl.LazyFrame, sample_rows: int, seed: int) -> pl.LazyFrame:
|
def _row_subsample(lf: pl.LazyFrame, sample_rows: int, seed: int) -> pl.LazyFrame:
|
||||||
"""Hash-subsample ~``sample_rows`` rows (for range estimation only)."""
|
"""Hash-subsample ~``sample_rows`` rows (for range estimation only).
|
||||||
|
|
||||||
|
``lf`` must still carry ``pre_E`` (the hash key) — subsample before
|
||||||
|
projecting away columns, not after.
|
||||||
|
"""
|
||||||
n_total = lf.select(pl.len()).collect(engine="streaming").item()
|
n_total = lf.select(pl.len()).collect(engine="streaming").item()
|
||||||
if n_total <= sample_rows:
|
if n_total <= sample_rows:
|
||||||
return lf
|
return lf
|
||||||
@@ -195,20 +199,29 @@ def build_context(
|
|||||||
if predictions:
|
if predictions:
|
||||||
sides = {ps.name: open_prediction(ps.source) for ps in predictions}
|
sides = {ps.name: open_prediction(ps.source) for ps in predictions}
|
||||||
present_vars = sorted(set().union(*(paired_vars_for_coord(s.coord) for s in sides.values())))
|
present_vars = sorted(set().union(*(paired_vars_for_coord(s.coord) for s in sides.values())))
|
||||||
for var in present_vars:
|
true_samples: dict[str, list[np.ndarray]] = {v: [] for v in present_vars}
|
||||||
true_samples, pred_samples, residual_samples = [], [], []
|
pred_samples: dict[str, list[np.ndarray]] = {v: [] for v in present_vars}
|
||||||
|
residual_samples: dict[str, list[np.ndarray]] = {v: [] for v in present_vars}
|
||||||
|
|
||||||
|
# One subsample+collect per side (not per variable) — `s.paired`
|
||||||
|
# still carries `pre_E`, which `_row_subsample`'s hash needs, so
|
||||||
|
# subsample before projecting down to the pred/true columns.
|
||||||
for s in sides.values():
|
for s in sides.values():
|
||||||
if var not in paired_vars_for_coord(s.coord):
|
vars_here = paired_vars_for_coord(s.coord)
|
||||||
continue
|
cols = [f"pred_{v}" for v in vars_here] + ([f"true_{v}" for v in vars_here] if s.has_truth else [])
|
||||||
cols = [f"pred_{var}"] + ([f"true_{var}"] if s.has_truth else [])
|
sample = _row_subsample(s.paired, sample_rows, seed).select(cols).collect(engine="streaming")
|
||||||
sample = _row_subsample(s.paired.select(cols), sample_rows, seed).collect(engine="streaming")
|
for var in vars_here:
|
||||||
pred_samples.append(sample[f"pred_{var}"].to_numpy())
|
p = sample[f"pred_{var}"].to_numpy()
|
||||||
|
pred_samples[var].append(p)
|
||||||
if s.has_truth:
|
if s.has_truth:
|
||||||
true_samples.append(sample[f"true_{var}"].to_numpy())
|
t = sample[f"true_{var}"].to_numpy()
|
||||||
residual_samples.append(sample[f"pred_{var}"].to_numpy() - sample[f"true_{var}"].to_numpy())
|
true_samples[var].append(t)
|
||||||
pred_var_ranges[var] = _combined_quantiles([*true_samples, *pred_samples], _LO_Q, _HI_Q)
|
residual_samples[var].append(p - t)
|
||||||
if residual_samples:
|
|
||||||
pred_residual_ranges[var] = _combined_quantiles(residual_samples, _LO_Q, _HI_Q)
|
for var in present_vars:
|
||||||
|
pred_var_ranges[var] = _combined_quantiles([*true_samples[var], *pred_samples[var]], _LO_Q, _HI_Q)
|
||||||
|
if residual_samples[var]:
|
||||||
|
pred_residual_ranges[var] = _combined_quantiles(residual_samples[var], _LO_Q, _HI_Q)
|
||||||
|
|
||||||
sec_pdg_counts: dict[int, int] = {}
|
sec_pdg_counts: dict[int, int] = {}
|
||||||
for s in sides.values():
|
for s in sides.values():
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "giant"
|
name = "giant"
|
||||||
version = "0.3.24"
|
version = "0.3.25"
|
||||||
description = "Geant4 step-function surrogate via conditional flow matching"
|
description = "Geant4 step-function surrogate via conditional flow matching"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
@@ -193,6 +193,28 @@ def test_build_context_resolves_prediction_ranges():
|
|||||||
assert ctx.pred_top_sec_pdgs # secondaries present in the fixture
|
assert ctx.pred_top_sec_pdgs # secondaries present in the fixture
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_context_resolves_prediction_ranges_when_subsampled():
|
||||||
|
"""Regression test: `sample_rows` smaller than the prediction row count
|
||||||
|
must still work — `_row_subsample`'s hash key (`pre_E`) has to survive
|
||||||
|
into the prediction branch's subsample call, not be projected away first
|
||||||
|
(see giant/analysis/context.py's predictions loop)."""
|
||||||
|
ctx = build_context(
|
||||||
|
[RolloutSpec("rollout", _rollout_frame())],
|
||||||
|
_reference_frame(),
|
||||||
|
predictions=[PredictionSpec("pred", _global_prediction_frame())],
|
||||||
|
n_energy_bins=2,
|
||||||
|
n_marginal_bins=10,
|
||||||
|
top_k_pdg=3,
|
||||||
|
sample_rows=2, # < the 3-row prediction fixture: forces the hash-filter branch
|
||||||
|
seed=0,
|
||||||
|
)
|
||||||
|
for var in PAIRED_SCALARS + ("cos_scatter", "cos_travel"):
|
||||||
|
lo, hi = ctx.pred_var_ranges[var]
|
||||||
|
assert np.isfinite(lo) and np.isfinite(hi) and lo < hi
|
||||||
|
lo, hi = ctx.pred_residual_ranges[var]
|
||||||
|
assert np.isfinite(lo) and np.isfinite(hi) and lo < hi
|
||||||
|
|
||||||
|
|
||||||
def test_prediction_specs_compute_valid_reduced():
|
def test_prediction_specs_compute_valid_reduced():
|
||||||
ctx = _ctx_with_predictions()
|
ctx = _ctx_with_predictions()
|
||||||
bundle = Bundle.open(
|
bundle = Bundle.open(
|
||||||
|
|||||||
Reference in New Issue
Block a user