Distribution.cdf rejects values outside the support, though the CDF is defined on all of R (and the implementations already clamp for it)
- Dominant language
- Python
- Stars
- 103k
- Forks
- 29.5k
- PR merge metrics
- PR metrics pending
Description
### 🐛 Describe the bug
`cdf` validates its `value` argument against the distribution's **support**, but a CDF is defined for every real number — `F(x) = P(X <= x)` is `0` below the support and `1` above it. With the default `validate_args`, evaluating a CDF outside the support raises instead of returning those values.
The most ordinary case this breaks is plotting a CDF over a grid that extends past the support:
```python
import torch, torch.distributions as D
grid = torch.linspace(-0.5, 1.5, 9)
D.Uniform(0., 1.).cdf(grid)
# ValueError: Expected value argument (Tensor of shape (9,)) to be within the support (Interval(...))
D.Uniform(0., 1., validate_args=False).cdf(grid)
# [0.0, 0.0, 0.0, 0.25, 0.5, 0.75, 1.0, 1.0, 1.0] <- correct, and what SciPy returns
```
Same for `Exponential(1.).cdf(-1.)` and `Gamma(2., 2.).cdf(-1.)`.
### Why this looks unintended rather than by design
`Uniform.cdf` clamps its result specifically so that out-of-support inputs produce `0`/`1`:
```python
def cdf(self, value):
if self._validate_args:
self._validate_sample(value) # rejects value outside [low, high]
result = (value - self.low) / (self.high - self.low)
return result.clamp(min=0, max=1) # ...but this exists to handle exactly those values
```
Under the default settings the `clamp` is unreachable: any input that would need clamping has already been rejected two lines above. The clamp only does anything when validation is off — so the implementation already encodes the intended out-of-support semantics, and the validation gate prevents them being used.
The mathematical distinction is that support validation is right for `log_prob` (the density is genuinely undefined/zero outside) but not for `cdf`, where every real input has a well-defined answer. `_validate_sample`'s docstring says it covers "`log_prob`, `cdf` and `icdf`", so the two cases are currently conflated.
### Scope / what I'm asking
The pattern is consistent across the library (`uniform`, `exponential`, `gamma`, `laplace`, `half_normal`, ...), so this is a deliberate-looking systematic choice rather than a one-file slip, and changing it affects every distribution — which is why I'm opening an issue rather than sending a patch.
Two options, if you agree it should change:
1. Have `cdf`/`icdf` validate only shape/broadcastability (via a variant of `_validate_sample` that skips the support check), keeping the strict support check for `log_prob`. This matches SciPy and makes the existing clamps meaningful.
2. Keep the current behaviour and document that CDF evaluation outside the support requires `validate_args=False`.
If the answer is (1), the distributions whose `cdf` doesn't already clamp/saturate would need that added so they return `0`/`1` rather than extrapolating — happy to do that work and send the PR.
Possibly related: #186826 covers the opposite end of the same area (`icdf` performing *no* validation even with `validate_args=True`), so `cdf` validating too much and `icdf` too little might be worth settling together.
### Versions
torch 2.13.0+cpu, Python 3.11.9, Windows. Behaviour also present in current `main` source.
cc @fritzo @neerajprad @alicanb @nikitaved
Contributor guide
Research direction
Start with the named Uniform.cdf implementation and the _validate_sample docstring, then compare the cdf behavior of Exponential, Gamma, Laplace, and HalfNormal. Trace how validate_args is applied to cdf, log_prob, and icdf; done requires an agreed validation policy and consistent out-of-support CDF results across the affected distributions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- machine-learning
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100