diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index d64e4be310..6143d0c229 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -49,6 +49,7 @@ This will be the last release to support Python 2. - Fixed `Rice` distribution, which inconsistently mixed two parametrizations (#3286). - `Rice` distribution now accepts multiple parameters and observations and is usable with NUTS (#3289). - `sample_posterior_predictive` no longer calls `draw_values` to initialize the shape of the ppc trace. This called could lead to `ValueError`'s when sampling the ppc from a model with `Flat` or `HalfFlat` prior distributions (Fix issue #3294). +- Added explicit conversion to `floatX` and `int32` for the continuous and discrete probability distribution parameters (addresses issue #3223). ### Deprecations diff --git a/pymc3/distributions/continuous.py b/pymc3/distributions/continuous.py index 52e8669f34..8acaa25312 100644 --- a/pymc3/distributions/continuous.py +++ b/pymc3/distributions/continuous.py @@ -425,7 +425,7 @@ def __init__(self, mu=0, sigma=None, tau=None, sd=None, **kwargs): self.sigma = self.sd = tt.as_tensor_variable(sigma) self.tau = tt.as_tensor_variable(tau) - self.mean = self.median = self.mode = self.mu = mu = tt.as_tensor_variable(mu) + self.mean = self.median = self.mode = self.mu = mu = tt.as_tensor_variable(floatX(mu)) self.variance = 1. / self.tau assert_negative_support(sigma, 'sigma', 'Normal') @@ -572,9 +572,9 @@ def __init__(self, mu=0, sigma=None, tau=None, lower=None, upper=None, tau, sigma = get_tau_sigma(tau=tau, sigma=sigma) self.sigma = self.sd = tt.as_tensor_variable(sigma) self.tau = tt.as_tensor_variable(tau) - self.lower = tt.as_tensor_variable(lower) if lower is not None else lower - self.upper = tt.as_tensor_variable(upper) if upper is not None else upper - self.mu = tt.as_tensor_variable(mu) + self.lower = tt.as_tensor_variable(floatX(lower)) if lower is not None else lower + self.upper = tt.as_tensor_variable(floatX(upper)) if upper is not None else upper + self.mu = tt.as_tensor_variable(floatX(mu)) if self.lower is None and self.upper is None: self._defaultval = mu @@ -906,10 +906,10 @@ class Wald(PositiveContinuous): def __init__(self, mu=None, lam=None, phi=None, alpha=0., *args, **kwargs): super().__init__(*args, **kwargs) mu, lam, phi = self.get_mu_lam_phi(mu, lam, phi) - self.alpha = alpha = tt.as_tensor_variable(alpha) - self.mu = mu = tt.as_tensor_variable(mu) - self.lam = lam = tt.as_tensor_variable(lam) - self.phi = phi = tt.as_tensor_variable(phi) + self.alpha = alpha = tt.as_tensor_variable(floatX(alpha)) + self.mu = mu = tt.as_tensor_variable(floatX(mu)) + self.lam = lam = tt.as_tensor_variable(floatX(lam)) + self.phi = phi = tt.as_tensor_variable(floatX(phi)) self.mean = self.mu + self.alpha self.mode = self.mu * (tt.sqrt(1. + (1.5 * self.mu / self.lam)**2) @@ -1120,8 +1120,8 @@ def __init__(self, alpha=None, beta=None, mu=None, sigma=None, if sd is not None: sigma = sd alpha, beta = self.get_alpha_beta(alpha, beta, mu, sigma) - self.alpha = alpha = tt.as_tensor_variable(alpha) - self.beta = beta = tt.as_tensor_variable(beta) + self.alpha = alpha = tt.as_tensor_variable(floatX(alpha)) + self.beta = beta = tt.as_tensor_variable(floatX(beta)) self.mean = self.alpha / (self.alpha + self.beta) self.variance = self.alpha * self.beta / ( @@ -1262,8 +1262,8 @@ class Kumaraswamy(UnitContinuous): def __init__(self, a, b, *args, **kwargs): super().__init__(*args, **kwargs) - self.a = a = tt.as_tensor_variable(a) - self.b = b = tt.as_tensor_variable(b) + self.a = a = tt.as_tensor_variable(floatX(a)) + self.b = b = tt.as_tensor_variable(floatX(b)) ln_mean = tt.log(b) + tt.gammaln(1 + 1 / a) + tt.gammaln(b) - tt.gammaln(1 + 1 / a + b) self.mean = tt.exp(ln_mean) @@ -1374,7 +1374,7 @@ class Exponential(PositiveContinuous): def __init__(self, lam, *args, **kwargs): super().__init__(*args, **kwargs) - self.lam = lam = tt.as_tensor_variable(lam) + self.lam = lam = tt.as_tensor_variable(floatX(lam)) self.mean = 1. / self.lam self.median = self.mean * tt.log(2) self.mode = tt.zeros_like(self.lam) @@ -1498,8 +1498,8 @@ class Laplace(Continuous): def __init__(self, mu, b, *args, **kwargs): super().__init__(*args, **kwargs) - self.b = b = tt.as_tensor_variable(b) - self.mean = self.median = self.mode = self.mu = mu = tt.as_tensor_variable(mu) + self.b = b = tt.as_tensor_variable(floatX(b)) + self.mean = self.median = self.mode = self.mu = mu = tt.as_tensor_variable(floatX(mu)) self.variance = 2 * self.b**2 @@ -1639,7 +1639,7 @@ def __init__(self, mu=0, sigma=None, tau=None, sd=None, *args, **kwargs): tau, sigma = get_tau_sigma(tau=tau, sigma=sigma) - self.mu = mu = tt.as_tensor_variable(mu) + self.mu = mu = tt.as_tensor_variable(floatX(mu)) self.tau = tau = tt.as_tensor_variable(tau) self.sigma = self.sd = sigma = tt.as_tensor_variable(sigma) @@ -1791,10 +1791,10 @@ class StudentT(Continuous): def __init__(self, nu, mu=0, lam=None, sigma=None, sd=None, *args, **kwargs): super().__init__(*args, **kwargs) + super(StudentT, self).__init__(*args, **kwargs) if sd is not None: sigma = sd - - self.nu = nu = tt.as_tensor_variable(nu) + self.nu = nu = tt.as_tensor_variable(floatX(nu)) lam, sigma = get_tau_sigma(tau=lam, sigma=sigma) self.lam = lam = tt.as_tensor_variable(lam) self.sigma = self.sd = sigma = tt.as_tensor_variable(sigma) @@ -1923,8 +1923,8 @@ class Pareto(Continuous): """ def __init__(self, alpha, m, transform='lowerbound', *args, **kwargs): - self.alpha = alpha = tt.as_tensor_variable(alpha) - self.m = m = tt.as_tensor_variable(m) + self.alpha = alpha = tt.as_tensor_variable(floatX(alpha)) + self.m = m = tt.as_tensor_variable(floatX(m)) self.mean = tt.switch(tt.gt(alpha, 1), alpha * m / (alpha - 1.), np.inf) @@ -2061,8 +2061,8 @@ class Cauchy(Continuous): def __init__(self, alpha, beta, *args, **kwargs): super().__init__(*args, **kwargs) - self.median = self.mode = self.alpha = tt.as_tensor_variable(alpha) - self.beta = tt.as_tensor_variable(beta) + self.median = self.mode = self.alpha = tt.as_tensor_variable(floatX(alpha)) + self.beta = tt.as_tensor_variable(floatX(beta)) assert_negative_support(beta, 'beta', 'Cauchy') @@ -2171,8 +2171,7 @@ class HalfCauchy(PositiveContinuous): def __init__(self, beta, *args, **kwargs): super().__init__(*args, **kwargs) self.mode = tt.as_tensor_variable(0) - self.median = tt.as_tensor_variable(beta) - self.beta = tt.as_tensor_variable(beta) + self.median = self.beta = tt.as_tensor_variable(floatX(beta)) assert_negative_support(beta, 'beta', 'HalfCauchy') @@ -2303,8 +2302,8 @@ def __init__(self, alpha=None, beta=None, mu=None, sigma=None, sigma = sd alpha, beta = self.get_alpha_beta(alpha, beta, mu, sigma) - self.alpha = alpha = tt.as_tensor_variable(alpha) - self.beta = beta = tt.as_tensor_variable(beta) + self.alpha = alpha = tt.as_tensor_variable(floatX(alpha)) + self.beta = beta = tt.as_tensor_variable(floatX(beta)) self.mean = alpha / beta self.mode = tt.maximum((alpha - 1) / beta, 0) self.variance = alpha / beta**2 @@ -2438,8 +2437,8 @@ def __init__(self, alpha=None, beta=None, mu=None, sigma=None, sd=None, sigma = sd alpha, beta = InverseGamma._get_alpha_beta(alpha, beta, mu, sigma) - self.alpha = alpha = tt.as_tensor_variable(alpha) - self.beta = beta = tt.as_tensor_variable(beta) + self.alpha = alpha = tt.as_tensor_variable(floatX(alpha)) + self.beta = beta = tt.as_tensor_variable(floatX(beta)) self.mean = self._calculate_mean() self.mode = beta / (alpha + 1.) @@ -2567,7 +2566,7 @@ class ChiSquared(Gamma): """ def __init__(self, nu, *args, **kwargs): - self.nu = nu = tt.as_tensor_variable(nu) + self.nu = nu = tt.as_tensor_variable(floatX(nu)) super().__init__(alpha=nu / 2., beta=0.5, *args, **kwargs) def _repr_latex_(self, name=None, dist=None): @@ -2625,8 +2624,8 @@ class Weibull(PositiveContinuous): def __init__(self, alpha, beta, *args, **kwargs): super().__init__(*args, **kwargs) - self.alpha = alpha = tt.as_tensor_variable(alpha) - self.beta = beta = tt.as_tensor_variable(beta) + self.alpha = alpha = tt.as_tensor_variable(floatX(alpha)) + self.beta = beta = tt.as_tensor_variable(floatX(beta)) self.mean = beta * tt.exp(gammaln(1 + 1. / alpha)) self.median = beta * tt.exp(gammaln(tt.log(2)))**(1. / alpha) self.variance = (beta**2) * \ @@ -2788,7 +2787,7 @@ def __init__(self, nu=1, sigma=None, lam=None, sd=None, self.median = tt.as_tensor_variable(sigma) self.sigma = self.sd = tt.as_tensor_variable(sigma) self.lam = tt.as_tensor_variable(lam) - self.nu = nu = tt.as_tensor_variable(nu) + self.nu = nu = tt.as_tensor_variable(floatX(nu)) assert_negative_support(sigma, 'sigma', 'HalfStudentT') assert_negative_support(lam, 'lam', 'HalfStudentT') @@ -2923,9 +2922,9 @@ def __init__(self, mu=0., sigma=None, nu=None, sd=None, if sd is not None: sigma = sd - self.mu = mu = tt.as_tensor_variable(mu) - self.sigma = self.sd = sigma = tt.as_tensor_variable(sigma) - self.nu = nu = tt.as_tensor_variable(nu) + self.mu = mu = tt.as_tensor_variable(floatX(mu)) + self.sigma = self.sd = sigma = tt.as_tensor_variable(floatX(sigma)) + self.nu = nu = tt.as_tensor_variable(floatX(nu)) self.mean = mu + nu self.variance = (sigma**2) + (nu**2) @@ -3074,8 +3073,8 @@ def __init__(self, mu=0.0, kappa=None, transform='circular', if transform == 'circular': transform = transforms.Circular() super().__init__(transform=transform, *args, **kwargs) - self.mean = self.median = self.mode = self.mu = mu = tt.as_tensor_variable(mu) - self.kappa = kappa = floatX(tt.as_tensor_variable(kappa)) + self.mean = self.median = self.mode = self.mu = mu = tt.as_tensor_variable(floatX(mu)) + self.kappa = kappa = tt.as_tensor_variable(floatX(kappa)) assert_negative_support(kappa, 'kappa', 'VonMises') @@ -3199,11 +3198,11 @@ def __init__(self, mu=0.0, sigma=None, tau=None, alpha=1, sd=None, sigma = sd tau, sigma = get_tau_sigma(tau=tau, sigma=sigma) - self.mu = mu = tt.as_tensor_variable(mu) + self.mu = mu = tt.as_tensor_variable(floatX(mu)) self.tau = tt.as_tensor_variable(tau) self.sigma = self.sd = tt.as_tensor_variable(sigma) - self.alpha = alpha = tt.as_tensor_variable(alpha) + self.alpha = alpha = tt.as_tensor_variable(floatX(alpha)) self.mean = mu + self.sigma * (2 / np.pi)**0.5 * alpha / (1 + alpha**2)**0.5 self.variance = self.sigma**2 * (1 - (2 * alpha**2) / ((1 + alpha**2) * np.pi)) @@ -3329,9 +3328,9 @@ class Triangular(BoundedContinuous): def __init__(self, lower=0, upper=1, c=0.5, *args, **kwargs): - self.median = self.mean = self.c = c = tt.as_tensor_variable(c) - self.lower = lower = tt.as_tensor_variable(lower) - self.upper = upper = tt.as_tensor_variable(upper) + self.median = self.mean = self.c = c = tt.as_tensor_variable(floatX(c)) + self.lower = lower = tt.as_tensor_variable(floatX(lower)) + self.upper = upper = tt.as_tensor_variable(floatX(upper)) super().__init__(lower=lower, upper=upper, *args, **kwargs) @@ -3464,8 +3463,8 @@ class Gumbel(Continuous): """ def __init__(self, mu=0, beta=1.0, **kwargs): - self.mu = tt.as_tensor_variable(mu) - self.beta = tt.as_tensor_variable(beta) + self.mu = tt.as_tensor_variable(floatX(mu)) + self.beta = tt.as_tensor_variable(floatX(beta)) assert_negative_support(beta, 'beta', 'Gumbel') @@ -3580,9 +3579,9 @@ def __init__(self, nu=None, sigma=None, b=None, sd=None, *args, **kwargs): sigma = sd nu, b, sigma = self.get_nu_b(nu, b, sigma) - self.nu = nu = tt.as_tensor_variable(nu) - self.sigma = self.sd = sigma = tt.as_tensor_variable(sigma) - self.b = b = tt.as_tensor_variable(b) + self.nu = nu = tt.as_tensor_variable(floatX(nu)) + self.sigma = self.sd = sigma = tt.as_tensor_variable(floatX(sigma)) + self.b = b = tt.as_tensor_variable(floatX(b)) self.mean = sigma * np.sqrt(np.pi / 2) * tt.exp((-nu**2 / (2 * sigma**2)) / 2) * ((1 - (-nu**2 / (2 * sigma**2))) * tt.i0(-(-nu**2 / (2 * sigma**2)) / 2) - (-nu**2 / (2 * sigma**2)) * tt.i1(-(-nu**2 / (2 * sigma**2)) / 2)) self.variance = 2 * sigma**2 + nu**2 - (np.pi * sigma**2 / 2) * (tt.exp((-nu**2 / (2 * sigma**2)) / 2) * ((1 - (-nu**2 / ( @@ -3693,8 +3692,8 @@ class Logistic(Continuous): def __init__(self, mu=0., s=1., *args, **kwargs): super().__init__(*args, **kwargs) - self.mu = tt.as_tensor_variable(mu) - self.s = tt.as_tensor_variable(s) + self.mu = tt.as_tensor_variable(floatX(mu)) + self.s = tt.as_tensor_variable(floatX(s)) self.mean = self.mode = mu self.variance = s**2 * np.pi**2 / 3. @@ -3826,7 +3825,7 @@ class LogitNormal(UnitContinuous): def __init__(self, mu=0, sigma=None, tau=None, sd=None, **kwargs): if sd is not None: sigma = sd - self.mu = mu = tt.as_tensor_variable(mu) + self.mu = mu = tt.as_tensor_variable(floatX(mu)) tau, sigma = get_tau_sigma(tau=tau, sigma=sigma) self.sigma = self.sd = tt.as_tensor_variable(sigma) self.tau = tau = tt.as_tensor_variable(tau) diff --git a/pymc3/distributions/discrete.py b/pymc3/distributions/discrete.py index f4f0b99280..cd963110cf 100644 --- a/pymc3/distributions/discrete.py +++ b/pymc3/distributions/discrete.py @@ -9,6 +9,7 @@ from .distribution import (Discrete, draw_values, generate_samples, broadcast_distribution_samples) from pymc3.math import tround, sigmoid, logaddexp, logit, log1pexp +from ..theanof import floatX, intX __all__ = ['Binomial', 'BetaBinomial', 'Bernoulli', 'DiscreteWeibull', @@ -61,8 +62,8 @@ class Binomial(Discrete): def __init__(self, n, p, *args, **kwargs): super().__init__(*args, **kwargs) - self.n = n = tt.as_tensor_variable(n) - self.p = p = tt.as_tensor_variable(p) + self.n = n = tt.as_tensor_variable(intX(n)) + self.p = p = tt.as_tensor_variable(floatX(p)) self.mode = tt.cast(tround(n * p), self.dtype) def random(self, point=None, size=None): @@ -147,9 +148,9 @@ def BetaBinom(a, b, n, x): def __init__(self, alpha, beta, n, *args, **kwargs): super().__init__(*args, **kwargs) - self.alpha = alpha = tt.as_tensor_variable(alpha) - self.beta = beta = tt.as_tensor_variable(beta) - self.n = n = tt.as_tensor_variable(n) + self.alpha = alpha = tt.as_tensor_variable(floatX(alpha)) + self.beta = beta = tt.as_tensor_variable(floatX(beta)) + self.n = n = tt.as_tensor_variable(intX(n)) self.mode = tt.cast(tround(alpha / (alpha + beta)), 'int8') def _random(self, alpha, beta, n, size=None): @@ -244,11 +245,11 @@ def __init__(self, p=None, logit_p=None, *args, **kwargs): raise ValueError('Specify one of p and logit_p') if p is not None: self._is_logit = False - self.p = p = tt.as_tensor_variable(p) + self.p = p = tt.as_tensor_variable(floatX(p)) self._logit_p = logit(p) else: self._is_logit = True - self.p = tt.nnet.sigmoid(logit_p) + self.p = tt.nnet.sigmoid(floatX(logit_p)) self._logit_p = tt.as_tensor_variable(logit_p) self.mode = tt.cast(tround(self.p), 'int8') @@ -320,8 +321,8 @@ def DiscreteWeibull(q, b, x): def __init__(self, q, beta, *args, **kwargs): super().__init__(*args, defaults=('median',), **kwargs) - self.q = q = tt.as_tensor_variable(q) - self.beta = beta = tt.as_tensor_variable(beta) + self.q = q = tt.as_tensor_variable(floatX(q)) + self.beta = beta = tt.as_tensor_variable(floatX(beta)) self.median = self._ppf(0.5) @@ -414,8 +415,8 @@ class Poisson(Discrete): def __init__(self, mu, *args, **kwargs): super().__init__(*args, **kwargs) - self.mu = mu = tt.as_tensor_variable(mu) - self.mode = tt.floor(mu).astype('int32') + self.mu = mu = tt.as_tensor_variable(floatX(mu)) + self.mode = intX(tt.floor(mu)) def random(self, point=None, size=None): mu = draw_values([self.mu], point=point, size=size)[0] @@ -493,9 +494,9 @@ def NegBinom(a, m, x): def __init__(self, mu, alpha, *args, **kwargs): super().__init__(*args, **kwargs) - self.mu = mu = tt.as_tensor_variable(mu) - self.alpha = alpha = tt.as_tensor_variable(alpha) - self.mode = tt.floor(mu).astype('int32') + self.mu = mu = tt.as_tensor_variable(floatX(mu)) + self.alpha = alpha = tt.as_tensor_variable(floatX(alpha)) + self.mode = intX(tt.floor(mu)) def random(self, point=None, size=None): mu, alpha = draw_values([self.mu, self.alpha], point=point, size=size) @@ -568,7 +569,7 @@ class Geometric(Discrete): def __init__(self, p, *args, **kwargs): super().__init__(*args, **kwargs) - self.p = p = tt.as_tensor_variable(p) + self.p = p = tt.as_tensor_variable(floatX(p)) self.mode = 1 def random(self, point=None, size=None): @@ -632,10 +633,10 @@ class DiscreteUniform(Discrete): def __init__(self, lower, upper, *args, **kwargs): super().__init__(*args, **kwargs) - self.lower = tt.floor(lower).astype('int32') - self.upper = tt.floor(upper).astype('int32') + self.lower = intX(tt.floor(lower)) + self.upper = intX(tt.floor(upper)) self.mode = tt.maximum( - tt.floor((upper + lower) / 2.).astype('int32'), self.lower) + intX(tt.floor((upper + lower) / 2.)), self.lower) def _random(self, lower, upper, size=None): # This way seems to be the only to deal with lower and upper @@ -708,7 +709,7 @@ def __init__(self, p, *args, **kwargs): self.k = tt.shape(p)[-1].tag.test_value except AttributeError: self.k = tt.shape(p)[-1] - p = tt.as_tensor_variable(p) + p = tt.as_tensor_variable(floatX(p)) self.p = (p.T / tt.sum(p, -1)).T self.mode = tt.argmax(p) @@ -839,8 +840,8 @@ class ZeroInflatedPoisson(Discrete): def __init__(self, psi, theta, *args, **kwargs): super().__init__(*args, **kwargs) - self.theta = theta = tt.as_tensor_variable(theta) - self.psi = psi = tt.as_tensor_variable(psi) + self.theta = theta = tt.as_tensor_variable(floatX(theta)) + self.psi = psi = tt.as_tensor_variable(floatX(psi)) self.pois = Poisson.dist(theta) self.mode = self.pois.mode @@ -931,9 +932,9 @@ class ZeroInflatedBinomial(Discrete): def __init__(self, psi, n, p, *args, **kwargs): super().__init__(*args, **kwargs) - self.n = n = tt.as_tensor_variable(n) - self.p = p = tt.as_tensor_variable(p) - self.psi = psi = tt.as_tensor_variable(psi) + self.n = n = tt.as_tensor_variable(intX(n)) + self.p = p = tt.as_tensor_variable(floatX(p)) + self.psi = psi = tt.as_tensor_variable(floatX(psi)) self.bin = Binomial.dist(n, p) self.mode = self.bin.mode @@ -1048,9 +1049,9 @@ def ZeroInfNegBinom(a, m, psi, x): def __init__(self, psi, mu, alpha, *args, **kwargs): super().__init__(*args, **kwargs) - self.mu = mu = tt.as_tensor_variable(mu) - self.alpha = alpha = tt.as_tensor_variable(alpha) - self.psi = psi = tt.as_tensor_variable(psi) + self.mu = mu = tt.as_tensor_variable(floatX(mu)) + self.alpha = alpha = tt.as_tensor_variable(floatX(alpha)) + self.psi = psi = tt.as_tensor_variable(floatX(psi)) self.nb = NegativeBinomial.dist(mu, alpha) self.mode = self.nb.mode @@ -1168,7 +1169,7 @@ class OrderedLogistic(Categorical): """ def __init__(self, eta, cutpoints, *args, **kwargs): - self.eta = tt.as_tensor_variable(eta) + self.eta = tt.as_tensor_variable(floatX(eta)) self.cutpoints = tt.as_tensor_variable(cutpoints) pa = sigmoid(tt.shape_padleft(self.cutpoints) - tt.shape_padright(self.eta)) diff --git a/pymc3/tests/test_distributions.py b/pymc3/tests/test_distributions.py index 41bbdf435a..2fef74d2d2 100644 --- a/pymc3/tests/test_distributions.py +++ b/pymc3/tests/test_distributions.py @@ -309,8 +309,10 @@ def logpow(v, p): def discrete_weibull_logpmf(value, q, beta): - return floatX(np.log(np.power(q, np.power(value, beta)) - - np.power(q, np.power(value + 1, beta)))) + return floatX(np.log(np.power(floatX(q), + np.power(floatX(value), floatX(beta))) + - np.power(floatX(q), np.power(floatX(value + 1), + floatX(beta))))) def dirichlet_logpdf(value, a): @@ -566,11 +568,6 @@ def scipy_logp(value, mu, sigma, lower, upper): return sp.truncnorm.logpdf( value, (lower-mu)/sigma, (upper-mu)/sigma, loc=mu, scale=sigma) - args = {'mu': array(-2.1), 'lower': array(-100.), 'upper': array(0.01), - 'sigma': array(0.01)} - val = TruncatedNormal.dist(**args).logp(0.) - assert_allclose(val.eval(), scipy_logp(value=0, **args)) - self.pymc3_matches_scipy( TruncatedNormal, R, {'mu': R, 'sigma': Rplusbig, 'lower': -Rplusbig, 'upper': Rplusbig}, @@ -1292,16 +1289,16 @@ def setup_class(self): sigma = HalfNormal('sigma', sigma=1) # Expected value of outcome - mu = Deterministic('mu', alpha + tt.dot(X, b)) + mu = Deterministic('mu', floatX(alpha + tt.dot(X, b))) # Likelihood (sampling distribution) of observations Y_obs = Normal('Y_obs', mu=mu, sigma=sigma, observed=Y) self.distributions = [alpha, sigma, mu, b, Y_obs] self.expected = ( - r'$\text{alpha} \sim \text{Normal}(\mathit{mu}=0,~\mathit{sigma}=10.0)$', + r'$\text{alpha} \sim \text{Normal}(\mathit{mu}=0.0,~\mathit{sigma}=10.0)$', r'$\text{sigma} \sim \text{HalfNormal}(\mathit{sigma}=1.0)$', r'$\text{mu} \sim \text{Deterministic}(\text{alpha},~\text{Constant},~\text{beta})$', - r'$\text{beta} \sim \text{Normal}(\mathit{mu}=0,~\mathit{sigma}=10.0)$', + r'$\text{beta} \sim \text{Normal}(\mathit{mu}=0.0,~\mathit{sigma}=10.0)$', r'$\text{Y_obs} \sim \text{Normal}(\mathit{mu}=\text{mu},~\mathit{sigma}=f(\text{sigma}))$' ) diff --git a/pymc3/tests/test_transforms.py b/pymc3/tests/test_transforms.py index 5b7dc58345..7396e253d7 100644 --- a/pymc3/tests/test_transforms.py +++ b/pymc3/tests/test_transforms.py @@ -249,8 +249,10 @@ def check_vectortransform_elementwise_logp(self, model, vect_opt=0): elementwiselogp = logp_nojac.sum(axis=-1) + jacob_det else: elementwiselogp = logp_nojac + jacob_det - - close_to(x.logp_elemwise(pt), elementwiselogp.eval(), tol) + # Hack to get relative tolerance + a = x.logp_elemwise(pt) + b = elementwiselogp.eval() + close_to(a, b, np.abs(0.5 * (a + b) * tol)) @pytest.mark.parametrize('sd,shape', [ (2.5, 2), diff --git a/pymc3/tests/test_variational_inference.py b/pymc3/tests/test_variational_inference.py index c6a199c0c0..2ae804523b 100644 --- a/pymc3/tests/test_variational_inference.py +++ b/pymc3/tests/test_variational_inference.py @@ -9,7 +9,10 @@ import pymc3 as pm import pymc3.memoize import pymc3.util -from pymc3.theanof import change_flags +from pymc3.theanof import ( + change_flags, + intX, +) from pymc3.variational.approximations import ( MeanFieldGroup, FullRankGroup, NormalizingFlowGroup, EmpiricalGroup, @@ -848,7 +851,7 @@ def test_pickle_approx_aevb(three_var_aevb_approx): @pytest.fixture('module') def binomial_model(): n_samples = 100 - xs = np.random.binomial(n=1, p=0.2, size=n_samples) + xs = intX(np.random.binomial(n=1, p=0.2, size=n_samples)) with pm.Model() as model: p = pm.Beta('p', alpha=1, beta=1) pm.Binomial('xs', n=1, p=p, observed=xs) diff --git a/pymc3/theanof.py b/pymc3/theanof.py index 49df9a8946..5403dd38e9 100644 --- a/pymc3/theanof.py +++ b/pymc3/theanof.py @@ -16,6 +16,7 @@ 'inputvars', 'cont_inputs', 'floatX', + 'intX', 'smartfloatX', 'jacobian', 'CallableTensor', @@ -67,6 +68,24 @@ def floatX(X): return np.asarray(X, dtype=theano.config.floatX) +_conversion_map = {'float64': 'int32', + 'float32': 'int16', + 'float16': 'int8', + 'float8': 'int8'} + + +def intX(X): + """ + Convert a theano tensor or numpy array to theano.tensor.int32 type. + """ + intX = _conversion_map[theano.config.floatX] + try: + return X.astype(intX) + except AttributeError: + # Scalar passed + return np.asarray(X, dtype=intX) + + def smartfloatX(x): """ Convert non int types to floatX