File size: 6,180 Bytes
b200bda
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
"""
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"]


@nx._dispatch
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


@nx._dispatch
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