Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/pyrecest/scenarios.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,16 @@ def _normalized_particle_weights(raw_weights: Any, particle_count: int, backend)
if any(weight < 0.0 for weight in weight_values):
raise ValueError("weights must be nonnegative")

weight_total = sum(weight_values)
if weight_total <= 0.0:
weight_scale = max(weight_values, default=0.0)
if weight_scale <= 0.0:
raise ValueError("weights must have positive total mass")
scaled_weights = [weight / weight_scale for weight in weight_values]
scaled_total = sum(scaled_weights)
if scaled_total <= 0.0 or not math.isfinite(scaled_total):
raise ValueError("weights must have positive finite total mass")

return backend.asarray(
[weight / weight_total for weight in weight_values],
[weight / scaled_total for weight in scaled_weights],
dtype=backend.float64,
)

Expand Down
36 changes: 36 additions & 0 deletions tests/test_scenario_particle_weight_overflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import math
import sys
from pathlib import Path

from pyrecest.scenarios import run_scenario


def test_particle_resampling_scenario_normalizes_extreme_finite_weights(
tmp_path: Path,
):
max_float = sys.float_info.max
scenario = tmp_path / "extreme_particle_weights.toml"
scenario.write_text(
f"""
[scenario]
type = "particle_resampling"
name = "extreme-particle-weights"
seed = 7

[data]
particles = [[0.0], [1.0]]
weights = [{max_float!r}, {max_float / 2.0!r}]
num_samples = 8
""".strip(),
encoding="utf-8",
)

result = run_scenario(scenario)

assert math.isclose(result.metrics["max_weight"], 2.0 / 3.0)
assert math.isclose(result.metrics["min_weight"], 1.0 / 3.0)
assert math.isclose(
result.metrics["max_weight"] + result.metrics["min_weight"],
1.0,
)
assert math.isfinite(result.metrics["effective_sample_size"])
Loading