def update_G_both(G_both, add_both, e_both, α, rng): df = df_0 + α * n rate = rate_0 + α * U K0 = np.empty((p, p)) K1 = np.empty((p, p)) try: accept_both = update_G_both_cpp(K0, K1, G_both[0].__graph_as_capsule(), G_both[1].__graph_as_capsule(), add_both[0], add_both[1], e_both[0], e_both[1], edge_prob, df, df_0, rate, cpmcmc.random_seed(rng)) except: print("Error in `update_G_both_cpp`. Retrying...") return update_G_both(G_both, add_both, e_both, α, rng) K_both = (K0, K1) res = np.empty(2, dtype=object) for j in range(2): if accept_both[j]: G_tilde = G_both[j].copy() G_tilde[e_both[j]] = True if add_both[j] else False res[j] = [G_tilde, K_both[j]] else: res[j] = [G_both[j], K_both[j]] return res
def update_G(G, add, e, α, rng): """ MCMC step for the given graph `G` `G_tilde` is the proposed graph. This follows the exchange algorithm from Lenkoski (2013, arXiv:1304.1350v1). The proposal distribution is assumed to be adding/removing an edge with equal probability, if `G` is not empty nor full. Then, the edge `e` is selected uniformly at random. """ df = df_0 + α * n rate = rate_0 + α * U K = np.empty((p, p)) try: accept = update_G_cpp(K, G.__graph_as_capsule(), add, e, edge_prob, df, df_0, rate, cpmcmc.random_seed(rng)) except: print("Error in `update_G_cpp`. Retrying...") return update_G(G, add, e, α, rng) if accept: G_tilde = G.copy() G_tilde[e] = True if add else False return [G_tilde, K] else: return [G, K]
def sample_e_both(G_both, rng): """Coupled sampling of edges""" res = sample_e_both_cpp(G_both[0].__graph_as_capsule(), G_both[1].__graph_as_capsule(), cpmcmc.random_seed(rng)) return [tuple(res[i]) for i in range(2)]
def sample_from_prior(rng): """ Draw z = (G, K) from its prior. This sets the random number generator from iGraph. """ igraph.set_random_number_generator(random.Random(cpmcmc.random_seed(rng))) if edge_prob <= 0.0: m_max_p1 = p * (p - 1) // 2 + 1 if edge_prob == 0.0: # Use the size-based prior. m = rng.integers(m_max_p1) else: # Use the size-based prior with a truncated geometric distribution # on the graph size. prob = -edge_prob m = int( np.floor( np.log(1.0 - rng.random() * (1.0 - (1.0 - prob)**m_max_p1)) / np.log(1.0 - prob))) G = igraph.Graph.Erdos_Renyi(n=p, m=m) else: G = igraph.Graph.Erdos_Renyi(n=p, p=edge_prob) return [G, rgwish_identity(G, df_0, rng)]
def coupled_sample_eta(eta_both, m_both, rng): """ Coupled slice sampling for eta per Algorithm 4 of arXiv:2012.04798v1 with 𝜈 = 1 """ coupled_sample_eta_cpp(eta_both[0], eta_both[1], m_both[0], m_both[1], p, cpmcmc.random_seed(rng)) # # Step 1 # U_crn = rng.random(p) # U_both = np.empty(2, dtype=object) # for j in range(2): # U_both[j] = U_crn / (1.0 + eta_both[j]) # # Step 2 # for j in range(p): # eta_both[0][j], eta_both[1][j] = maximal_coupling( # P=lambda rng: sample_P(m_both[0][j], U_both[0][j], rng), # Q=lambda rng: sample_P(m_both[1][j], U_both[1][j], rng), # log_p=lambda eta: log_dens_P(eta, m_both[0][j], U_both[0][j]), # log_q=lambda eta: log_dens_P(eta, m_both[1][j], U_both[1][j]), # rng=rng # ) return eta_both
def coupled_MCMC_update(z_i, rng, α=1.0): # Common seed MCMC update tmp_seed = cpmcmc.random_seed(rng) for j in range(2): z_i[:, j] = MCMC_update(x_i=z_i[:, j], rng=np.random.default_rng(tmp_seed), α=α) return z_i
def rgwish_identity(G, df, rng): """Sample from the G-Wishart distribution with an identity scale matrix.""" K = np.empty(2 * (G.vcount(), )) try: rgwish_L_identity_cpp(K, G.__graph_as_capsule(), df, cpmcmc.random_seed(rng)) except: print("Error in `rgwish_L_identity_cpp`. Retrying...") return rgwish_identity(G, df, rng) return K
def rejection_sampling(N, α, rng): K_out = np.empty((N, p, p)) adj_out = np.empty((N, p, p)) try: rejection_sampling_cpp(K_out, adj_out, p, n, N, α, edge_prob, df_0, U, cpmcmc.random_seed(rng)) except: print("Error in `rejection_sampling`. Retrying...") return rejection_sampling(N, α, rng) res = np.empty(N, dtype=object) for i in range(N): res[i] = [ igraph.Graph.Adjacency(adj_out[i, :, :].tolist(), mode=1), K_out[i, :, :] ] return res
U = data.T @ data # The code uses the size-based prior if edge_prob <= 0.0. Negative `edge_prob` # specifies a truncated geometric prior with success probability `-edge_prob` # on the number of edges. `edge_prob = 0` specifies a uniform prior on the # number of edges. If `edge_prob > 0.0`, then the edges are a priori # independent with prior edge inclusion probability `edge_prob`. edge_prob = 0.5 df_0 = 3.0 # Degrees of freedom of the Wishart prior rate_0 = np.eye(p) # Rate matrix of the Wishart prior rng = np.random.Generator(np.random.SFC64(seed=0)) N = 10**2 # Number of SMC particles k = 7 min_iter = 40 # Denoted by l in the paper max_iter = 10**4 tmp_seed = cpmcmc.random_seed(rng) n_hours = 0.02 # Time budget post adaptation in hours def trace_inner(A, B): """ Trace inner product of the matrices `A` and `B` This function computes <A, B> = tr(A' B). Taken from https://stackoverflow.com/a/18855277/5216563. `A` can be a higher dimensional array. Then, this function is broadcast over the first dimensions of `A`. """ return c_einsum('...ij,ji->...', A, B)