def test_primitive_conversion(): for _ in supported_backends(): assert pg.obj2id("str") == str(hash("str")) assert pg.sum(pg.to_array([1, 2, 3])) == 6 assert pg.sum(pg.dot(pg.exp(pg.log(pg.to_array([4, 5]))), pg.to_array([2, 2]))) == 18 primitive = pg.to_array([1, 2, 3]) assert id(primitive) == id(pg.to_array(primitive, copy_array=False)) assert id(primitive) != id(pg.to_array(primitive, copy_array=True))
def test_sequential(): graph = next(pg.load_datasets_graph(["graph5"])) for _ in supported_backends(): prior = pg.to_signal(graph, {"A": 2}) posterior1 = pg.Normalize(pg.PageRank(), "range").rank(prior) posterior2 = pg.Normalize("range")(pg.PageRank()(prior)) posterior3 = pg.Sequential(pg.PageRank(), pg.Normalize("range")).rank(prior) assert pg.sum(pg.abs(posterior1 - posterior2)) < pg.epsilon( ) # TODO: investigate when not exactly zero assert pg.sum(pg.abs(posterior1 - posterior3)) < pg.epsilon( ) # TODO: investigate when not exactly zero
def test_normalize(): import networkx as nx graph = next(pg.load_datasets_graph(["graph5"])) for _ in supported_backends(): assert float( pg.sum( pg.Normalize("range").transform( pg.to_signal(nx.Graph([("A", "B")]), [2, 2])).np)) == 4 r = pg.Normalize(pg.PageRank(), "range").rank(graph) assert pg.min(r.np) == 0 assert pg.max(r.np) == 1 r = pg.Normalize(pg.PageRank(), "sum").rank(graph) assert abs(pg.sum(r.np) - 1) < pg.epsilon() with pytest.raises(Exception): pg.Normalize(pg.PageRank(), "unknown").rank(graph)
def _transform(self, ranks: pg.GraphSignal, **kwargs): if ranks.graph not in self.known_ranks or not self.assume_immutability: with pg.Backend("numpy"): A = pg.preprocessor(normalization=self.normalization)( ranks.graph) D = pg.degrees( pg.preprocessor(normalization="none")(ranks.graph)) s = pg.sum( D)**0.5 / 2 if self.sparsity is None else self.sparsity D = (D / pg.max(D))**self.beta S = scipy.sparse.random( self.dims, A.shape[0], density=1. / s, data_rvs=lambda l: np.random.choice([-1, 1], size=l), format="csc") S = S @ scipy.sparse.spdiags(D, 0, *A.shape) self.embeddigns[ranks.graph] = pg.scipy_sparse_to_backend(S.T) self.known_ranks[ranks.graph] = [ ] # we know that the first term is zero and avoid direct embedding comparison for _ in range(len(self.weights)): S = S @ A self.known_ranks[ranks.graph].append( pg.scipy_sparse_to_backend(S)) ret = 0 on = pg.conv(ranks.np, self.embeddigns[ranks.graph]) for weight, S in zip(self.weights, self.known_ranks[ranks.graph]): uv = pg.conv(on, S) ret = ret + weight * uv return pg.to_signal(ranks, ret)
def test_norm_maintain(): # TODO: investigate that 2.5*epsilon is truly something to be expected graph = next(pg.load_datasets_graph(["graph5"])) for _ in supported_backends(): prior = pg.to_signal(graph, {"A": 2}) posterior = pg.MabsMaintain(pg.Normalize(pg.PageRank(), "range")).rank(prior) assert abs(pg.sum(pg.abs(posterior.np)) - 2) < 2.5 * pg.epsilon()
def test_separate_and_combine(): for _ in supported_backends(): table = pg.to_primitive([[1, 2, 3], [4, 5, 6]]) cols = pg.separate_cols(table) assert len(cols) == 3 for col in cols: assert pg.length(col) == 2 new_table = pg.combine_cols(cols) assert pg.sum(pg.abs(table - new_table)) == 0
def test_transform(): import math graph = next(pg.load_datasets_graph(["graph5"])) for _ in supported_backends(): r1 = pg.Normalize(pg.PageRank(), "sum").rank(graph) r2 = pg.Transformer(pg.PageRank(), lambda x: x / pg.sum(x)).rank(graph) assert pg.Mabs(r1)(r2) < pg.epsilon() r1 = pg.Transformer(math.exp).transform(pg.PageRank()(graph)) r2 = pg.Transformer(pg.PageRank(), pg.exp).rank(graph) assert pg.Mabs(r1)(r2) < pg.epsilon()
def test_preprocessor_types(): def test_graph(): return next(pg.load_datasets_graph(["graph5"])) for _ in supported_backends(): from random import random graph = test_graph() signal = pg.to_signal(graph, {v: random() for v in graph}) laplacian = pg.preprocessor(normalization="laplacian")(graph) symmetric = pg.preprocessor(normalization="symmetric")(graph) assert pg.abs(pg.sum(pg.conv(signal, laplacian) + pg.conv(signal, symmetric) - signal)) <= pg.epsilon()
def rank(self, graph: pg.GraphSignalGraph = None, personalization: pg.GraphSignalData = None, **kwargs): personalization = pg.to_signal(graph, personalization) graph = personalization.graph ranks = self.ranker(personalization) ret = 0 total_sum = pg.sum(ranks) accum_sum = 0 for threshold in sorted(ranks.values()): accum_sum += threshold if accum_sum > total_sum * 0.1: break for i, v in enumerate(ranks): pg.utils.log(f"{i}/{len(ranks)}") if ranks[v] >= threshold: partial = ranks >> pg.Threshold(ranks[v], inclusive=True) >> self.ranker ret = partial * ranks[v] + ret return ret
def test_fastgraph(): for graph_api in [pg.fastgraph, nx]: graph = next(pg.load_datasets_graph(["graph5"], graph_api=graph_api)) assert graph.has_edge("A", "B") assert not graph.has_edge("A", "E") graph.add_edge("A", "E") assert graph.has_edge("A", "E") assert graph.has_edge( "E", "A") # checks that undirected is the default mode prev_count = graph.number_of_edges() graph.remove_edge("A", "E") assert graph.number_of_edges() == prev_count - 1 sparse = graph.to_scipy_sparse_array() if isinstance( graph, pg.Graph) else nx.to_scipy_sparse_matrix( graph, weight="weight", dtype=float) assert pg.sum(sparse) == 14 assert not graph.has_edge("A", "E") assert not graph.has_edge("E", "A") graph.add_edge("A", "E") assert graph.has_edge("A", "E") graph.add_edge("Y", "Z") assert graph.has_edge("Y", "Z")
def test_krylov_space(): graph = next(pg.load_datasets_graph(["bigraph"])) nodes = list(graph) for _ in supported_backends(): personalization = pg.to_signal(graph, {nodes[0]: 1, nodes[1]: 1}) M = pg.preprocessor(normalization="symmetric")(graph) krylov_dims = 5 krylov_result = pg.eye(int(krylov_dims)) krylov_base, H = pg.krylov_base(M, personalization.np, int(krylov_dims)) error_bound = pg.krylov_error_bound(krylov_base, H, M, personalization.np) assert pg.sum(pg.krylov2original(0, H, krylov_dims)) == 0 assert error_bound < 0.01 for _ in range(100): krylov_result = krylov_result @ H personalization.np = pg.conv(personalization.np, M) # print(pg.Mabs(personalization.np)(pg.krylov2original(krylov_base, krylov_result, int(krylov_dims)))) assert pg.Mabs(personalization.np)(pg.krylov2original( krylov_base, krylov_result, int(krylov_dims))) <= error_bound assert pg.krylov2original( krylov_base, krylov_result, int(krylov_dims)).shape == personalization.np.shape
def test_zero_personalization(): assert pg.sum(pg.PageRank()(next(pg.load_datasets_graph(["graph9"])), {}).np) == 0
def test_signal_direct_operations(): for _ in supported_backends(): graph = nx.DiGraph([(1, 2), (2, 3)]) signal = pg.to_signal(graph, [1., 2., 3.]) assert pg.sum(signal) == 6 assert pg.sum(signal + 1) == 9 assert pg.sum(1 + signal) == 9 assert pg.sum(signal**2) == 14 assert pg.sum(signal - pg.to_signal(graph, [1, 2, 2])) == 1 assert pg.sum(-1 + signal) == 3 assert pg.sum(signal / pg.to_signal(graph, [1., 2., 3.])) == 3 assert pg.sum(3**signal) == 3 + 9 + 27 signal **= 2 assert pg.sum(signal) == 14 signal.np = pg.to_signal(graph, [4, 4, 4]) assert pg.sum(signal) == 12 assert pg.sum(+signal) == 12 assert pg.sum(-signal) == -12 assert pg.sum(-signal / 2) == -6 #assert pg.sum(-signal//2) == -6 assert pg.sum(2 / signal) == 1.5 #assert pg.sum(2//signal) == 0 signal += 1 assert pg.sum(signal) == 15 signal -= 1 assert pg.sum(signal) == 12 signal /= 2 assert pg.sum(signal) == 6 signal /= 2 # //= 2 assert pg.sum(signal) == 3 signal *= 4 assert pg.sum(signal) == 12 with pytest.raises(Exception): signal + pg.to_signal(graph.copy(), [1., 2., 3.])