def collapsed_sample_f(self, theta,n,A,f): """ Use a collapsed Gibbs sampler to update the block assignments by integrating out the block-to-block connection probabilities B. Since this is a Beta-Bernoulli model the posterior can be computed in closed form and the integral can be computed analytically. """ (B,pi) = theta A = A.astype(np.bool) zother = np.array(f).astype(np.int) # P(A|z) \propto # \prod_{r1}\prod_{r2} Beta(m(r1,r2)+b1,\hat{m}(r1,r2)+b0) / # Beta(b1,b0) # # Switching z changes the product over r1 and the product over r2 # Compute the posterior distribution over blocks # TODO: This literal translation of the log prob is O(R^3) # But it can almost certainly be sped up to O(R^2) ln_pi_post = np.log(pi) for r in np.arange(self.R): zother[n] = r for r1 in np.arange(self.R): for r2 in np.arange(self.R): # Look at outgoing edges under z[n] = r Ar1r2 = A[np.ix_(zother==r1,zother==r2)] mr1r2 = np.sum(Ar1r2) hat_mr1r2 = Ar1r2.size - mr1r2 ln_pi_post[r] += betaln(mr1r2+self.b1, hat_mr1r2+self.b0) - \ betaln(self.b1,self.b0) zn = log_sum_exp_sample(ln_pi_post) return zn
def naive_sample_f(self, theta, n, A, f, beta=1.0): """ Naively Gibbs sample z given B and pi """ (B,pi) = theta A = A.astype(np.bool) zother = np.array(f).astype(np.int) # Compute the posterior distribution over blocks ln_pi_post = np.log(pi) rrange = np.arange(self.R) for r in rrange: zother[n] = r # Block IDs of nodes we connect to o1 = A[n,:] if np.any(A[n,:]): ln_pi_post[r] += beta * np.sum(np.log(B[np.ix_([r],zother[o1])])) # Block IDs of nodes we don't connect to o2 = np.logical_not(A[n,:]) if np.any(o2): ln_pi_post[r] += beta * np.sum(np.log(1-B[np.ix_([r],zother[o2])])) # Block IDs of nodes that connect to us i1 = A[:,n] if np.any(i1): ln_pi_post[r] += beta * np.sum(np.log(B[np.ix_(zother[i1],[r])])) # Block IDs of nodes that do not connect to us i2 = np.logical_not(A[:,n]) if np.any(i2): ln_pi_post[r] += beta * np.sum(np.log(1-B[np.ix_(zother[i2],[r])])) zn = log_sum_exp_sample(ln_pi_post) return zn