123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- """
- Provides functions for finding and testing for locally `(k, l)`-connected
- graphs.
- """
- import copy
- import networkx as nx
- __all__ = ["kl_connected_subgraph", "is_kl_connected"]
- def kl_connected_subgraph(G, k, l, low_memory=False, same_as_graph=False):
- """Returns the maximum locally `(k, l)`-connected subgraph of `G`.
- A graph is locally `(k, l)`-connected if for each edge `(u, v)` in the
- graph there are at least `l` edge-disjoint paths of length at most `k`
- joining `u` to `v`.
- Parameters
- ----------
- G : NetworkX graph
- The graph in which to find a maximum locally `(k, l)`-connected
- subgraph.
- k : integer
- The maximum length of paths to consider. A higher number means a looser
- connectivity requirement.
- l : integer
- The number of edge-disjoint paths. A higher number means a stricter
- connectivity requirement.
- low_memory : bool
- If this is True, this function uses an algorithm that uses slightly
- more time but less memory.
- same_as_graph : bool
- If True then return a tuple of the form `(H, is_same)`,
- where `H` is the maximum locally `(k, l)`-connected subgraph and
- `is_same` is a Boolean representing whether `G` is locally `(k,
- l)`-connected (and hence, whether `H` is simply a copy of the input
- graph `G`).
- Returns
- -------
- NetworkX graph or two-tuple
- If `same_as_graph` is True, then this function returns a
- two-tuple as described above. Otherwise, it returns only the maximum
- locally `(k, l)`-connected subgraph.
- See also
- --------
- is_kl_connected
- References
- ----------
- .. [1] Chung, Fan and Linyuan Lu. "The Small World Phenomenon in Hybrid
- Power Law Graphs." *Complex Networks*. Springer Berlin Heidelberg,
- 2004. 89--104.
- """
- H = copy.deepcopy(G) # subgraph we construct by removing from G
- graphOK = True
- deleted_some = True # hack to start off the while loop
- while deleted_some:
- deleted_some = False
- # We use `for edge in list(H.edges()):` instead of
- # `for edge in H.edges():` because we edit the graph `H` in
- # the loop. Hence using an iterator will result in
- # `RuntimeError: dictionary changed size during iteration`
- for edge in list(H.edges()):
- (u, v) = edge
- # Get copy of graph needed for this search
- if low_memory:
- verts = {u, v}
- for i in range(k):
- for w in verts.copy():
- verts.update(G[w])
- G2 = G.subgraph(verts).copy()
- else:
- G2 = copy.deepcopy(G)
- ###
- path = [u, v]
- cnt = 0
- accept = 0
- while path:
- cnt += 1 # Found a path
- if cnt >= l:
- accept = 1
- break
- # record edges along this graph
- prev = u
- for w in path:
- if prev != w:
- G2.remove_edge(prev, w)
- prev = w
- # path = shortest_path(G2, u, v, k) # ??? should "Cutoff" be k+1?
- try:
- path = nx.shortest_path(G2, u, v) # ??? should "Cutoff" be k+1?
- except nx.NetworkXNoPath:
- path = False
- # No Other Paths
- if accept == 0:
- H.remove_edge(u, v)
- deleted_some = True
- if graphOK:
- graphOK = False
- # We looked through all edges and removed none of them.
- # So, H is the maximal (k,l)-connected subgraph of G
- if same_as_graph:
- return (H, graphOK)
- return H
- def is_kl_connected(G, k, l, low_memory=False):
- """Returns True if and only if `G` is locally `(k, l)`-connected.
- A graph is locally `(k, l)`-connected if for each edge `(u, v)` in the
- graph there are at least `l` edge-disjoint paths of length at most `k`
- joining `u` to `v`.
- Parameters
- ----------
- G : NetworkX graph
- The graph to test for local `(k, l)`-connectedness.
- k : integer
- The maximum length of paths to consider. A higher number means a looser
- connectivity requirement.
- l : integer
- The number of edge-disjoint paths. A higher number means a stricter
- connectivity requirement.
- low_memory : bool
- If this is True, this function uses an algorithm that uses slightly
- more time but less memory.
- Returns
- -------
- bool
- Whether the graph is locally `(k, l)`-connected subgraph.
- See also
- --------
- kl_connected_subgraph
- References
- ----------
- .. [1] Chung, Fan and Linyuan Lu. "The Small World Phenomenon in Hybrid
- Power Law Graphs." *Complex Networks*. Springer Berlin Heidelberg,
- 2004. 89--104.
- """
- graphOK = True
- for edge in G.edges():
- (u, v) = edge
- # Get copy of graph needed for this search
- if low_memory:
- verts = {u, v}
- for i in range(k):
- [verts.update(G.neighbors(w)) for w in verts.copy()]
- G2 = G.subgraph(verts)
- else:
- G2 = copy.deepcopy(G)
- ###
- path = [u, v]
- cnt = 0
- accept = 0
- while path:
- cnt += 1 # Found a path
- if cnt >= l:
- accept = 1
- break
- # record edges along this graph
- prev = u
- for w in path:
- if w != prev:
- G2.remove_edge(prev, w)
- prev = w
- # path = shortest_path(G2, u, v, k) # ??? should "Cutoff" be k+1?
- try:
- path = nx.shortest_path(G2, u, v) # ??? should "Cutoff" be k+1?
- except nx.NetworkXNoPath:
- path = False
- # No Other Paths
- if accept == 0:
- graphOK = False
- break
- # return status
- return graphOK
|