def log_likelihood(self, RVs, observations, llik): """ The output from this function may be a random variable, if not all sources of randomness are observed. llik - a vector to which observation log-likelihoods will be added. """ if len(RVs) != len(observations): raise TypeError("RVs and observations must have same length") for iv in RVs: if not montetheano.rv.is_rv(iv.vals): raise ValueError("non-random var in RVs element", iv) assignment = {} idxs_of = {} for rv, o in zip(RVs, observations): assignment[rv.vals] = o.vals idxs_of[o.vals] = o.idxs # All random variables that are not assigned should stay as the same # object so it can later be replaced # If this is not done this way, they get cloned # raw_RVs = [v for v in ancestors(RVs.flatten()) # if montetheano.rv.is_raw_rv(v)] # Cast assignment elements to the right kind of thing assignment = montetheano.rv.typed_items(assignment) for rv_vals, obs_vals in assignment.items(): lpdf = montetheano.rv.lpdf(rv_vals, obs_vals) llik = tensor.inc_subtensor(llik[idxs_of[obs_vals]], lpdf) # rewire the graph so that the posteriors depend on other # observations instead of each other. involved = [llik] + RVs.flatten() + observations.flatten() inputs = theano.gof.graph.inputs(involved) env = ienv.std_interactive_env(inputs, involved, clone_inputs_and_orphans=False) env.replace_all_sorted( zip(RVs.flatten(), observations.flatten()), reason="IndependentNodeTreeEstimator.log_likelihood" ) # raise an exception if we created cycles env.toposort() rval = env.newest(llik) env.disown() return rval
def posterior(self, priors, observations, s_rng): """ priors - an IdxsValsList of random variables observations - an IdxsValsList of corresponding samples returns - an IdxsValsList of posterior random variables """ assert len(priors) == len(observations) # observation.idxs could be invalid. # They could be invalid, in the sense of describing samples that # could not have been drawn from the prior. # This code does not try to detect that situation. post_vals = [self.s_posterior_helper(p, o, s_rng) for p, o in zip(priors, observations)] # At this point, each post_vals[i] is connected to the original graph # formed of prior nodes. # # We want each post_vals[i] to be connected instead to the other # post_vals just created, corresponding to the posterior values of other # random vars. # # The way to get all the new_s_val nodes hooked up to one another is to # use the clone_keep_replacements function. # XXX: this clones everything. It should be possible to do a more # selective clone of just the pieces that change. inputs = theano.gof.graph.inputs(priors.flatten() + post_vals) env = ienv.std_interactive_env(inputs, priors.flatten() + post_vals, clone_inputs_and_orphans=False) env.prefer_replace(zip(priors.valslist(), post_vals), reason="IndependentNodeTreeEstimator.posterior") # raise an exception if we created cycles env.toposort() # extract the cloned results from the env rval = IdxsValsList.fromlists([env.newest(v) for v in priors.idxslist()], [env.newest(v) for v in post_vals]) # remove all references in the variables to the env. Prepare them # to be inserted into another env if necessary. env.disown() return rval