Using Transform API for vectors

In discussion located here, @chartl mentiones a way to transform any distribution using the Transform API in order to add a location parameter to the Weibull distribution.

His code states the following:

class Translate(pm.Transform):
  def __init__(self, v):
    self.v_ = v

  def forward(self, x):
    return x + self.v_

  def forward_val(self, x):
    return x + self.v_

  def backward(self, z):
    return x - self.v_

  def jacobian_det(self, x):
    return 0. * x  # log(1) = 0

  def apply(self, dist):
    return TransformedDistribution.dist(dist, self)

with pm.Model() as model:
  l = pm.Gamma('l_pr', 3.)
  a = pm.Gamma('a_pr', 3.)
  b = pm.Gamma('b_pr', 3.)
  shift = Translate(l)
  weib = shift.apply(pm.Weibull('base', a, b))

This code does not work out-of the box, but I got it working by changing the Translate class into:

import pymc3 as pm

class Translate(pm.distributions.transforms.Transform):
  def __init__(self, v):
    self.v_ = v

  def forward(self, x):
    return x.T + self.v_

  def forward_val(self, x):
    return x.T + self.v_

  def backward(self, x):
    return x.T - self.v_

  def jacobian_det(self, x):
    return 0. * x.T  # log(1) = 0

  def apply(self, rv):
    return pm.distributions.transforms.TransformedDistribution.dist(rv.distribution, self)

The code above works perfectly, when the shape of the distributions is not given. However, my own code is:

X = theano.shared(x)
if NEW_POSTERIOR:
    with pm.Model() as model:
        # Priors for unknown model parameters
        alpha = pm.TruncatedNormal('alpha', mu=0.5, sd=10, lower=0.01, shape=9)
        beta = pm.TruncatedNormal('beta', mu=40, sd=100, lower=0.01, shape=9)
        loc = pm.Gamma('loc', alpha=3., beta=3.)
        # Likelihood (sampling distribution) of observations
        shift = Translate(loc)
        Y_obs = shift.apply(pm.Weibull('Y_obs', alpha=alpha[X], beta=beta[X], observed=Y))
ppc = pm.sample_posterior_predictive(trace, samples=500, model=model)

However, this code does not run at all. Perhaps due to the shape parameter, but I can not get this working without getting more into Theano.

Traceback (most recent call last):
  File "C:/Users/xxx/source/xx/run_scripts/xxx/xxxx.py", line 48, in <module>
    Y_obs = shift.apply(pm.Weibull('Y_obs', alpha=alpha[X], beta=beta[X], observed=Y))
  File "C:\Users\xxx\source\xxx\stats\Transform.py", line 20, in apply
    return pm.distributions.transforms.TransformedDistribution.dist(rv.distribution, self)
  File "C:\Users\xxx\AppData\Local\Continuum\anaconda3\envs\xxxx\lib\site-packages\pymc3\distributions\distribution.py", line 52, in dist
    dist.__init__(*args, **kwargs)
  File "C:\Users\xxxx\AppData\Local\Continuum\anaconda3\envs\xxx\lib\site-packages\pymc3\distributions\transforms.py", line 125, in __init__
    v = forward(FreeRV(name='v', distribution=dist))
  File "C:\Users\xxx\AppData\Local\Continuum\anaconda3\envs\xxx\lib\site-packages\pymc3\model.py", line 1209, in __init__
    self.logp_elemwiset = distribution.logp(self)
  File "C:\Users\xxx\AppData\Local\Continuum\anaconda3\envs\xxx\lib\site-packages\pymc3\distributions\continuous.py", line 2666, in logp
    - (value / beta)**alpha,
  File "C:\Users\xxx\AppData\Local\Continuum\anaconda3\envs\xxx\lib\site-packages\theano\tensor\var.py", line 197, in __truediv__
    return theano.tensor.basic.true_div(self, other)
  File "C:\Users\xxx\AppData\Local\Continuum\anaconda3\envs\xxx\lib\site-packages\theano\gof\op.py", line 615, in __call__
    node = self.make_node(*inputs, **kwargs)
  File "C:\Users\xxx\AppData\Local\Continuum\anaconda3\envs\xxx\lib\site-packages\theano\tensor\elemwise.py", line 482, in make_node
    DimShuffle, *inputs)
  File "C:\Users\xxx\AppData\Local\Continuum\anaconda3\envs\xxx\lib\site-packages\theano\tensor\elemwise.py", line 438, in get_output_info
    ['x'] * difference + list(range(length)))(input))
  File "C:\Users\xxx\AppData\Local\Continuum\anaconda3\envs\xxx\lib\site-packages\theano\gof\op.py", line 625, in __call__
    storage_map[ins] = [self._get_test_value(ins)]
  File "C:\Users\xxx\AppData\Local\Continuum\anaconda3\envs\xxx\lib\site-packages\theano\gof\op.py", line 562, in _get_test_value
    ret = v.type.filter(v.tag.test_value)
  File "C:\Users\xxx\AppData\Local\Continuum\anaconda3\envs\xxx\lib\site-packages\theano\tensor\type.py", line 178, in filter
    data.shape))
TypeError: For compute_test_value, one input test value does not have the requested type.

The error when converting the test value to that variable type:
Wrong number of dimensions: expected 0, got 1 with shape (10000,).

Specifying the transform as:

import pymc3 as pm

class Translate(pm.distributions.transforms.ElemwiseTransform):
  def __init__(self, loc):
      self.loc = loc

  def forward(self, x):
      return x + self.loc

  def backward(self, x):
      return x - self.loc

  def jacobian_det(self, x):
      return 0. * x  # log(1) = 0

And using the kwarg:

Y_obs = pm.Weibull('Y_obs', alpha=alpha[X], beta=beta[X], observed=Y, transform=Translate(loc))

Fixed the problem I was experiencing.

1 Like