Here it is. The CustomDist doesn’t explicitly forbid negative values but the prior predictive plot shows that there are no sampled negative values.
import numpy as np
import plotly.express as px
import pymc as pm
def dist_beta(a, b, loc, scale,size):
return pm.Beta.dist(a, b, size=size) * scale + loc
def dist_gamma(a,b,loc,size):
return pm.Gamma.dist(a, b, size = size) + loc
def logp_beta(x, a, b, loc, scale):
return (x - loc)/scale
def logp_gamma(x, a, b, loc):
return x - loc
sd_mult = 0.01
with pm.Model() as m:
# Left beta
left_a = pm.Normal('left_a', 1.70, 1.70 * sd_mult )
left_b = pm.Normal('left_b', 1.11, 1.11 * sd_mult )
left_loc = pm.Normal('left_loc', 2.0e6, 2.0e6 * sd_mult )
left_scale = pm.Normal('left_scale', 1.57e5, 1.57e5 * sd_mult )
# Middle beta
middle_a = pm.Normal('middle_a', 0.75, 0.75 * sd_mult )
middle_b = pm.Normal('middle_b', 1.11, 1.11 * sd_mult )
middle_loc = pm.Normal('middle_loc', 2.16e6, 2.16e6 * sd_mult )
middle_scale = pm.Normal('middle_scale', 8.6e4, 8.6e4 * sd_mult )
gamma_a = pm.Normal('gamma_a', 344.92, 344.92 * sd_mult )
gamma_b = pm.Normal('gamma_b', 1 / 1045, (1 / 1045) * sd_mult )
gamma_loc = pm.Normal('gamma_loc', 1.92e6, 1.92e6 * sd_mult)
left_beta = pm.CustomDist.dist(
left_a,
left_b,
left_loc,
left_scale,
dist=dist_beta,
logp = logp_beta,
class_name='left_beta'
)
middle_beta = pm.CustomDist.dist(
middle_a,
middle_b,
middle_loc,
middle_scale,
dist=dist_beta,
logp = logp_beta,
class_name='middle_beta'
)
right = pm.CustomDist.dist(
gamma_a,
gamma_b,
gamma_loc,
dist=dist_gamma,
logp = logp_gamma,
class_name='gamma'
)
w = pm.Dirichlet('w', np.ones(3))
data = np.array([2.207e6, 2.064e6, 2.156e6, 2.057e6, 2.089e6, 2.205e6])
resp = pm.Mixture('resp', w = w, comp_dists=[left_beta, middle_beta, right], observed=data)
trace = pm.sample()
with m:
prior = pm.sample_prior_predictive()
post = pm.sample_posterior_predictive(trace)
Prior predictive samples look good:
px.histogram(prior.prior_predictive.resp.to_numpy().reshape(-1))
But the posteriors are strange:
px.histogram(post.posterior_predictive.resp.to_numpy().reshape(-1))