Fix: MultivariateQExponential.log_prob ignores power when fast log-prob is off - #1
Open
richardcsuwandi wants to merge 1 commit into
Conversation
…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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When
gpytorch.settings.fast_computations.log_probis off,MultivariateQExponential.log_probfalls back totorch.distributions.MultivariateNormal.log_prob. That path is Gaussian-only, so it ignorespowerand always returns theq = 2log-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 viaquietly does ordinary Gaussian MLE, even when
power != 2.I hit this with a BoTorch-style
SingleTaskQEPwrapper: same data and init,q ∈ {2, 1.5, 1, 0.5}, butoutput.log_prob(...)and the MLL stayed identical for everyq(matching the Gaussian value).output.poweritself was set correctly; only the numerical log-prob / MLL was wrong.Why it happens
MultivariateQExponential.log_probcurrently does:That early return is copied from GPyTorch's
MultivariateNormal, where a Gaussian fallback is fine. Heresuper()is stilltorch.distributions.MultivariateNormal, which has nopower, so the fallback is wrong forq != 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 isq-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 /
SingleTaskQEPexample, the broken MLL was about-12.25for all fourqvalues. Recomputing the QED formula from the sameloc, covariance, andpowergave the correct sequence: about-12.25,-13.68,-15.65,-18.17.Implications
ExactMarginalLogLikelihoodunder BoTorch (or any code with fast log-prob off) fits a GP, not a QEP, wheneverq != 2..power(e.g. some acquisition logic) is unaffected.Fix
Only use the Gaussian
super().log_probfallback whenpower == 2. Forpower != 2, always evaluate the Q-Exponential log-density viainv_quad_logdet, even iffast_computations.log_probis off.After the fix, the same BoTorch /
SingleTaskQEPsetup recovers theq-dependent MLL values above.Checklist (from CONTRIBUTING.md)
python -m unittestlocally before opening the PR