pypfda.weights

Particle weight computation and ensemble health diagnostics.

All weight math lives in the log domain to avoid underflow when many particles have negligible likelihood. The public functions accept and return float arrays of shape (n_members,).

Functions

cap_max_weight(log_weights, max_weight)

Clip the largest particle weight to prevent degeneracy.

effective_sample_size(weights)

Effective sample size of a normalized weight vector.

gaussian_log_likelihood(ensemble_obs, ...)

Log-likelihood of each ensemble member under a diagonal Gaussian observation model.

normalize_log_weights(log_weights)

Normalize log-weights to a probability vector summing to one.

weight_entropy(weights)

Shannon entropy (in nats) of a normalized weight vector.

pypfda.weights.gaussian_log_likelihood(ensemble_obs, observations, obs_err)[source]

Log-likelihood of each ensemble member under a diagonal Gaussian observation model.

For ensemble member \(m\) with predicted observations \(\hat y_m \in \mathbb R^{p}\) and actual observations \(y \in \mathbb R^{p}\), the log-likelihood under \(y \mid x \sim \mathcal N(\hat y_m, \mathrm{diag}(\sigma^2))\) reduces (up to an additive constant that is the same for every member) to

\[\log p(y \mid x_m) \;\propto\; -\frac{1}{2} \sum_{i=1}^{p} \left( \frac{\hat y_{m,i} - y_i}{\sigma_i} \right)^{2}.\]

The constant is dropped because only weight ratios matter for the particle filter.

Parameters:
  • ensemble_obs (array_like, shape (n_members, n_obs)) – Predicted observations for each ensemble member.

  • observations (array_like, shape (n_obs,)) – Actual observations.

  • obs_err (float or array_like of shape (n_obs,)) – Observation-error standard deviation. Scalar values are broadcast over all observations.

Returns:

log_likelihood – Unnormalized log-likelihood per member.

Return type:

ndarray, shape (n_members,)

Raises:

ValueError – If shapes are inconsistent or obs_err contains non-positive values.

pypfda.weights.normalize_log_weights(log_weights)[source]

Normalize log-weights to a probability vector summing to one.

Uses the log-sum-exp trick so the result is numerically stable even when the largest log-weight is very negative.

Parameters:

log_weights (array_like, shape (n_members,)) – Unnormalized log-weights.

Returns:

weights – Normalized weights, weights.sum() == 1 to within floating-point rounding.

Return type:

ndarray, shape (n_members,)

pypfda.weights.effective_sample_size(weights)[source]

Effective sample size of a normalized weight vector.

\[N_{\mathrm{eff}} = \frac{1}{\sum_{m=1}^{N} w_m^{2}}.\]

Ranges from 1 (all weight on one particle) to len(weights) (uniform).

Parameters:

weights (array_like, shape (n_members,)) – Normalized weights.

Returns:

ess – Effective sample size, in particles.

Return type:

float

pypfda.weights.weight_entropy(weights)[source]

Shannon entropy (in nats) of a normalized weight vector.

\[H(w) = -\sum_{m=1}^{N} w_m \log w_m.\]

Zero-weight particles contribute zero (the convention \(0 \log 0 = 0\)).

Parameters:

weights (array_like, shape (n_members,)) – Normalized weights.

Returns:

entropy – Entropy in nats. Maximum is log(N) for the uniform distribution.

Return type:

float

pypfda.weights.cap_max_weight(log_weights, max_weight)[source]

Clip the largest particle weight to prevent degeneracy.

Implements the simple “max-weight” heuristic from DA_T9b: if the largest normalized weight exceeds max_weight, redistribute the excess uniformly to the others. Operates on log-weights and returns log-weights so it composes with downstream calls.

Parameters:
  • log_weights (array_like, shape (n_members,)) – Unnormalized log-weights.

  • max_weight (float) – Maximum allowed normalized weight for any single particle, in (1/N, 1]. Values outside this range are rejected.

Returns:

capped_log_weights – Log of the capped, re-normalized weight distribution.

Return type:

ndarray, shape (n_members,)