def logp(self, value): value_x = value[0][0] value_y = value[0][1] mu_x = self.mu_x mu_y = self.mu_y log_prob_x = bound( logpow(mu_x, value_x) - factln(value_x) - mu_x, mu_x >= 0, value_x >= 0) log_prob_y = bound( logpow(mu_y, value_y) - factln(value_y) - mu_y, mu_y >= 0, value_y >= 0) out = T.switch(1 * T.eq(mu_x, 0) * T.eq(value_x, 0), 0, log_prob_x) out += T.switch(1 * T.eq(mu_x, 0) * T.eq(value_x, 0), 0, log_prob_y) return out
def logp(self, weights): """ Calculate log-probability of the given set of weights. Parameters ---------- value : 1-D array, having numeric values Set of weights for which log-probability is calculated. Returns ------- TensorVariable """ k = self.shape a = self.a wts = tt.as_tensor_variable(weights) wt = wts[:-1] wt_sum = tt.extra_ops.cumsum(wt) denom = 1 - wt_sum denom_shift = tt.concatenate([[1.], denom]) betas = wts/denom_shift Beta_ = pm.Beta.dist(1, a) logp = Beta_.logp(betas).sum() return bound(Beta_.logp(betas).sum(), tt.all(betas > 0), tt.all(weights) > 0, wt_sum < 1, wt_sum > 0, tt.all(denom) > 0)
def logp(self, value): """ Calculate log-probability of defined Mixture distribution at specified value. Parameters ---------- value: numeric Value(s) for which log-probability is calculated. If the log probabilities for multiple values are desired the values must be provided in a numpy array or Aesara tensor Returns ------- TensorVariable """ w = self.w return bound( logsumexp(at.log(w) + self._comp_logp(value), axis=-1, keepdims=False), w >= 0, w <= 1, at.allclose(w.sum(axis=-1), 1), broadcast_conditions=False, )
def gev_logp(value): scaled = (value - loc) / scale logp = -(tt.log(scale) + (((c - 0.5) + 1) / (c - 0.5) * tt.log1p( (c - 0.5) * scaled) + (1 + (c - 0.5) * scaled)**(-1 / (c - 0.5)))) bound1 = loc - scale / (c - 0.5) bounds = tt.switch((c - 0.5) > 0, value > bound1, value < bound1) return bound(logp, bounds, c != 0)
def logp(self, value): mu = self.mu tau = self.tau offset = self.offset v = value + offset log_pdf = (-0.5 * tau * (tt.log(v) - mu)**2 + 0.5 * tt.log(tau / (2. * np.pi)) - tt.log(v)) return bound(log_pdf, tau > 0, offset > 0)
def logp(self, value): p = self.p k = self.k a = self.loss_func(p, value) p = ttu.normalize(p) sum_to1 = theano.gradient.zero_grad(tt.le(abs(tt.sum(p, axis=-1) - 1), 1e-5)) value_k = tt.argmax(value, axis=1) return bound(a, value_k >= 0, value_k <= (k - 1), sum_to1)
def logp(value, n, p): return bound( factln(n) - factln(value).sum() + (value * at.log(p)).sum(), value >= 0, 0 <= p, p <= 1, at.isclose(p.sum(), 1), broadcast_conditions=False, )
def gev_logp(value, t): loc_ = m1_ * t + m2_ scaled = (value - loc_) / scale_ logp = -(tt.log(scale_) + (((c_ - 0.5) + 1) / (c_ - 0.5) * tt.log1p( (c_ - 0.5) * scaled) + (1 + (c_ - 0.5) * scaled)**(-1 / (c_ - 0.5)))) bound1 = loc_ - scale_ / (c_ - 0.5) bounds = tt.switch((c_ - 0.5) > 0, value > bound1, value < bound1) return bound(logp, bounds, c_ != 0)
def gev_logp(value, t, t2): loc = m1 * t2 + m2 * t + m3 # scale=tt.log(tt.exp(scale1)) scaled = (value - loc) / scale logp = -(tt.log(scale) + (((c - 0.5) + 1) / (c - 0.5) * tt.log1p( (c - 0.5) * scaled) + (1 + (c - 0.5) * scaled)**(-1 / (c - 0.5)))) bound1 = loc - scale / (c - 0.5) bounds = tt.switch((c - 0.5) > 0, value > bound1, value < bound1) return bound(logp, bounds, c != 0)
def logp(self, value): logp = self._wrapped.logp(value) bounds = [] if self.lower is not None: bounds.append(value >= self.lower) if self.upper is not None: bounds.append(value <= self.upper) if len(bounds) > 0: return bound(logp, *bounds) else: return logp
def logp(self, value): logp = self._wrapped.logp(value) bounds = [] if self.lower is not None: bounds.append(value >= self.lower) if self.upper is not None: bounds.append(value <= self.upper) if len(bounds) > 0: return bound(logp, *bounds) else: return logp
def logp(self, value): n = self.n p = self.p return bound( factln(n) - factln(value).sum() + (value * at.log(p)).sum(), at.all(value >= 0), at.all(0 <= p), at.all(p <= 1), at.isclose(p.sum(), 1), broadcast_conditions=False, )
def logp(self, x): n = self.n p = self.p return bound(tt.sum(factln(n)) - tt.sum(factln(x)) + tt.sum(x * tt.log(p)), tt.all(x >= 0), tt.all(tt.eq(tt.sum(x, axis=-1, keepdims=True), n)), tt.all(p <= 1), tt.all(tt.eq(tt.sum(p, axis=-1), 1)), tt.all(tt.ge(n, 0)), broadcast_conditions=False)
def logp(self, value): n = self.n p = self.p return bound( factln(n) - factln(value).sum() + (value * aet.log(p)).sum(), value >= 0, 0 <= p, p <= 1, aet.isclose(p.sum(), 1), broadcast_conditions=False, )
def logp(self, Z): alpha = self.alpha beta = self.beta Zanchored = Z[self.mask.nonzero()] #import pdb; pdb.set_trace() logp = bound( gammaln(alpha + beta) - gammaln(alpha) - gammaln(beta) + logpow(Zanchored, alpha - 1) + logpow(1 - Zanchored, beta - 1), 0 <= Zanchored, Zanchored <= 1, alpha > 0, beta > 0) return logp
def logp(self, value): mu = self.mu # mu^k # PDF = ------------ # k! (e^mu - 1) # log(PDF) = log(mu^k) - (log(k!) + log(e^mu - 1)) # # See https://en.wikipedia.org/wiki/Zero-truncated_Poisson_distribution p = logpow(mu, value) - (factln(value) + pm.math.log(pm.math.exp(mu) - 1)) log_prob = bound(p, mu >= 0, value >= 0) # Return zero when mu and value are both zero return tt.switch(1 * tt.eq(mu, 0) * tt.eq(value, 0), 0, log_prob)
def logp(self, value): """ Calculate log-probability of EDSD distribution at specified value. Parameters ---------- value : numeric Value(s) for which log-probability is calculated. If the log probabilities for multiple values are desired the values must be provided in a numpy array or theano tensor Returns ------- TensorVariable """ scale = self.scale log_d = 2.0 * tt.log(value) - tt.log(2.0 * scale**3) - value / scale return bound(log_d, value >= 0, scale > 0)
def logp(self, Z): alpha = self.alpha beta = self.beta Zanchored = Z[self.mask.nonzero()] #import pdb; pdb.set_trace() logp = bound( gammaln(alpha + beta) - gammaln(alpha) - gammaln(beta) + logpow( Zanchored, alpha - 1) + logpow(1 - Zanchored, beta - 1), 0 <= Zanchored, Zanchored <= 1, alpha > 0, beta > 0) return logp
def logp(self, x): log_p = 0. for i in range(0, self.shape[0]): p_it = self.PI[x[i, ][:-1]] x_t = x[i, ][1:] x_0 = tt.stack([x[i, ][0]]) mask = self.renewal_mask log_p_i = pm.Categorical.dist(self.Q).logp(x_0) + tt.sum( pm.Categorical.dist(p_it).logp(x_t)) # Restrction: if not churned, cannot be in state 0 at a renewal period log_p = log_p + bound(log_p_i, tt.dot(mask, x_t) > 0) return log_p
def negative_binomial_gaussian_approx_logp(mu, alpha, value): """Generates symbolic Gaussian approximation to negative binomial logp. Args: mu: negative binomial mean tensor alpha: negative binomial over-dispersion tensor value: negative binomial counts Note: `mu`, `alpha` and `value` must be either shape-compatible or be commensurately broadcastable. Returns: symbolic approximate negative binomial logp """ tau = alpha / (mu * (alpha + mu)) # precision return pm_dist_math.bound(0.5 * (tt.log(tau) - _log_2_pi - tau * tt.square(value - mu)), mu > 0, value >= 0, alpha > 0)
def gev_logp(value): scaled = (value - loc) / scale logp = -(scale + ((c + 1) / c) * tt.log1p(c * scaled) + (1 + c * scaled) ** (-1/c)) alpha = loc - scale / c # If the value is greater than zero, then check to see if # it is greater than alpha. Otherwise, check to see if it # is less than alpha. bounds = tt.switch(value > 0, value > alpha, value < alpha) # The returned array will have entries of -inf if the bound # is not satisfied. This condition says "if c is zero or # value is less than alpha, return -inf and blow up # the log-likelihood. return bound(logp, bounds, c != 0)
def negative_binomial_gaussian_approx_logp(mu, alpha, value): """Generates symbolic Gaussian approximation to negative binomial logp. Args: mu: negative binomial mean tensor alpha: negative binomial over-dispersion tensor value: negative binomial counts Note: `mu`, `alpha` and `value` must be either shape-compatible or be commensurately broadcastable. Returns: symbolic approximate negative binomial logp """ tau = alpha / (mu * (alpha + mu)) # precision return pm_dist_math.bound(0.5 * (tt.log(tau) - _log_2_pi - tau * tt.square(value - mu)), mu > 0, value >= 0, alpha > 0)
def logp(self, value): """ Calculate log-probability of defined ``MixtureSameFamily`` distribution at specified value. Parameters ---------- value : numeric Value(s) for which log-probability is calculated. If the log probabilities for multiple values are desired the values must be provided in a numpy array or Aesara tensor Returns ------- TensorVariable """ comp_dists = self.comp_dists w = self.w mixture_axis = self.mixture_axis event_shape = comp_dists.shape[mixture_axis + 1:] # To be able to broadcast the comp_dists.logp with w and value # We first have to pad the shape of w to the right with ones # so that it can broadcast with the event_shape. w = at.shape_padright(w, len(event_shape)) # Second, we have to add the mixture_axis to the value tensor # To insert the mixture axis at the correct location, we use the # negative number index. This way, we can also handle situations # in which, value is an observed value with more batch dimensions # than the ones present in the comp_dists. comp_dists_ndim = len(comp_dists.shape) value = at.shape_padaxis(value, axis=mixture_axis - comp_dists_ndim) comp_logp = comp_dists.logp(value) return bound( logsumexp(at.log(w) + comp_logp, axis=mixture_axis, keepdims=False), w >= 0, w <= 1, at.allclose(w.sum(axis=mixture_axis - comp_dists_ndim), 1), broadcast_conditions=False, )
def centered_heavy_tail_logp(mu, value): """This distribution is obtained by taking X ~ Exp and performing a Bose transformation Y = (exp(X) - 1)^{-1}. The result is: p(y) = (1 + 2 \mu) y^{2\mu} (1 + y)^{-2(1 + \mu)} It is a heavy-tail distribution with non-existent first moment. Args: mu: exponential parameter of X value: values of Y Returns: symbolic logp """ return pm_dist_math.bound(tt.log(1.0 + 2.0 * mu) + 2.0 * mu * tt.log(value) - 2.0 * (1.0 + mu) * tt.log(1.0 + value), mu >= 0, value > 0)
def centered_heavy_tail_logp(mu, value): """This distribution is obtained by taking X ~ Exp and performing a Bose transformation Y = (exp(X) - 1)^{-1}. The result is: p(y) = (1 + 2 \mu) y^{2\mu} (1 + y)^{-2(1 + \mu)} It is a heavy-tail distribution with non-existent first moment. Args: mu: exponential parameter of X value: values of Y Returns: symbolic logp """ return pm_dist_math.bound(tt.log(1.0 + 2.0 * mu) + 2.0 * mu * tt.log(value) - 2.0 * (1.0 + mu) * tt.log(1.0 + value), mu >= 0, value > 0)
def negative_binomial_logp(mu, alpha, value): """Generates symbolic negative binomial logp. Args: mu: negative binomial mean tensor alpha: negative binomial over-dispersion tensor value: negative binomial counts Note: `mu`, `alpha` and `value` must be either shape-compatible or be commensurately broadcastable. Returns: symbolic negative binomial logp """ return pm_dist_math.bound(pm_dist_math.binomln(value + alpha - 1, value) + pm_dist_math.logpow(mu / (mu + alpha), value) + pm_dist_math.logpow(alpha / (mu + alpha), alpha), mu > 0, value >= 0, alpha > 0)
def negative_binomial_logp(mu, alpha, value): """Generates symbolic negative binomial logp. Args: mu: negative binomial mean tensor alpha: negative binomial over-dispersion tensor value: negative binomial counts Note: `mu`, `alpha` and `value` must be either shape-compatible or be commensurately broadcastable. Returns: symbolic negative binomial logp """ return pm_dist_math.bound(pm_dist_math.binomln(value + alpha - 1, value) + pm_dist_math.logpow(mu / (mu + alpha), value) + pm_dist_math.logpow(alpha / (mu + alpha), alpha), mu > 0, value >= 0, alpha > 0)
def logp(self, value): """ Calculate log-probability of Weibull distribution at specified value. Parameters ---------- value: numeric Value(s) for which log-probability is calculated. If the log probabilities for multiple values are desired the values must be provided in a numpy array or theano tensor Returns ------- TensorVariable """ alpha = self.alpha beta = self.beta value_ = pm.math.maximum(value - self.offset, 1e-313) return bound( tt.log(alpha) - tt.log(beta) + (alpha - 1) * tt.log(value_ / beta) - (value_ / beta)**alpha, value_ >= 0, alpha > 0, beta > 0, )
def logp(self, value): """ Calculate log-probability of Bounded distribution at specified value. Parameters ---------- value: numeric Value for which log-probability is calculated. Returns ------- TensorVariable """ logp = self._wrapped.logp(value) bounds = [] if self.lower is not None: bounds.append(value >= self.lower) if self.upper is not None: bounds.append(value <= self.upper) if len(bounds) > 0: return bound(logp, *bounds) else: return logp
def test_bound(): logp = at.ones((10, 10)) cond = at.ones((10, 10)) assert np.all(bound(logp, cond).eval() == logp.eval()) logp = at.ones((10, 10)) cond = at.zeros((10, 10)) assert np.all(bound(logp, cond).eval() == (-np.inf * logp).eval()) logp = at.ones((10, 10)) cond = True assert np.all(bound(logp, cond).eval() == logp.eval()) logp = at.ones(3) cond = np.array([1, 0, 1]) assert not np.all(bound(logp, cond).eval() == 1) assert np.prod(bound(logp, cond).eval()) == -np.inf logp = at.ones((2, 3)) cond = np.array([[1, 1, 1], [1, 0, 1]]) assert not np.all(bound(logp, cond).eval() == 1) assert np.prod(bound(logp, cond).eval()) == -np.inf
def logp_Poisson(self, value): r""" Calculate log-probability of Poisson distribution at specified value. Switches to Gamma distribution when mu gets large (> 1000) Parameters ---------- value: numeric Value(s) for which log-probability is calculated. If the log probabilities for multiple values are desired the values must be provided in a numpy array or theano tensor Returns ------- TensorVariable """ # Return zero when mu and value are both zero, return Gamma log-probability when mu is > 1000, # otherwise return Poisson log-probability return tt.switch( tt.gt(self.mu_biol, 1e3), pm.Gamma.dist(mu=self.mu_biol, sd=tt.sqrt(self.mu_biol)).logp(value), tt.switch( tt.eq(self.mu_biol, 0) * tt.eq(value, 0), 0, bound( logpow(self.mu_biol, value) - factln(value) - self.mu_biol, self.mu_biol >= 0, value >= 0)))
def logp(self, x): # -1/2 (x-mu) @ Sigma^-1 @ (x-mu)^T - 1/2 log(2pi^k|Sigma|) # Sigma = diag(std) @ Corr @ diag(std) # Sigma^-1 = diag(std^-1) @ Corr^-1 @ diag(std^-1) # Corr is a block matrix of special form # +----------+ # Corr = [[ | 1, b1, b1|, 0, 0, 0,..., 0] # [ |b1, 1, b1|, 0, 0, 0,..., 0] # [ |b1, b1, 1|, 0, 0, 0,..., 0] # +-----------+----------+ # [ 0, 0, 0, | 1, b2, b2|,..., 0] # [ 0, 0, 0, |b2, 1, b2|,..., 0] # [ 0, 0, 0, |b2, b2, 1|,..., 0] # +----------+ # [ ... ] # [ 0, 0, 0, 0, 0, 0 ,..., 1]] # # Corr = [[B1, 0, 0, ..., 0] # [ 0, B2, 0, ..., 0] # [ 0, 0, B3, ..., 0] # [ ... ] # [ 0, 0, 0, ..., Bk]] # # Corr^-1 = [[B1^-1, 0, 0, ..., 0] # [ 0, B2^-1, 0, ..., 0] # [ 0, 0, B3^-1, ..., 0] # [ ... ] # [ 0, 0, 0, ..., Bk^-1]] # # |B| matrix of rank r is easy # https://math.stackexchange.com/a/1732839 # Let D = eye(r) * (1-b) # Then B = D + b * ones((r, r)) # |B| = (1-b) ** r + b * r * (1-b) ** (r-1) # |B| = (1.-b) ** (r-1) * (1. + b * (r - 1)) # log(|B|) = log(1-b)*(r-1) + log1p(b*(r-1)) # # Inverse B^-1 is easy as well # https://math.stackexchange.com/a/1766118 # let # c = 1/b + r*1/(1-b) # (B^-1)ii = 1/(1-b) - 1/(c*(1-b)**2) # (B^-1)ij = - 1/(c*(1-b)**2) # # assuming # z = (x - mu) / std # we have det fix # detfix = -sum(log(std)) # # now we need to compute z @ Corr^-1 @ z^T # note that B can be unique per timestep # so we need z_t @ Corr_t^-1 @ z_t^T in perfect # z_t @ Corr_t^-1 @ z_t^T is a sum of block terms # quad = z_ct @ B_ct^-1 @ z_ct^T = (B^-1)_iict * sum(z_ct**2) + (B^-1)_ijct*sum_{i!=j}(z_ict * z_jct) # # finally all terms are computed explicitly # logp = detfix - 1/2 * ( quad + log(pi*2) * k + log(|B|) ) x = tt.as_tensor_variable(x) clust_ids, clust_pos, clust_counts = \ tt.extra_ops.Unique(return_inverse=True, return_counts=True)(self.clust) clust_order = tt.argsort(clust_pos) mu = self.mu corr = self.corr[..., clust_ids] std = self.std if std.ndim == 0: std = tt.repeat(std, x.shape[-1]) if std.ndim == 1: std = std[None, :] if corr.ndim == 1: corr = corr[None, :] z = (x - mu) / std z = z[..., clust_order] detfix = -tt.log(std).sum(-1) # following the notation above r = clust_counts b = corr # detB = (1.-b) ** (r-1) * (1. + b * (r - 1)) logdetB = tt.log1p(-b) * (r - 1) + tt.log1p(b * (r - 1)) c = 1 / b + r / (1. - b) invBij = -1. / (c * (1. - b)**2) invBii = 1. / (1. - b) + invBij invBij = tt.repeat(invBij, clust_counts, axis=-1) invBii = tt.repeat(invBii, clust_counts, axis=-1) # to compute (Corr^-1)_ijt*sum_{i!=j}(z_it * z_jt) # we use masked cross products mask = tt.arange(x.shape[-1])[None, :] mask = tt.repeat(mask, x.shape[-1], axis=0) mask = tt.maximum(mask, mask.T) block_end_pos = tt.cumsum(r) block_end_pos = tt.repeat(block_end_pos, clust_counts) mask = tt.lt(mask, block_end_pos) mask = tt.and_(mask, mask.T) mask = tt.fill_diagonal(mask.astype('float32'), 0.) # type: tt.TensorVariable invBiizizi_sum = ((z**2) * invBii).sum(-1) invBijzizj_sum = ( (z.dimshuffle(0, 1, 'x') * mask.dimshuffle('x', 0, 1) * z.dimshuffle(0, 'x', 1)) * invBij.dimshuffle(0, 1, 'x')).sum( [-1, -2]) quad = invBiizizi_sum + invBijzizj_sum k = pm.floatX(x.shape[-1]) logp = (detfix - .5 * (quad + pm.floatX(np.log(np.pi * 2)) * k + logdetB.sum(-1))) if self.nonzero: logp = tt.switch(tt.eq(x, 0).any(-1), 0., logp) return bound(logp, tt.gt(corr, -1.), tt.lt(corr, 1.), tt.gt(std, 0.), broadcast_conditions=False)
def test_check_bounds_false(): with pm.Model(check_bounds=False): logp = at.ones(3) cond = np.array([1, 0, 1]) assert np.all(bound(logp, cond).eval() == logp.eval())
def logp(self, data): """ Retrieve thetas """ theta4 = self.theta4 theta5 = self.theta5 theta6 = self.theta6 theta7 = self.theta7 theta8 = self.theta8 #theta9 = self.theta9 #self.theta10 = theta10 k1 = self.k1 """ Parse data """ f_lengths = data[0] g_starts = data[1] rates = data[2] p_lengths = data[3] g_ends = data[4] """ Transition kernel """ eps = 0.01 Q = eps + (1. - 2. * eps) / (1. + tt.exp(-0.1 * theta4 * (g_ends - 20. * theta5))) ll_Q = tt.log(Q) ll_notQ = tt.log(1 - tt.exp(ll_Q)) """ Short pause ll """ """ p = 0.5 ll_bout = p + bound(tt.log(theta6) - theta6 * p_lengths, p_lengths > 0, theta6 > 0) ll_drink = (1. - p) + bound(tt.log(theta9) - theta9 * p_lengths, p_lengths > 0, theta9 > 0) ll_S = ll_drink + tt.log1p(tt.exp(ll_bout - ll_drink)) """ ll_S = bound( tt.log(theta6) - theta6 * p_lengths, p_lengths > 0, theta6 > 0) """ Long pause ll """ ## Full emptying time t_cs = 2. * tt.sqrt(g_ends) / k1 ## ll if time is less than full emptying g_pausing_1 = 0.25 * tt.sqr( k1 * p_lengths) - tt.sqrt(g_ends) * k1 * p_lengths + g_ends phi_L_1 = 1. / (theta7 + theta8 * g_pausing_1) psi_L_1 = 2. * tt.arctan(0.5 * tt.sqrt(theta8 / theta7) * (k1 * p_lengths - 2. * tt.sqrt(g_ends))) psi_L_1 = psi_L_1 - 2. * tt.arctan(0.5 * tt.sqrt(theta8 / theta7) * (-2. * tt.sqrt(g_ends))) psi_L_1 = psi_L_1 / (k1 * tt.sqrt(theta7 * theta8)) ll_L_1 = tt.log(phi_L_1) - psi_L_1 ## ll if time exceeds full emptying phi_L_2 = 1. / theta7 psi_L_2 = 2. * tt.arctan(0.5 * tt.sqrt(theta8 / theta7) * (k1 * t_cs - 2. * tt.sqrt(g_ends))) psi_L_2 = psi_L_2 - 2. * tt.arctan(0.5 * tt.sqrt(theta8 / theta7) * (-2. * tt.sqrt(g_ends))) psi_L_2 = psi_L_2 / (k1 * tt.sqrt(theta7 * theta8)) psi_L_2 = psi_L_2 + (p_lengths - t_cs) / theta7 ll_L_2 = tt.log(phi_L_2) - psi_L_2 ## Switch based on t_c ll_L = tt.switch(tt.lt(p_lengths, t_cs), ll_L_1, ll_L_2) ## Avoid numerical issues in logaddexp ll_short = ll_notQ + ll_S ll_long = ll_Q + ll_L ll_pause = ll_short + tt.log1p(tt.exp(ll_long - ll_short)) return ll_pause
def logp(self, value): logp = self.fn syms = self.syms return logp(value) return bound(logp(value), value >= -np.pi / syms, value < np.pi / syms)
def logp(self, data): """ Retrieve thetas """ theta5 = self.theta5 theta6 = self.theta6 theta7 = self.theta7 theta8 = self.theta8 theta9 = self.theta9 k1 = self.k1 #theta5_print = theano.printing.Print('theta5')(theta5) #theta6_print = theano.printing.Print('theta6')(theta6) #theta7_print = theano.printing.Print('theta7')(theta7) #theta8_print = theano.printing.Print('theta8')(theta8) #theta9_print = theano.printing.Print('theta9')(theta9) """ Parse data """ f_lengths = data[0] g_starts = data[1] rates = data[2] p_lengths = data[3] g_ends = data[4] p_lengths_print = theano.printing.Print('p_lengths')(p_lengths) g_ends_print = theano.printing.Print('g_ends')(g_ends) """ Transition kernel """ eps = 0.01 Q = eps + (1. - 2. * eps) / (1. + tt.exp(-0.1 * theta5 * (g_ends - 20. * theta6))) #Q_print = theano.printing.Print('Q')(Q) ll_Q = tt.log(Q) ll_notQ = tt.log(1 - tt.exp(ll_Q)) """ Short pause ll """ ll_S = bound( tt.log(theta7) - theta7 * p_lengths, p_lengths > 0, theta7 > 0) #ll_S = tt.log(theta7) - theta7*p_lengths # just exponential dist """ Long pause ll """ ## Full emptying time t_cs = 2. * tt.sqrt(g_ends) / k1 #t_cs_print = theano.printing.Print('t_cs')(t_cs) #p_lengths_print = theano.printing.Print('p_lengths')(p_lengths) ## ll if time is less than full emptying g_pausing_1 = 0.25 * tt.sqr( k1 * p_lengths) - tt.sqrt(g_ends) * k1 * p_lengths + g_ends phi_L_1 = 1. / (theta8 + theta9 * g_pausing_1) psi_L_1 = 2. * tt.arctan(0.5 * tt.sqrt(theta9 / theta8) * (k1 * p_lengths - 2. * tt.sqrt(g_ends))) psi_L_1 = psi_L_1 - 2. * tt.arctan(0.5 * tt.sqrt(theta9 / theta8) * (-2. * tt.sqrt(g_ends))) psi_L_1 = psi_L_1 / (k1 * tt.sqrt(theta8 * theta9)) ll_L_1 = tt.log(phi_L_1) - psi_L_1 ## ll if time exceeds full emptying phi_L_2 = 1. / theta8 psi_L_2 = 2. * tt.arctan(0.5 * tt.sqrt(theta9 / theta8) * (k1 * t_cs - 2. * tt.sqrt(g_ends))) psi_L_2 = psi_L_2 - 2. * tt.arctan(0.5 * tt.sqrt(theta9 / theta8) * (-2. * tt.sqrt(g_ends))) psi_L_2 = psi_L_2 / (k1 * tt.sqrt(theta8 * theta9)) psi_L_2 = psi_L_2 + (p_lengths - t_cs) / theta8 ll_L_2 = tt.log(phi_L_2) - psi_L_2 ## Switch based on t_c ll_L = tt.switch(tt.lt(p_lengths, t_cs), ll_L_1, ll_L_2) """ ll_1_print = theano.printing.Print('ll_1_print')(ll_L_1) ll_2_print = theano.printing.Print('ll_2_print')(ll_L_2) ll_L_print = theano.printing.Print('ll_L_print')(ll_L) """ ## Assemble 2 likelihood paths ll_Q_print = theano.printing.Print('ll_Q')(ll_Q) ll_notQ_print = theano.printing.Print('ll_notQ')(ll_notQ) ll_L_print = theano.printing.Print('ll_L')(ll_L) ll_S_print = theano.printing.Print('ll_S')(ll_S) ## Avoid numerical issues in logaddexp #ll_short = ll_notQ_print + ll_S_print #ll_long = ll_Q_print + ll_L_print ll_short = ll_notQ + ll_S ll_long = ll_Q + ll_L """ ll_pause = tt.switch(tt.lt(ll_short, ll_long), ll_short + tt.log1p(tt.exp(ll_long - ll_short)), ll_long + tt.log1p(tt.exp(ll_short - ll_long))) """ #ll_pause = ll_short + tt.log1p(tt.exp(ll_long - ll_short)) ll_pause = ll_long + tt.log1p(tt.exp(ll_short - ll_long)) ll_nans = tt.any(tt.isnan(ll_pause)) ll_nan_print = theano.printing.Print('ll_nans')(ll_nans) ll_pause_print = theano.printing.Print('ll_pause')(ll_pause) #ll = ll_pause # tt.log(tt.exp(ll_notQ + ll_S) + tt.exp(ll_Q + ll_L)) #ll_print = theano.printing.Print('ll')(ll) return ll_pause_print + ll_nan_print