'Computes the log probability and conditionals for observations.
Args:
x: A batch of observations to compute the log probability of, sized
`[batch_size, num_dims]`.
b_enc: External encoder bias terms (`b` in [1]), sized
`[batch_size, num_hidden]`.
b_dec: External decoder bias terms (`c` in [1]), sized
`[batch_size, num_dims]`.
Returns:
log_prob: The log probabilities of each observation in the batch, sized
`[batch_size, 1]`.
cond_probs: The conditional probabilities at each index for every batch,
sized `[batch_size, num_dims]`.'

def log_prob(self, x, b_enc, b_dec):

    batch_size = tf.shape(x)[0]
    a_0 = b_enc
    log_p_0 = tf.zeros([batch_size, 1])
    cond_p_0 = []
    x_arr = tf.unstack(tf.reshape(tf.transpose(x), [self.num_dims, batch_size, 1]))
    w_enc_arr = tf.unstack(self.w_enc)
    w_dec_arr = tf.unstack(self.w_dec_t)
    b_dec_arr = tf.unstack(tf.reshape(tf.transpose(b_dec), [self.num_dims, batch_size, 1]))
    def loop_body(i, a, log_p, cond_p):
        'Accumulate    hidden    state,    log_p,    and    cond_p    for    index    i.'
        w_enc_i = w_enc_arr[i]
        w_dec_i = w_dec_arr[i]
        b_dec_i = b_dec_arr[i]
        v_i = x_arr[i]
        cond_p_i = self._cond_prob(a, w_dec_i, b_dec_i)
        log_p_i = ((v_i * safe_log(cond_p_i)) + ((1 - v_i) * safe_log((1 - cond_p_i))))
        log_p_new = (log_p + log_p_i)
        cond_p_new = (cond_p + [cond_p_i])
        a_new = (a + tf.matmul(v_i, w_enc_i))
        return (a_new, log_p_new, cond_p_new)
    (a, log_p, cond_p) = (a_0, log_p_0, cond_p_0)
    for i in range(self.num_dims):
        (a, log_p, cond_p) = loop_body(i, a, log_p, cond_p)
    return (tf.squeeze(log_p, squeeze_dims=[1]), tf.transpose(tf.squeeze(tf.stack(cond_p), [2])))
