def initialize_AED_sampler(kernel, random_state=None): """ .. seealso:: - :func:`add_delete_sampler <add_delete_sampler>` - :func:`basis_exchange_sampler <basis_exchange_sampler>` - :func:`initialize_AED_sampler <initialize_AED_sampler>` - :func:`add_exchange_delete_sampler <add_exchange_delete_sampler>` """ rng = check_random_state(random_state) N = kernel.shape[0] ground_set = np.arange(N) S0, det_S0 = [], 0.0 nb_trials = 100 tol = 1e-9 for _ in range(nb_trials): if det_S0 > tol: break else: T = rng.choice(2 * N, size=N, replace=False) S0 = np.intersect1d(T, ground_set, assume_unique=True) det_S0 = det_ST(kernel, S0) else: err_str = [ 'Initialization terminated unsuccessfully.', 'After {} random trials, no initial set S0 satisfies det L_S0 > {}.' .format(nb_trials, tol), 'You may consider passing your own initial state **{"s_init": S0}.' ] raise ValueError('\n'.join(err_str)) return S0.tolist()
def initialize_AD_and_E_sampler(kernel, size=None): """ .. seealso:: - :func:`add_delete_sampler <add_delete_sampler>` - :func:`basis_exchange_sampler <basis_exchange_sampler>` - :func:`initialize_AED_sampler <initialize_AED_sampler>` - :func:`add_exchange_delete_sampler <add_exchange_delete_sampler>` """ N = kernel.shape[0] S0, det_S0 = [], 0.0 it_max = 100 tol = 1e-9 for _ in range(it_max): if det_S0 > tol: break else: sz = size if size else np.random.randint(1, N + 1) S0 = np.random.choice(N, size=sz, replace=False).tolist() det_S0 = det_ST(kernel, S0) else: raise ValueError('Initialization problem, you may be using a size `k` > rank of the kernel') return S0
def doubleton_adequation(dpp, samples, tol=0.05): """Perform chi-square test to check that P[{i, j} C X] = det [[K_ii, K_ij], [K_ji, K_jj]] """ samples = list(map(set, samples)) dpp.compute_K() N = len(dpp.K) nb_doubletons = 10 doubletons = [ set(rndm.choice(N, size=2, replace=False)) for _ in range(nb_doubletons) ] # det [[K_ii, K_ij], [K_ji, K_jj]] marginal_th = [det_ST(dpp.K, list(d)) for d in doubletons] counts = [ sum([doubl.issubset(sampl) for sampl in samples]) for doubl in doubletons ] marginal_emp = np.array(counts) / len(samples) _, pval = chisquare(f_obs=marginal_emp, f_exp=marginal_th) msg = 'pval = {}, emp = {}, th = {}'.format(pval, marginal_emp, marginal_th) return pval > tol, msg
def __init__(self, *args, **kwargs): super(UniformityOfSamplerForUniformSpanningTree, self).__init__(*args, **kwargs) # Sample a connected Erdos-Renyi graph n, p = 5, 0.4 nb_st_min, nb_st_max = 5, 10 it_max = 100 for _ in range(it_max): g = erdos_renyi_graph(n, p) if is_connected(g): A = incidence_matrix(g, oriented=True)[:-1, :].toarray() potential_st = itt.combinations(range(g.number_of_edges()), n - 1) list_st = [ st for st in potential_st if det_ST(A, range(n - 1), st) ] if nb_st_min <= len(list_st) <= nb_st_max: break else: raise ValueError('No satisfactory Erdos-Renyi graph found') self.nb_spanning_trees = len(list_st) self.ust = UST(g) self.nb_samples = 1000
def doubleton_adequation(self, dpp, samples, tol=0.05): """Perform chi-square test""" samples = list(map(set, samples)) dpp.compute_K() nb_doubletons_to_check = 10 doubletons = [ set( rndm.choice(self.N, size=2, p=np.diag(dpp.K) / self.rank, replace=False)) for _ in range(nb_doubletons_to_check) ] counts = [ sum([doubl.issubset(sampl) for sampl in samples]) for doubl in doubletons ] freq = np.array(counts) / len(samples) marg_theo = [det_ST(dpp.K, list(d)) for d in doubletons] print(freq) print(marg_theo) _, pval = chisquare(f_obs=freq, f_exp=marg_theo) return pval > tol
def initialize_AED_sampler(kernel): """ .. seealso:: - :func:`add_delete_sampler <add_delete_sampler>` - :func:`basis_exchange_sampler <basis_exchange_sampler>` - :func:`initialize_AED_sampler <initialize_AED_sampler>` - :func:`add_exchange_delete_sampler <add_exchange_delete_sampler>` """ N = kernel.shape[0] ground_set = np.arange(N) S0, det_S0 = [], 0.0 nb_iter = 100 tol = 1e-9 for _ in range(nb_iter): if det_S0 > tol: break else: T = np.random.choice(2 * N, size=N, replace=False) S0 = np.intersect1d(T, ground_set).tolist() det_S0 = det_ST(kernel, S0) else: raise ValueError('Initialization problem, you may be using a size `k` > rank of the kernel') return S0
def test_det_ST(self): """Test determinant - det_ST(arr, S) = det(arr[S, S]) - det_ST(arr, S, T) = det(arr[S, T]) """ shapes = [10, 50, 100, 300] nb_minors = 10 for sh in shapes: arr = rndm.rand(sh, sh) size_minors = sh // 3 for _ in range(nb_minors): S, T = rndm.choice(sh, size=(2, size_minors)) self.assertTrue( np.allclose(utils.det_ST(arr, S), la.det(arr[np.ix_(S, S)]))) self.assertTrue( np.allclose(utils.det_ST(arr, S, T), la.det(arr[np.ix_(S, T)])))
def test_det_ST(self): """Test determinant - det_ST(arr, S) = det(arr[S, S]) - det_ST(arr, S, T) = det(arr[S, T]) """ shapes = [10, 50, 100, 300] nb_minors = 10 for sh in shapes: with self.subTest(axis=sh): arr = rndm.rand(sh, sh) size_minors = sh // 3 for idx in range(nb_minors): with self.subTest(idx=idx): S, T = rndm.choice(sh, size=(2, size_minors), replace=False) for test_det_ST in ['SS', 'ST']: with self.subTest(test_det_ST=test_det_ST): if test_det_ST == 'SS': self.assertTrue( np.allclose(utils.det_ST(arr, S), la.det(arr[np.ix_(S, S)]))) if test_det_ST == 'ST': self.assertTrue( np.allclose(utils.det_ST(arr, S, T), la.det(arr[np.ix_(S, T)])))
def doubleton_adequation(self, tol=0.05): """Perform chi-square test""" samples = list(map(set, self.list_of_samples)) nb_doubletons_to_check = 10 doubletons = [set(choice(self.N, size=2, p=diag(self.K) / self.rank, replace=False)) for _ in range(nb_doubletons_to_check)] counts = [sum([doubl.issubset(sampl) for sampl in samples]) for doubl in doubletons] freq = array(counts) / self.nb_samples marg_theo = [det_ST(self.K, list(d)) for d in doubletons] _, pval = chisquare(f_obs=freq, f_exp=marg_theo) return pval > tol
def initialize_AD_and_E_sampler(kernel, size=None, random_state=None): """ .. seealso:: - :func:`add_delete_sampler <add_delete_sampler>` - :func:`basis_exchange_sampler <basis_exchange_sampler>` - :func:`initialize_AED_sampler <initialize_AED_sampler>` - :func:`add_exchange_delete_sampler <add_exchange_delete_sampler>` """ rng = check_random_state(random_state) N = kernel.shape[0] S0, det_S0 = [], 0.0 nb_trials = 100 tol = 1e-9 for _ in range(nb_trials): if det_S0 > tol: break else: S0 = rng.choice(N, size=size if size else rng.randint(1, N + 1), replace=False) det_S0 = det_ST(kernel, S0) else: err_str = [ 'Initialization terminated unsuccessfully.', 'After {} random trials, no initial set S0 satisfies det L_S0 > {}.' .format(nb_trials, tol), 'If you are sampling from a k-DPP, make sure k <= rank(L).' if size else '', 'You may consider passing your own initial state **{"s_init": S0}.' ] raise ValueError('\n'.join(err_str)) return S0.tolist()
def basis_exchange_sampler(kernel, s_init, nb_iter=10, T_max=None, random_state=None): """ MCMC sampler for projection DPPs, based on the basis exchange property. :param kernel: Feature vector matrix, feature vectors are stacked columnwise. It is assumed to be full row rank. :type kernel: array_like :param s_init: Initial sample. :type s_init: list :param nb_iter: Maximum number of iterations performed by the the algorithm. Default is 10. :type nb_iter: int :param T_max: Maximum running time of the algorithm (in seconds). Default is None. :type T_max: float :param random_state: :type random_state: None, np.random, int, np.random.RandomState :return: MCMC chain of approximate sample (stacked row_wise i.e. nb_iter rows). :rtype: array_like .. seealso:: Algorithm 2 in :cite:`LiJeSr16c` """ rng = check_random_state(random_state) # Initialization N = kernel.shape[0] # Number of elements ground_set = np.arange(N) # Ground set size = len(s_init) # Size of the sample (cardinality is fixed) # Initialization S0, det_S0 = s_init, det_ST(kernel, s_init) chain = np.zeros((nb_iter, size), dtype=int) chain[0] = S0 # Evaluate running time... t_start = time.time() if T_max else 0 for it in range(1, nb_iter): # With proba 1/2 try to swap 2 elements if rng.rand() < 0.5: # Perform the potential exchange move S1 = S0 - s + t S1 = S0.copy() # S1 = S0 # Pick one element s in S0 by index uniformly at random s_ind = rng.choice(size) # Pick one element t in [N]\S0 uniformly at random t = rng.choice(np.delete(ground_set, S0)) S1[s_ind] = t # S_1 = S0 - S0[s_ind] + t det_S1 = det_ST(kernel, S1) # det K_S1 # Accept_reject the move w. proba if rng.rand() < det_S1 / det_S0: S0, det_S0 = S1, det_S1 chain[it] = S1 else: # if reject, stay in the same state chain[it] = S0 else: chain[it] = S0 if T_max: if time.time() - t_start < T_max: break return chain.tolist()
def add_delete_sampler(kernel, s_init, nb_iter=10, T_max=None, random_state=None): """ MCMC sampler for generic DPP(kernel), it performs local moves by removing/adding one element at a time. :param kernel: Kernel martrix :type kernel: array_like :param s_init: Initial sample. :type s_init: list :param nb_iter: Maximum number of iterations performed by the the algorithm. Default is 10. :type nb_iter: int :param T_max: Maximum running time of the algorithm (in seconds). Default is None. :type T_max: float :param random_state: :type random_state: None, np.random, int, np.random.RandomState :return: list of `nb_iter` approximate sample of DPP(kernel) :rtype: array_like .. seealso:: Algorithm 1 in :cite:`LiJeSr16c` """ rng = check_random_state(random_state) # Initialization N = kernel.shape[0] # Number of elements # Initialization S0, det_S0 = s_init, det_ST(kernel, s_init) chain = [S0] # Initialize the collection (list) of sample # Evaluate running time... t_start = time.time() if T_max else 0 for _ in range(1, nb_iter): # With proba 1/2 try to add/delete an element if rng.rand() < 0.5: # Perform the potential add/delete move S1 = S0 +/- s S1 = S0.copy() # S1 = S0 s = rng.choice(N) # Uniform item in [N] if s in S1: S1.remove(s) # S1 = S0 - s else: S1.append(s) # S1 = SO + s # Accept_reject the move det_S1 = det_ST(kernel, S1) # det K_S1 if rng.rand() < det_S1 / det_S0: S0, det_S0 = S1, det_S1 chain.append(S1) else: chain.append(S0) else: chain.append(S0) if T_max: if time.time() - t_start < T_max: break return chain
def add_exchange_delete_sampler(kernel, s_init=None, nb_iter=10, T_max=None, random_state=None): """ MCMC sampler for generic DPPs, it is a mix of add/delete and basis exchange MCMC samplers. :param kernel: Kernel martrix :type kernel: array_like :param s_init: Initial sample. :type s_init: list :param nb_iter: Maximum number of iterations performed by the the algorithm. Default is 10. :type nb_iter: int :param T_max: Maximum running time of the algorithm (in seconds). :type T_max: float :param random_state: :type random_state: None, np.random, int, np.random.RandomState :return: list of `nb_iter` approximate sample of DPP(kernel) :rtype: array_like .. seealso:: Algorithm 3 in :cite:`LiJeSr16c` """ rng = check_random_state(random_state) # Initialization N = kernel.shape[0] ground_set = np.arange(N) S0, det_S0 = s_init, det_ST(kernel, s_init) size_S0 = len(S0) # Size of the current sample chain = [S0] # Initialize the collection (list) of sample # Evaluate running time... t_start = time.time() if T_max else 0 for _ in range(1, nb_iter): S1 = S0.copy() # S1 = S0 # Pick one element s in S_0 by index uniformly at random s_ind = rng.choice(size_S0 if size_S0 else N) # , size=1)[0] # Unif t in [N]-S0 t = rng.choice(np.delete(ground_set, S0)) U = rng.rand() ratio = size_S0 / N # Proportion of items in current sample # Add: S1 = S0 + t if U < 0.5 * (1 - ratio)**2: S1.append(t) # S1 = S0 + t # Accept_reject the move det_S1 = det_ST(kernel, S1) # det K_S1 if rng.rand() < det_S1 / det_S0 * (size_S0 + 1) / (N - size_S0): S0, det_S0 = S1, det_S1 chain.append(S1) size_S0 += 1 else: chain.append(S0) # Exchange: S1 = S0 - s + t elif (0.5 * (1 - ratio)**2 <= U) & (U < 0.5 * (1 - ratio)): del S1[s_ind] # S1 = S0 - s S1.append(t) # S1 = S1 + t = S0 - s + t # Accept_reject the move det_S1 = det_ST(kernel, S1) # det K_S1 if rng.rand() < (det_S1 / det_S0): S0, det_S0 = S1, det_S1 chain.append(S1) # size_S0 stays the same else: chain.append(S0) # Delete: S1 = S0 - s elif (0.5 * (1 - ratio) <= U) & (U < 0.5 * (ratio**2 + (1 - ratio))): del S1[s_ind] # S0 - s # Accept_reject the move det_S1 = det_ST(kernel, S1) # det K_S1 if rng.rand() < det_S1 / det_S0 * size_S0 / (N - (size_S0 - 1)): S0, det_S0 = S1, det_S1 chain.append(S1) size_S0 -= 1 else: chain.append(S0) else: chain.append(S0) if T_max: if time.time() - t_start < T_max: break return chain
def basis_exchange_sampler(kernel, s_init, nb_iter=10, T_max=10): """ MCMC sampler for projection DPPs, based on the basis exchange property. :param kernel: Feature vector matrix, feature vectors are stacked columnwise. It is assumed to be full row rank. :type kernel: array_like :param s_init: Initial sample. :type s_init: list :param nb_iter: Maximum number of iterations performed by the the algorithm. Default is 10. :type nb_iter: int :param T_max: Maximum running time of the algorithm (in seconds). Default is 10s. :type T_max: float :return: MCMC chain of approximate sample (stacked row_wise i.e. nb_iter rows). :rtype: array_like .. seealso:: Algorithm 2 in :cite:`LiJeSr16c` """ # Initialization N = kernel.shape[0] # Number of elements ground_set = np.arange(N) # Ground set size = len(s_init) # Size of the sample (cardinality is fixed) # Initialization S0, det_S0 = s_init, det_ST(kernel, s_init) samples = [S0] # Initialize the collection (list) of sample # Evaluate running time... flag = True it = 1 t_start = time.time() if T_max else 0 while flag: # With proba 1/2 try to swap 2 elements if np.random.rand() < 0.5: # Perform the potential exchange move S1 = S0 - s + t S1 = S0.copy() # S1 = S0 # Pick one element s in S0 by index uniformly at random s_ind = np.random.choice(size, size=1)[0] # Pick one element t in [N]\S0 uniformly at random t = np.random.choice(np.delete(ground_set, S0), size=1)[0] S1[s_ind] = t # S_1 = S0 - S0[s_ind] + t det_S1 = det_ST(kernel, S1) # det K_S1 # Accept_reject the move w. proba if np.random.rand() < det_S1 / det_S0: S0, det_S0 = S1, det_S1 samples.append(S1) else: # if reject, stay in the same state samples.append(S0) else: samples.append(S0) it += 1 flag = (it < nb_iter) if not T_max else ((time.time()-t_start) < T_max) return samples
def add_delete_sampler(kernel, s_init, nb_iter=10, T_max=10): """ MCMC sampler for generic DPP(kernel), it performs local moves by removing/adding one element at a time. :param kernel: Kernel martrix :type kernel: array_like :param s_init: Initial sample. :type s_init: list :param nb_iter: Maximum number of iterations performed by the the algorithm. Default is 10. :type nb_iter: int :param T_max: Maximum running time of the algorithm (in seconds). Default is 10s. :type T_max: float :return: list of `nb_iter` approximate sample of DPP(kernel) :rtype: array_like .. seealso:: Algorithm 1 in :cite:`LiJeSr16c` """ # Initialization N = kernel.shape[0] # Number of elements # Initialization S0, det_S0 = s_init, det_ST(kernel, s_init) samples = [S0] # Initialize the collection (list) of sample # Evaluate running time... flag = True it = 1 t_start = time.time() if T_max else 0 while flag: # With proba 1/2 try to add/delete an element if np.random.rand() < 0.5: # Perform the potential add/delete move S1 = S0 +/- s S1 = S0.copy() # S1 = S0 s = np.random.choice(N, size=1)[0] # Uniform item in [N] if s in S1: S1.remove(s) # S1 = S0 - s else: S1.extend([s]) # S1 = SO + s # Accept_reject the move det_S1 = det_ST(kernel, S1) # det K_S1 if np.random.rand() < det_S1 / det_S0: S0, det_S0 = S1, det_S1 samples.append(S1) else: samples.append(S0) else: samples.append(S0) it += 1 flag = (it < nb_iter) if not T_max else ((time.time()-t_start) < T_max) return samples
def add_exchange_delete_sampler(kernel, s_init=None, nb_iter=10, T_max=None): """ MCMC sampler for generic DPPs, it is a mix of add/delete and basis exchange MCMC samplers. :param kernel: Kernel martrix :type kernel: array_like :param s_init: Initial sample. :type s_init: list :param nb_iter: Maximum number of iterations performed by the the algorithm. Default is 10. :type nb_iter: int :param T_max: Maximum running time of the algorithm (in seconds). :type T_max: float :return: list of `nb_iter` approximate sample of DPP(kernel) :rtype: array_like .. seealso:: Algorithm 3 in :cite:`LiJeSr16c` """ N = kernel.shape[0] ground_set = np.arange(N) # Initialization S0, det_S0 = s_init, det_ST(kernel, s_init) sampl_size = len(S0) # Size of the current sample samples = [S0] # Initialize the collection (list) of sample # Evaluate running time... flag = True it = 1 t_start = time.time() if T_max else 0 while flag: S1 = S0.copy() # S1 = S0 # Pick one element s in S_0 by index uniformly at random s_ind = np.random.choice(sampl_size, size=1)[0] # Unif t in [N]-S0 t = np.random.choice(np.delete(ground_set, S0), size=1)[0] unif_01 = np.random.rand() ratio = sampl_size / N # Proportion of items in current sample # Add: S1 = S0 + t if unif_01 < 0.5 * (1 - ratio)**2: S1.extend([t]) # S1 = S0 + t # Accept_reject the move det_S1 = det_ST(kernel, S1) # det K_S1 if np.random.rand() < det_S1/det_S0*(sampl_size+1)/(N-sampl_size): S0, det_S0 = S1, det_S1 samples.append(S1) sampl_size += 1 else: samples.append(S0) # Exchange: S1 = S0 - s + t elif (0.5 * (1 - ratio)**2 <= unif_01) & (unif_01 < 0.5 * (1 - ratio)): del S1[s_ind] # S1 = S0 - s S1.extend([t]) # S1 = S1 + t = S0 - s + t # Accept_reject the move det_S1 = det_ST(kernel, S1) # det K_S1 if np.random.rand() < (det_S1 / det_S0): S0, det_S0 = S1, det_S1 samples.append(S1) # sampl_size stays the same else: samples.append(S0) # Delete: S1 = S0 - s elif (0.5*(1-ratio) <= unif_01) & (unif_01 < 0.5*(ratio**2+(1-ratio))): del S1[s_ind] # S0 - s # Accept_reject the move det_S1 = det_ST(kernel, S1) # det K_S1 if np.random.rand() < det_S1/det_S0*sampl_size/(N-(sampl_size-1)): S0, det_S0 = S1, det_S1 samples.append(S1) sampl_size -= 1 else: samples.append(S0) else: samples.append(S0) it += 1 flag = (it < nb_iter) if not T_max else ((time.time()-t_start) < T_max) return samples