Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
- Add `logit_p` keyword to `pm.Bernoulli`, so that users can specify the logit
of the success probability. This is faster and more stable than using
`p=tt.nnet.sigmoid(logit_p)`.
- Add `random` keyword to `pm.DensityDist` thus enabling users to pass custom random method
which in turn makes sampling from a `DensityDist` possible.

### Deprecations

Expand Down
11 changes: 10 additions & 1 deletion pymc3/distributions/distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,21 @@ def __init__(self, shape=(), dtype=None, defaults=('median', 'mean', 'mode'),
class DensityDist(Distribution):
"""Distribution based on a given log density function."""

def __init__(self, logp, shape=(), dtype=None, testval=0, *args, **kwargs):
def __init__(self, logp, shape=(), dtype=None, testval=0, random=None, *args, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

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

Should add this to the doc-string.

if dtype is None:
dtype = theano.config.floatX
super(DensityDist, self).__init__(
shape, dtype, testval, *args, **kwargs)
self.logp = logp
self.rand = random

def random(self, *args, **kwargs):
if self.rand is not None:
return self.rand(*args, **kwargs)
else:
raise ValueError("Distribution was not passed any random method "
"Define a custom random method and pass it as kwarg random")



def draw_values(params, point=None):
Expand Down
44 changes: 44 additions & 0 deletions pymc3/tests/test_distributions_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,3 +688,47 @@ def ref_rand(size, w, mu, sd):
'sd': Domain([[1.5, 2., 3.]], edges=(None, None))},
size=1000,
ref_rand=ref_rand)

def test_density_dist(self):
def ref_rand(size, mu, sd):
return st.norm.rvs(size=size, loc=mu, scale=sd)

class TestDensityDist(pm.DensityDist):

def __init__(self, **kwargs):
norm_dist = pm.Normal.dist()
super(TestDensityDist, self).__init__(logp=norm_dist.logp, random=norm_dist.random)

pymc3_random(TestDensityDist, {},ref_rand=ref_rand)

def check_model_samplability(self):
model = pm.Model()
with model:
normal_dist = pm.Normal.dist()
density_dist = pm.DensityDist('density_dist', normal_dist.logp, random=normal_dist.random)
Copy link
Member

Choose a reason for hiding this comment

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

This looks great. Does it work with a scipy distribution inside a lambda as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't quite get that, can you elaborate on that a little?
My interpretation is something like

def mynormal_logp():
	norm_dist_logp = st.norm.logpdf
	norm_dist_random = np.random.normal
	density_dist = pm.DensityDist('density_dist', norm_dist_logp, random=norm_dist_random)

is that what you meant?

Copy link
Member

Choose a reason for hiding this comment

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

Exactly.

Copy link
Contributor Author

@Vaibhavdixit02 Vaibhavdixit02 Feb 2, 2018

Choose a reason for hiding this comment

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

I'll test this locally and check! BTW does PyMC3 handle scipy distributions in general?

step = pm.Metropolis()
trace = pm.sample(100, step, tuning=0)

try:
ppc = pm.sample_ppc(trace, samples=500, model=model, size=100)
if len(ppc) == 0:
npt.assert_true(len(ppc) == 0, 'length of ppc sample is zero')
except:
assert False

def check_scipy_distributions(self):
model = pm.Model()
with model:
norm_dist_logp = st.norm.logpdf
norm_dist_random = np.random.normal
density_dist = pm.DensityDist('density_dist', normal_dist_logp, random=normal_dist_random)
Copy link
Member

Choose a reason for hiding this comment

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

I just realized, I think we need to turn this into an observed to actually get sampling from this.

step = pm.Metropolis()
trace = pm.sample(100, step, tuning=0)

try:
ppc = pm.sample_ppc(trace, samples=500, model=model, size=100)
if len(ppc) == 0:
Copy link
Member

Choose a reason for hiding this comment

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

can remove this line.

npt.assert_true(len(ppc) == 0, 'length of ppc sample is zero')
except:
assert False