Skip to content

[BUG] fix NormalMixture energy to use the component means - #1209

Open
Mudassiruddin7 wants to merge 1 commit into
sktime:mainfrom
Mudassiruddin7:fix/normal-mixture-energy
Open

Mudassiruddin7 wants to merge 1 commit into
sktime:mainfrom
Mudassiruddin7:fix/normal-mixture-energy

Conversation

@Mudassiruddin7

Copy link
Copy Markdown

Reference Issues/PRs

Fixes #1184.

What does this implement/fix? Explain your changes.

NormalMixture._energy_self did not compute E|X - Y|. It dropped the component means and used 2 * sqrt(2 * avg_sigma_sq / pi) with a variance-weighted average, which is off by a factor of sqrt(2) even for one component (1.5958 instead of 1.1284 for a standard normal) and cannot change with the means. Since energy is tagged exact, CRPS values computed from it were wrong too.

For two independent mixture draws, E|X - Y| is the pair sum sum_k sum_l pi_k pi_l E|N_k - N_l|. Each term is the mean of a folded normal, with d = mu_k - mu_l and s^2 = sigma_k^2 + sigma_l^2: d * erf(d / (s * sqrt(2))) + s * sqrt(2 / pi) * exp(-d^2 / (2 s^2)). That is what _energy_self now computes, for any number of rows, components and outputs, and it still sums over outputs like before.

I checked it against the closed form and numerical quadrature of 2 * integral of F (1 - F) for the three cases in #1184 (1.1284, 2.5224, 10.5642), a three-component mixture, and a batched two-row, two-output example, and they agree to five decimals. The CRPS example in the issue now gives 0.6054, the value from scoringrules that the issue quotes.

Does your contribution introduce a new dependency? If yes, which one?

No.

What should a reviewer concentrate their feedback on?

  • The pair arrays have shape (n_samples, K, K, n_outputs), so memory grows with the square of the number of components. That seemed fine for mixture density networks, where K is small, but say if you'd rather loop over components.

Did you add any tests for the change?

Yes, in test_normal_mixture.py: energy against quadrature for four mixtures, the single-component case against 2 sigma / sqrt(pi), a batched example with per-row weights and two outputs, and CRPS from energy against its defining integral. Seven of the thirteen tests in the file fail on the old formula. The 120 tests in skpro/distributions and skpro/metrics that involve NormalMixture, energy or CRPS pass.

Any other comments?

_energy_x was already correct and is unchanged.

PR checklist

For all contributions
  • I've added myself to the list of contributors with any new badges I've earned :-)
  • The PR title starts with either [ENH], [MNT], [DOC], or [BUG]. [BUG] - bugfix, [MNT] - CI, test framework, [ENH] - adding or improving code, [DOC] - writing or improving documentation or docstrings.

`NormalMixture._energy_self` ignored the component means and used a
variance-weighted average in `2 * sqrt(2 * avg_sigma_sq / pi)`, which is off by a
factor of sqrt(2) even for a single component and does not depend on the means at
all. The energy E|X - Y| of two i.i.d. mixture draws is the weighted sum over
component pairs of the folded-normal mean with d = mu_k - mu_l and
s^2 = sigma_k^2 + sigma_l^2. Compute that, which also fixes CRPS values obtained
from `energy`.

Fixes sktime#1184
Copilot AI balanced review requested due to automatic review settings October 8, 2026 17:26

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] NormalMixture.energy() (E|X−Y|) ignores component means and is off by a factor of √2 even for a single component

2 participants