def rooted_product(G, H, root): """ Return the rooted product of graphs G and H rooted at root in H. A new graph is constructed representing the rooted product of the inputted graphs, G and H, with a root in H. A rooted product duplicates H for each nodes in G with the root of H corresponding to the node in G. Nodes are renamed as the direct product of G and H. The result is a subgraph of the cartesian product. Parameters ---------- G,H : graph A NetworkX graph root : node A node in H Returns ------- R : The rooted product of G and H with a specified root in H Notes ----- The nodes of R are the Cartesian Product of the nodes of G and H. The nodes of G and H are not relabeled. """ if root not in H: raise nx.NetworkXError('root must be a vertex in H') R = nx.Graph() R.add_nodes_from(product(G, H)) R.add_edges_from(((e[0], root), (e[1], root)) for e in G.edges()) R.add_edges_from(((g, e[0]), (g, e[1])) for g in G for e in H.edges()) return R
def test_inverse(self): """Tests that the encoding and decoding functions are inverses. """ for T in nx.nonisomorphic_trees(4): T2 = nx.from_prufer_sequence(nx.to_prufer_sequence(T)) assert_nodes_equal(list(T), list(T2)) assert_edges_equal(list(T.edges()), list(T2.edges())) for seq in product(range(4), repeat=2): seq2 = nx.to_prufer_sequence(nx.from_prufer_sequence(seq)) assert_equal(list(seq), seq2)
def root_to_leaf_paths(G): """Yields root-to-leaf paths in a directed acyclic graph. `G` must be a directed acyclic graph. If not, the behavior of this function is undefined. A "root" in this graph is a node of in-degree zero and a "leaf" a node of out-degree zero. When invoked, this function iterates over each path from any root to any leaf. A path is a list of nodes. """ roots = (v for v, d in G.in_degree() if d == 0) leaves = (v for v, d in G.out_degree() if d == 0) all_paths = partial(nx.all_simple_paths, G) # TODO In Python 3, this would be better as `yield from ...`. return chaini(starmap(all_paths, product(roots, leaves)))
def test_all_pairs_lowest_common_ancestor3(self): """Produces the correct results when all pairs given as a generator.""" all_pairs = product(self.DG.nodes(), self.DG.nodes()) ans = all_pairs_lca(self.DG, pairs=all_pairs) self.assert_lca_dicts_same(dict(ans), self.gold)
def _node_product(G, H): for u, v in product(G, H): yield ((u, v), _dict_product(G.nodes[u], H.nodes[v]))
def navigable_small_world_graph(n, p=1, q=1, r=2, dim=2, seed=None): r"""Return a navigable small-world graph. A navigable small-world graph is a directed grid with additional long-range connections that are chosen randomly. [...] we begin with a set of nodes [...] that are identified with the set of lattice points in an $n \times n$ square, $\{(i, j): i \in \{1, 2, \ldots, n\}, j \in \{1, 2, \ldots, n\}\}$, and we define the *lattice distance* between two nodes $(i, j)$ and $(k, l)$ to be the number of "lattice steps" separating them: $d((i, j), (k, l)) = |k - i| + |l - j|$. For a universal constant $p >= 1$, the node $u$ has a directed edge to every other node within lattice distance $p$---these are its *local contacts*. For universal constants $q >= 0$ and $r >= 0$ we also construct directed edges from $u$ to $q$ other nodes (the *long-range contacts*) using independent random trials; the $i$th directed edge from $u$ has endpoint $v$ with probability proportional to $[d(u,v)]^{-r}$. -- [1]_ Parameters ---------- n : int The length of one side of the lattice; the number of nodes in the graph is therefore $n^2$. p : int The diameter of short range connections. Each node is joined with every other node within this lattice distance. q : int The number of long-range connections for each node. r : float Exponent for decaying probability of connections. The probability of connecting to a node at lattice distance $d$ is $1/d^r$. dim : int Dimension of grid seed : integer, random_state, or None (default) Indicator of random number generation state. See :ref:`Randomness<randomness>`. References ---------- .. [1] J. Kleinberg. The small-world phenomenon: An algorithmic perspective. Proc. 32nd ACM Symposium on Theory of Computing, 2000. """ if (p < 1): raise nx.NetworkXException("p must be >= 1") if (q < 0): raise nx.NetworkXException("q must be >= 0") if (r < 0): raise nx.NetworkXException("r must be >= 1") G = nx.DiGraph() nodes = list(product(range(n), repeat=dim)) for p1 in nodes: probs = [0] for p2 in nodes: if p1 == p2: continue d = sum((abs(b - a) for a, b in zip(p1, p2))) if d <= p: G.add_edge(p1, p2) probs.append(d**-r) cdf = list(nx.utils.accumulate(probs)) for _ in range(q): target = nodes[bisect_left(cdf, seed.uniform(0, cdf[-1]))] G.add_edge(p1, target) return G
def edge_relation(b, c): return any(v in G[u] for u, v in product(b, c))
def _quotient_graph(G, partition, edge_relation=None, node_data=None, edge_data=None, relabel=False, create_using=None): # Each node in the graph must be in exactly one block. if any(sum(1 for b in partition if v in b) != 1 for v in G): raise NetworkXException('each node must be in exactly one block') if create_using is None: H = G.__class__() else: H = nx.empty_graph(0, create_using) # By default set some basic information about the subgraph that each block # represents on the nodes in the quotient graph. if node_data is None: def node_data(b): S = G.subgraph(b) return dict(graph=S, nnodes=len(S), nedges=S.number_of_edges(), density=density(S)) # Each block of the partition becomes a node in the quotient graph. partition = [frozenset(b) for b in partition] H.add_nodes_from((b, node_data(b)) for b in partition) # By default, the edge relation is the relation defined as follows. B is # adjacent to C if a node in B is adjacent to a node in C, according to the # edge set of G. # # This is not a particularly efficient implementation of this relation: # there are O(n^2) pairs to check and each check may require O(log n) time # (to check set membership). This can certainly be parallelized. if edge_relation is None: def edge_relation(b, c): return any(v in G[u] for u, v in product(b, c)) # By default, sum the weights of the edges joining pairs of nodes across # blocks to get the weight of the edge joining those two blocks. if edge_data is None: def edge_data(b, c): edgedata = (d for u, v, d in G.edges(b | c, data=True) if (u in b and v in c) or (u in c and v in b)) return dict(('weight', sum(d.get('weight', 1))) for d in edgedata) block_pairs = permutations(H, 2) if H.is_directed() else combinations(H, 2) # In a multigraph, add one edge in the quotient graph for each edge # in the original graph. if H.is_multigraph(): edges = chaini(((b, c, G.get_edge_data(u, v, default={})) for u, v in product(b, c) if v in G[u]) for b, c in block_pairs if edge_relation(b, c)) # In a simple graph, apply the edge data function to each pair of # blocks to determine the edge data attributes to apply to each edge # in the quotient graph. else: edges = ((b, c, edge_data(b, c)) for (b, c) in block_pairs if edge_relation(b, c)) H.add_edges_from(edges) # If requested by the user, relabel the nodes to be integers, # numbered in increasing order from zero in the same order as the # iteration order of `partition`. if relabel: # Can't use nx.convert_node_labels_to_integers() here since we # want the order of iteration to be the same for backward # compatibility with the nx.blockmodel() function. labels = dict((b, i) for i, b in enumerate(partition)) H = nx.relabel_nodes(H, labels) return H
def modularity(G, communities, weight='weight'): r"""Returns the modularity of the given partition of the graph. Modularity is defined in [1]_ as .. math:: Q = \frac{1}{2m} \sum_{ij} \left( A_{ij} - \frac{k_ik_j}{2m}\right) \delta(c_i,c_j) where $m$ is the number of edges, $A$ is the adjacency matrix of `G`, $k_i$ is the degree of $i$ and $\delta(c_i, c_j)$ is 1 if $i$ and $j$ are in the same community and 0 otherwise. Parameters ---------- G : NetworkX Graph communities : list List of sets of nodes of `G` representing a partition of the nodes. Returns ------- Q : float The modularity of the paritition. Raises ------ NotAPartition If `communities` is not a partition of the nodes of `G`. Examples -------- >>> G = nx.barbell_graph(3, 0) >>> nx.algorithms.community.modularity(G, [{0, 1, 2}, {3, 4, 5}]) 0.35714285714285704 References ---------- .. [1] M. E. J. Newman *Networks: An Introduction*, page 224. Oxford University Press, 2011. """ if not is_partition(G, communities): raise NotAPartition(G, communities) multigraph = G.is_multigraph() directed = G.is_directed() m = G.size(weight=weight) if directed: out_degree = dict(G.out_degree(weight=weight)) in_degree = dict(G.in_degree(weight=weight)) norm = 1 / m else: out_degree = dict(G.degree(weight=weight)) in_degree = out_degree norm = 1 / (2 * m) def val(u, v): try: if multigraph: w = sum(d.get(weight, 1) for k, d in G[u][v].items()) else: w = G[u][v].get(weight, 1) except KeyError: w = 0 # Double count self-loops if the graph is undirected. if u == v and not directed: w *= 2 return w - in_degree[u] * out_degree[v] * norm Q = sum(val(u, v) for c in communities for u, v in product(c, repeat=2)) return Q * norm