-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Allow arrays in pm.Bound #2277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow arrays in pm.Bound #2277
Conversation
pymc3/distributions/distribution.py
Outdated
|
||
Parameters | ||
---------- | ||
distribution : pymc3 distribution | ||
Distribution to be transformed into a bounded distribution | ||
lower : float (optional) | ||
lower : float, optional |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should it be tensor (float or 1D vector)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, arbitrary shape actually.
LGTM. The broken example |
pymc3/distributions/distribution.py
Outdated
The resulting distribution is not normalized anymore. This | ||
is usually fine if the bounds are constants. If you need | ||
truncated distributions, use `Bound` in combination with | ||
a `pm.Potential` with the cumulative probability function. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably want an example of this. Maybe just in the examples
folder, rather than a full-blown notebook.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can edit the example censored_data.py
By "before/after" do you mean before and after applying this patch? |
Is it possible to make |
Calling a |
@aseyboldt Yep |
That was a smart one. I forgot to pass the transformation to the wrapped distribution.... |
@aseyboldt |
@ferrine No, that shouldn't be true. If anything it should be |
You can, just use |
I didn't say it can't be done. But I think it is trouble and doesn't do anything useful. |
Sorry if I misunderstood you. It adds consistency in the structure. It is confusing to have failing subclass check for implementation stuff that works with distributions |
I think we shouldn't depend on any subclass checks. That is typically bad practice, too. If we need to find out more about the distribution/RV, we could add a property |
@aseyboldt can you remove the conflicts? would like to merge this into the 3.2 branch. |
There is failed test as well. |
1c7abb0
to
2bbc610
Compare
0b2196a
to
e6a7a36
Compare
I think this is ready to merge. |
@aseyboldt |
'par3', mu=0.0, sd=1.0, testval=1.0) | ||
""" | ||
|
||
def __init__(self, distribution, lower=None, upper=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you feel about returning dynamicly created class that can be ofc pickled with __reduce__
? I saw code above and that can be not so difficult and take little coding
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel worried about this extra Bound
class that does nothing and is not Distribution itself but proposed to be the one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not make this any more complicated than it already is.
Sadly, we don't distinguish properly between random variables and distributions, but use the constructor of our distribution objects to build random variables (observed and otherwise). This leads to the somewhat strange behaviour
issubclass(pm.Normal, pm.Distribution) # -> True
isinstance(pm.Normal('a'), pm.Distribution) # -> False
isinstance(pm.Normal('a'), pm.Normal) # -> False
As we really don't want to second to be true, I think the first actually shouldn't be true in the first place. We shouldn't think of our distribution classes as distributions, but as factory functions for random variables. The distributions are pm.Normal.dist
.
So I kind of don't see the point of adding another non-standard thing on top of the metaclasses and overriden __new__
that's already in there, to make a subclass check true, that shouldn't be true in the first place. Writing it isn't the problem, it just makes the code ever harder to read.
We do have the following now, which I think is the important part:
BoundNormal = pm.Bound(pm.Normal)
isinstance(BoundNormal.dist(), pm.Continuous) # -> True
The original design of pm.Bound
is I guess a bit unfortunate, we wouldn't have that problem in the first place if it took a distribution as parameter and returned a random variable:
dist = pm.Normal.dist(mu=5, sd=3)
pm.Bound("a", dist, lower=3)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds reasonable. Hope no one will meet the problem of that kind
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aseyboldt for the thing you mention above:
dist = pm.Normal.dist(mu=5, sd=3)
pm.Bound("a", dist, lower=3)
Could we not use tt.clip
to do the same thing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, clip would be more like censoring, as the values below lower
are just set to lower
. Bound mostly sets an appropriate transformation.
@junpenglao I just pushed a test for tensor bounds. |
Thanks @aseyboldt |
pm.Bound
doesn't support array valued bounds at the moment. This is fixed in this PR. I also refactored theBounded.__init__
function a bit.