Skip to content

Fix: MultivariateQExponential.log_prob ignores power when fast log-prob is off - #1

Open
richardcsuwandi wants to merge 1 commit into
lanzithinking:mainfrom
richardcsuwandi:fix/mqe-log-prob-ignores-power-when-fast-off
Open

Fix: MultivariateQExponential.log_prob ignores power when fast log-prob is off#1
richardcsuwandi wants to merge 1 commit into
lanzithinking:mainfrom
richardcsuwandi:fix/mqe-log-prob-ignores-power-when-fast-off

Conversation

@richardcsuwandi

Copy link
Copy Markdown

Problem

When gpytorch.settings.fast_computations.log_prob is off, MultivariateQExponential.log_prob falls back to torch.distributions.MultivariateNormal.log_prob. That path is Gaussian-only, so it ignores power and always returns the q = 2 log-density.

This matters in practice because BoTorch turns that setting off on import, and QePyTorch reuses gpytorch.settings. So any BoTorch-style Exact QEP fit via

mll = ExactMarginalLogLikelihood(likelihood, model)
loss = -mll(output, target)

quietly does ordinary Gaussian MLE, even when power != 2.

I hit this with a BoTorch-style SingleTaskQEP wrapper: same data and init, q ∈ {2, 1.5, 1, 0.5}, but output.log_prob(...) and the MLL stayed identical for every q (matching the Gaussian value). output.power itself was set correctly; only the numerical log-prob / MLL was wrong.

Why it happens

MultivariateQExponential.log_prob currently does:

if settings.fast_computations.log_prob.off():
    return super().log_prob(value)  # Gaussian-only; ignores power

That early return is copied from GPyTorch's MultivariateNormal, where a Gaussian fallback is fine. Here super() is still torch.distributions.MultivariateNormal, which has no power, so the fallback is wrong for q != 2.

The Q-Exponential formula in the rest of the method is correct. A plain MultivariateQExponential(...).log_prob(y) with fast log-prob left on is q-dependent as expected. The bug only appears once the fast-log-prob-off path is taken (e.g. after importing BoTorch).

On a small ExactQEP / SingleTaskQEP example, the broken MLL was about -12.25 for all four q values. Recomputing the QED formula from the same loc, covariance, and power gave the correct sequence: about -12.25, -13.68, -15.65, -18.17.

Implications

  • Hyperparameter learning through ExactMarginalLogLikelihood under BoTorch (or any code with fast log-prob off) fits a GP, not a QEP, whenever q != 2.
  • Code that only reads .power (e.g. some acquisition logic) is unaffected.
  • The failure is silent: no error, just the wrong objective.

Fix

Only use the Gaussian super().log_prob fallback when power == 2. For power != 2, always evaluate the Q-Exponential log-density via inv_quad_logdet, even if fast_computations.log_prob is off.

After the fix, the same BoTorch / SingleTaskQEP setup recovers the q-dependent MLL values above.

Checklist (from CONTRIBUTING.md)

  • Code change
  • No new public API / docs objects required
  • Ran python -m unittest locally before opening the PR

…g-prob is off

## Problem

When `gpytorch.settings.fast_computations.log_prob` is off, `MultivariateQExponential.log_prob` falls back to `torch.distributions.MultivariateNormal.log_prob`. That path is Gaussian-only, so it ignores `power` and always returns the `q = 2` log-density.

This matters in practice because **BoTorch turns that setting off on import**, and QePyTorch reuses `gpytorch.settings`. So any BoTorch-style Exact QEP fit via

```python
mll = ExactMarginalLogLikelihood(likelihood, model)
loss = -mll(output, target)
```

quietly does ordinary Gaussian MLE, even when `power != 2`.

I hit this with a BoTorch-style `SingleTaskQEP` wrapper: same data and init, `q ∈ {2, 1.5, 1, 0.5}`, but `output.log_prob(...)` and the MLL stayed identical for every `q` (matching the Gaussian value). `output.power` itself was set correctly; only the numerical log-prob / MLL was wrong.

## Why it happens

`MultivariateQExponential.log_prob` currently does:

```python
if settings.fast_computations.log_prob.off():
    return super().log_prob(value)  # Gaussian-only; ignores power
```

That early return is copied from GPyTorch's `MultivariateNormal`, where a Gaussian fallback is fine. Here `super()` is still `torch.distributions.MultivariateNormal`, which has no `power`, so the fallback is wrong for `q != 2`.

The Q-Exponential formula in the rest of the method is correct. A plain `MultivariateQExponential(...).log_prob(y)` with fast log-prob left on is `q`-dependent as expected. The bug only appears once the fast-log-prob-off path is taken (e.g. after importing BoTorch).

On a small ExactQEP / `SingleTaskQEP` example, the broken MLL was about `-12.25` for all four `q` values. Recomputing the QED formula from the same `loc`, covariance, and `power` gave the correct sequence: about `-12.25`, `-13.68`, `-15.65`, `-18.17`.

## Implications

- Hyperparameter learning through `ExactMarginalLogLikelihood` under BoTorch (or any code with fast log-prob off) fits a GP, not a QEP, whenever `q != 2`.
- Code that only reads `.power` (e.g. some acquisition logic) is unaffected.
- The failure is silent: no error, just the wrong objective.

## Fix

Only use the Gaussian `super().log_prob` fallback when `power == 2`. For `power != 2`, always evaluate the Q-Exponential log-density via `inv_quad_logdet`, even if `fast_computations.log_prob` is off.

After the fix, the same BoTorch / `SingleTaskQEP` setup recovers the `q`-dependent MLL values above.

## Checklist (from CONTRIBUTING.md)

- [x] Code change
- [x] No new public API / docs objects required
- [x] Ran `python -m unittest` locally before opening the PR
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.

1 participant