Skip to content

Commit

Permalink
started splitting up dist. classes; some small testval test changes
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonwillard committed Jan 2, 2017
1 parent 9f99178 commit 9563d8a
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 97 deletions.
34 changes: 21 additions & 13 deletions pymc3/distributions/continuous.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@

import numpy as np
import theano.tensor as tt
from theano.gof.op import get_test_value
from scipy import stats
import warnings

from . import transforms
from .dist_math import bound, logpow, gammaln, betaln, std_cdf, i0, i1
from .distribution import UnivariateContinuous, draw_values, generate_samples
from .distribution import Univariate, Continuous, draw_values, generate_samples

__all__ = ['Uniform', 'Flat', 'Normal', 'Beta', 'Exponential', 'Laplace',
'StudentT', 'Cauchy', 'HalfCauchy', 'Gamma', 'Weibull',
Expand All @@ -23,16 +24,24 @@
'VonMises', 'SkewNormal']


class PositiveUnivariateContinuous(UnivariateContinuous):
class PositiveUnivariateContinuous(Univariate, Continuous):
"""Base class for positive univariate continuous distributions"""

def __init__(self, *args, **kwargs):
transform = kwargs.get('transform', transforms.log)
super(PositiveUnivariateContinuous, self).__init__(transform=transform,
*args, **kwargs)
# TODO: is there a better way to use Theano's
# existing `...tag.test_value` mechanics?
ndim_sum = self.ndim_supp + self.ndim_ind + self.ndim_reps
if self.testval is None:
if ndim_sum == 0:
self.testval = 0.5
else:
self.testval = get_test_value(tt.alloc(*((0.5,) + self.shape)))


class UnitUnivariateContinuous(UnivariateContinuous):
class UnitUnivariateContinuous(Univariate, Continuous):
"""Base class for univariate continuous distributions in [0,1]"""

def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -103,7 +112,7 @@ def get_tau_sd(tau=None, sd=None):
return (tt.as_tensor_variable(tau), tt.as_tensor_variable(sd))


class Uniform(UnivariateContinuous):
class Uniform(Univariate, Continuous):
R"""
Continuous uniform log-likelihood.
Expand Down Expand Up @@ -157,7 +166,7 @@ def logp(self, value):
value >= lower, value <= upper)


class Flat(UnivariateContinuous):
class Flat(Univariate, Continuous):
"""
Uninformative log-likelihood that returns 0 regardless of
the passed value.
Expand All @@ -178,7 +187,7 @@ def logp(self, value):
return tt.zeros_like(value)


class Normal(UnivariateContinuous):
class Normal(Univariate, Continuous):
R"""
Univariate normal log-likelihood.
Expand Down Expand Up @@ -573,7 +582,7 @@ def logp(self, value):
return bound(tt.log(lam) - lam * value, value > 0, lam > 0)


class Laplace(UnivariateContinuous):
class Laplace(Univariate, Continuous):
R"""
Laplace log-likelihood.
Expand Down Expand Up @@ -686,7 +695,7 @@ def logp(self, value):
tau > 0)


class StudentT(UnivariateContinuous):
class StudentT(Univariate, Continuous):
r"""
Non-central Student's T log-likelihood.
Expand Down Expand Up @@ -812,7 +821,7 @@ def logp(self, value):
value >= m, alpha > 0, m > 0)


class Cauchy(UnivariateContinuous):
class Cauchy(Univariate, Continuous):
R"""
Cauchy log-likelihood.
Expand Down Expand Up @@ -1154,7 +1163,7 @@ def logp(self, value):
value >= 0, alpha > 0, beta > 0)


class Bounded(UnivariateContinuous):
class Bounded(Univariate, Continuous):
R"""
An upper, lower or upper+lower bounded distribution
Expand Down Expand Up @@ -1269,8 +1278,7 @@ def StudentTpos(*args, **kwargs):

HalfStudentT = Bound(StudentT, lower=0)


class ExGaussian(UnivariateContinuous):
class ExGaussian(Univariate, Continuous):
R"""
Exponentially modified Gaussian log-likelihood.
Expand Down Expand Up @@ -1356,7 +1364,7 @@ def logp(self, value):
return bound(lp, sigma > 0., nu > 0.)


class VonMises(UnivariateContinuous):
class VonMises(Univariate, Continuous):
R"""
Univariate VonMises log-likelihood.
Expand Down
23 changes: 11 additions & 12 deletions pymc3/distributions/discrete.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
from scipy import stats

from .dist_math import bound, factln, binomln, betaln, logpow
from .distribution import UnivariateDiscrete, draw_values, generate_samples
from .distribution import Univariate, Discrete, draw_values, generate_samples

__all__ = ['Binomial', 'BetaBinomial', 'Bernoulli', 'Poisson',
'NegativeBinomial', 'ConstantDist', 'Constant', 'ZeroInflatedPoisson',
'ZeroInflatedNegativeBinomial', 'DiscreteUniform', 'Geometric',
'Categorical']


class Binomial(UnivariateDiscrete):
class Binomial(Univariate, Discrete):
R"""
Binomial log-likelihood.
Expand Down Expand Up @@ -63,7 +63,7 @@ def logp(self, value):
0 <= p, p <= 1)


class BetaBinomial(UnivariateDiscrete):
class BetaBinomial(Univariate, Discrete):
R"""
Beta-binomial log-likelihood.
Expand Down Expand Up @@ -132,7 +132,7 @@ def logp(self, value):
alpha > 0, beta > 0)


class Bernoulli(UnivariateDiscrete):
class Bernoulli(Univariate, Discrete):
R"""Bernoulli log-likelihood
The Bernoulli distribution describes the probability of successes
Expand Down Expand Up @@ -174,7 +174,7 @@ def logp(self, value):
p >= 0, p <= 1)


class Poisson(UnivariateDiscrete):
class Poisson(Univariate, Discrete):
R"""
Poisson log-likelihood.
Expand Down Expand Up @@ -225,7 +225,7 @@ def logp(self, value):
0, log_prob)


class NegativeBinomial(UnivariateDiscrete):
class NegativeBinomial(Univariate, Discrete):
R"""
Negative binomial log-likelihood.
Expand Down Expand Up @@ -282,7 +282,7 @@ def logp(self, value):
negbinom)


class Geometric(UnivariateDiscrete):
class Geometric(Univariate, Discrete):
R"""
Geometric log-likelihood.
Expand Down Expand Up @@ -322,7 +322,7 @@ def logp(self, value):
0 <= p, p <= 1, value >= 1)


class DiscreteUniform(UnivariateDiscrete):
class DiscreteUniform(Univariate, Discrete):
R"""
Discrete uniform distribution.
Expand Down Expand Up @@ -373,7 +373,7 @@ def logp(self, value):
lower <= value, value <= upper)


class Categorical(UnivariateDiscrete):
class Categorical(Univariate, Discrete):
R"""
Categorical log-likelihood.
Expand Down Expand Up @@ -437,7 +437,7 @@ def logp(self, value):
sumto1)


class ConstantDist(UnivariateDiscrete):
class ConstantDist(Univariate, Discrete):
"""
Constant log-likelihood.
Expand Down Expand Up @@ -472,8 +472,7 @@ def ConstantDist(*args, **kwargs):
DeprecationWarning)
return Constant(*args, **kwargs)


class ZeroInflatedPoisson(Discrete):
class ZeroInflatedPoisson(Univariate, Discrete):
R"""
Zero-inflated Poisson log-likelihood.
Expand Down
69 changes: 15 additions & 54 deletions pymc3/distributions/distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,32 +42,6 @@ def _as_tensor_shape_variable(var):
return res


def _as_tensor_shape_variable(var):
""" Just a collection of useful shape stuff from
`_infer_ndim_bcast` """

if var is None:
return T.constant([], dtype='int64')

res = var
if isinstance(res, (tuple, list)):
if len(res) == 0:
return T.constant([], dtype='int64')
res = T.as_tensor_variable(res, ndim=1)

else:
if res.ndim != 1:
raise TypeError("shape must be a vector or list of scalar, got\
'%s'" % res)

if (not (res.dtype.startswith('int') or
res.dtype.startswith('uint'))):

raise TypeError('shape must be an integer vector or list',
res.dtype)
return res


class Distribution(object):
"""Statistical distribution"""
def __new__(cls, name, *args, **kwargs):
Expand Down Expand Up @@ -186,23 +160,20 @@ def __init__(self, shape_supp, shape_ind, shape_reps, bcast, dtype,
tuple(self.shape_ind) +\
tuple(self.shape_supp)

if testval is None:
if ndim_sum == 0:
testval = tt.constant(0, dtype=dtype)
else:
testval = tt.zeros(self.shape)

self.ndim = tt.get_vector_length(self.shape)

self.testval = testval
self.defaults = defaults
self.transform = transform

if testval is None:
testval = self.get_test_value(defaults=self.defaults)

self.testval = testval
self.type = tt.TensorType(str(dtype), bcast)

def default(self):
return self.get_test_val(self.testval, self.defaults)
return self.get_test_value(self.testval, self.defaults)

def get_test_val(self, val, defaults):
def get_test_value(self, val=None, defaults=None):
if val is None:
for v in defaults:
the_attr = getattr(self, v, None)
Expand All @@ -216,9 +187,14 @@ def get_test_val(self, val, defaults):
str(defaults) + " pass testval argument or adjust so value is finite.")

def getattr_value(self, val):
""" Attempts to obtain a non-symbolic value for an attribute
(potentially given in str form)
"""
if isinstance(val, string_types):
val = getattr(self, val)

# Could use Theano's:
# val = theano.gof.op.get_test_value(val)
if isinstance(val, tt.sharedvar.SharedVariable):
return val.get_value()
elif isinstance(val, tt.TensorVariable):
Expand Down Expand Up @@ -290,7 +266,7 @@ def __init__(self, logp, ndim_support, ndim, size, bcast, dtype='float64',
self.logp = logp


class UnivariateContinuous(Continuous):
class Univariate(Distribution):

def __init__(self, dist_params, ndim=None, size=None, dtype=None,
bcast=None, *args, **kwargs):
Expand All @@ -312,30 +288,15 @@ def __init__(self, dist_params, ndim=None, size=None, dtype=None,
dtype = tt.scal.upcast(*(tt.config.floatX,) + tuple(x.dtype for x in dist_params))

# We just assume
super(UnivariateContinuous, self).__init__(
super(Univariate, self).__init__(
tuple(), tuple(), size, bcast, *args, **kwargs)


class MultivariateContinuous(Continuous):
class Multivariate(Distribution):

pass



class MultivariateDiscrete(Discrete):

pass


class UnivariateDiscrete(Discrete):

def __init__(self, ndim, size, bcast, *args, **kwargs):
self.shape_supp = ()

super(UnivariateDiscrete, self).__init__(
0, ndim, size, bcast, *args, **kwargs)


def draw_values(params, point=None):
"""
Draw (fix) parameter values. Handles a number of cases:
Expand Down
Loading

0 comments on commit 9563d8a

Please sign in to comment.