This file is indexed.

/usr/lib/python2.7/dist-packages/networkx/algorithms/connectivity/connectivity.py is in python-networkx 1.8.1-0ubuntu3.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
# -*- coding: utf-8 -*-
"""
Flow based connectivity algorithms
"""
import itertools
import networkx as nx

__author__ = '\n'.join(['Jordi Torrents <jtorrents@milnou.net>'])

__all__ = [ 'average_node_connectivity',
            'local_node_connectivity',
            'node_connectivity',
            'local_edge_connectivity',
            'edge_connectivity',
            'all_pairs_node_connectivity_matrix',
            'dominating_set',
            ]

def average_node_connectivity(G):
    r"""Returns the average connectivity of a graph G.

    The average connectivity `\bar{\kappa}` of a graph G is the average 
    of local node connectivity over all pairs of nodes of G [1]_ .

    .. math::

        \bar{\kappa}(G) = \frac{\sum_{u,v} \kappa_{G}(u,v)}{{n \choose 2}}

    Parameters
    ----------

    G : NetworkX graph
        Undirected graph

    Returns
    -------
    K : float
        Average node connectivity

    See also
    --------
    local_node_connectivity
    node_connectivity
    local_edge_connectivity
    edge_connectivity
    max_flow
    ford_fulkerson 

    References
    ----------
    .. [1]  Beineke, L., O. Oellermann, and R. Pippert (2002). The average 
            connectivity of a graph. Discrete mathematics 252(1-3), 31-45.
            http://www.sciencedirect.com/science/article/pii/S0012365X01001807

    """
    if G.is_directed():
        iter_func = itertools.permutations
    else:
        iter_func = itertools.combinations

    H, mapping = _aux_digraph_node_connectivity(G)
    num = 0.
    den = 0.
    for u,v in iter_func(G, 2):
        den += 1
        num += local_node_connectivity(G, u, v, aux_digraph=H, mapping=mapping)
    
    if den == 0: # Null Graph
        return 0
    return num/den

def _aux_digraph_node_connectivity(G):
    r""" Creates a directed graph D from an undirected graph G to compute flow
    based node connectivity.

    For an undirected graph G having `n` nodes and `m` edges we derive a 
    directed graph D with 2n nodes and 2m+n arcs by replacing each 
    original node `v` with two nodes `vA`,`vB` linked by an (internal) 
    arc in D. Then for each edge (u,v) in G we add two arcs (uB,vA) 
    and (vB,uA) in D. Finally we set the attribute capacity = 1 for each 
    arc in D [1].

    For a directed graph having `n` nodes and `m` arcs we derive a 
    directed graph D with 2n nodes and m+n arcs by replacing each 
    original node `v` with two nodes `vA`,`vB` linked by an (internal) 
    arc `(vA,vB)` in D. Then for each arc (u,v) in G we add one arc (uB,vA) 
    in D. Finally we set the attribute capacity = 1 for each arc in D.

    References
    ----------
    .. [1] Kammer, Frank and Hanjo Taubig. Graph Connectivity. in Brandes and 
        Erlebach, 'Network Analysis: Methodological Foundations', Lecture 
        Notes in Computer Science, Volume 3418, Springer-Verlag, 2005. 
        http://www.informatik.uni-augsburg.de/thi/personen/kammer/Graph_Connectivity.pdf
 
    """
    directed = G.is_directed()

    mapping = {}
    D = nx.DiGraph()
    for i,node in enumerate(G):
        mapping[node] = i
        D.add_node('%dA' % i,id=node)
        D.add_node('%dB' % i,id=node)
        D.add_edge('%dA' % i, '%dB' % i, capacity=1)
    
    edges = []
    for (source, target) in G.edges():
        edges.append(('%sB' % mapping[source], '%sA' % mapping[target]))
        if not directed:
            edges.append(('%sB' % mapping[target], '%sA' % mapping[source]))
    
    D.add_edges_from(edges, capacity=1)
    return D, mapping

def local_node_connectivity(G, s, t, aux_digraph=None, mapping=None):
    r"""Computes local node connectivity for nodes s and t.

    Local node connectivity for two non adjacent nodes s and t is the
    minimum number of nodes that must be removed (along with their incident 
    edges) to disconnect them.

    This is a flow based implementation of node connectivity. We compute the
    maximum flow on an auxiliary digraph build from the original input
    graph (see below for details). This is equal to the local node 
    connectivity because the value of a maximum s-t-flow is equal to the 
    capacity of a minimum s-t-cut (Ford and Fulkerson theorem) [1]_ .

    Parameters
    ----------
    G : NetworkX graph
        Undirected graph

    s : node
        Source node

    t : node
        Target node

    aux_digraph : NetworkX DiGraph (default=None)
        Auxiliary digraph to compute flow based node connectivity. If None
        the auxiliary digraph is build.

    mapping : dict (default=None)
        Dictionary with a mapping of node names in G and in the auxiliary digraph.

    Returns
    -------
    K : integer
        local node connectivity for nodes s and t

    Examples
    --------
    >>> # Platonic icosahedral graph has node connectivity 5 
    >>> # for each non adjacent node pair
    >>> G = nx.icosahedral_graph()
    >>> nx.local_node_connectivity(G,0,6)
    5

    Notes
    -----
    This is a flow based implementation of node connectivity. We compute the
    maximum flow using the Ford and Fulkerson algorithm on an auxiliary digraph 
    build from the original input graph:

    For an undirected graph G having `n` nodes and `m` edges we derive a 
    directed graph D with 2n nodes and 2m+n arcs by replacing each 
    original node `v` with two nodes `v_A`, `v_B` linked by an (internal) 
    arc in `D`. Then for each edge (`u`, `v`) in G we add two arcs 
    (`u_B`, `v_A`) and (`v_B`, `u_A`) in `D`. Finally we set the attribute 
    capacity = 1 for each arc in `D` [1]_ .

    For a directed graph G having `n` nodes and `m` arcs we derive a 
    directed graph `D` with `2n` nodes and `m+n` arcs by replacing each 
    original node `v` with two nodes `v_A`, `v_B` linked by an (internal) 
    arc `(v_A, v_B)` in D. Then for each arc `(u,v)` in G we add one arc 
    `(u_B,v_A)` in `D`. Finally we set the attribute capacity = 1 for 
    each arc in `D`.

    This is equal to the local node connectivity because the value of 
    a maximum s-t-flow is equal to the capacity of a minimum s-t-cut (Ford 
    and Fulkerson theorem).

    See also
    --------
    node_connectivity
    all_pairs_node_connectivity_matrix
    local_edge_connectivity
    edge_connectivity
    max_flow
    ford_fulkerson 

    References
    ----------
    .. [1] Kammer, Frank and Hanjo Taubig. Graph Connectivity. in Brandes and 
        Erlebach, 'Network Analysis: Methodological Foundations', Lecture 
        Notes in Computer Science, Volume 3418, Springer-Verlag, 2005. 
        http://www.informatik.uni-augsburg.de/thi/personen/kammer/Graph_Connectivity.pdf
    
    """ 
    if aux_digraph is None or mapping is None:
        H, mapping = _aux_digraph_node_connectivity(G)
    else:
        H = aux_digraph 
    return nx.max_flow(H,'%sB' % mapping[s], '%sA' % mapping[t])

def node_connectivity(G, s=None, t=None):
    r"""Returns node connectivity for a graph or digraph G.

    Node connectivity is equal to the minimum number of nodes that 
    must be removed to disconnect G or render it trivial. If source 
    and target nodes are provided, this function returns the local node
    connectivity: the minimum number of nodes that must be removed to break
    all paths from source to target in G.

    This is a flow based implementation. The algorithm is based in 
    solving a number of max-flow problems (ie local st-node connectivity, 
    see local_node_connectivity) to determine the capacity of the
    minimum cut on an auxiliary directed network that corresponds to the 
    minimum node cut of G. It handles both directed and undirected graphs.
   
    Parameters
    ----------
    G : NetworkX graph
        Undirected graph

    s : node
        Source node. Optional (default=None)

    t : node
        Target node. Optional (default=None)

    Returns
    -------
    K : integer
        Node connectivity of G, or local node connectivity if source 
        and target were provided

    Examples
    --------
    >>> # Platonic icosahedral graph is 5-node-connected 
    >>> G = nx.icosahedral_graph()
    >>> nx.node_connectivity(G)
    5
    >>> nx.node_connectivity(G, 3, 7)
    5
    
    Notes
    -----
    This is a flow based implementation of node connectivity. The 
    algorithm works by solving `O((n-\delta-1+\delta(\delta-1)/2)` max-flow 
    problems on an auxiliary digraph. Where `\delta` is the minimum degree 
    of G. For details about the auxiliary digraph and the computation of
    local node connectivity see local_node_connectivity.

    This implementation is based on algorithm 11 in [1]_. We use the Ford 
    and Fulkerson algorithm to compute max flow (see ford_fulkerson).
    
    See also
    --------
    local_node_connectivity
    all_pairs_node_connectivity_matrix
    local_edge_connectivity
    edge_connectivity
    max_flow
    ford_fulkerson 

    References
    ----------
    .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms. 
        http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf

    """
    # Local node connectivity
    if s is not None and t is not None:
        if s not in G:
            raise nx.NetworkXError('node %s not in graph' % s)
        if t not in G:
            raise nx.NetworkXError('node %s not in graph' % t)
        return local_node_connectivity(G, s, t)
    # Global node connectivity
    if G.is_directed():
        if not nx.is_weakly_connected(G):
            return 0
        iter_func = itertools.permutations
        # I think that it is necessary to consider both predecessors
        # and successors for directed graphs
        def neighbors(v):
            return itertools.chain.from_iterable([G.predecessors_iter(v),
                                                  G.successors_iter(v)])
    else:
        if not nx.is_connected(G):
            return 0
        iter_func = itertools.combinations
        neighbors = G.neighbors_iter
    # Initial guess \kappa = n - 1
    K = G.order()-1
    deg = G.degree()
    min_deg = min(deg.values())
    v = next(n for n,d in deg.items() if d==min_deg)
    # Reuse the auxiliary digraph
    H, mapping = _aux_digraph_node_connectivity(G)
    # compute local node connectivity with all non-neighbors nodes
    for w in set(G) - set(neighbors(v)) - set([v]):
        K = min(K, local_node_connectivity(G, v, w, 
                                            aux_digraph=H, mapping=mapping))
    # Same for non adjacent pairs of neighbors of v
    for x,y in iter_func(neighbors(v), 2):
        if y in G[x]: continue
        K = min(K, local_node_connectivity(G, x, y, 
                                            aux_digraph=H, mapping=mapping))
    return K

def all_pairs_node_connectivity_matrix(G):
    """Return a numpy 2d ndarray with node connectivity between all pairs
    of nodes.

    Parameters
    ----------
    G : NetworkX graph
        Undirected graph

    Returns
    -------
    K : 2d numpy ndarray
         node connectivity between all pairs of nodes.

    See also
    --------
    local_node_connectivity
    node_connectivity
    local_edge_connectivity
    edge_connectivity
    max_flow
    ford_fulkerson 

    """
    try:
        import numpy
    except ImportError:
        raise ImportError(\
            "all_pairs_node_connectivity_matrix() requires NumPy")

    n = G.order()
    M = numpy.zeros((n, n), dtype=int)
    # Create auxiliary Digraph
    D, mapping = _aux_digraph_node_connectivity(G)

    if G.is_directed():
        for u, v in itertools.permutations(G, 2):
            K = local_node_connectivity(G, u, v, aux_digraph=D, mapping=mapping)
            M[mapping[u],mapping[v]] = K
    else:
        for u, v in itertools.combinations(G, 2):
            K = local_node_connectivity(G, u, v, aux_digraph=D, mapping=mapping)
            M[mapping[u],mapping[v]] = M[mapping[v],mapping[u]] = K

    return M

def _aux_digraph_edge_connectivity(G):
    """Auxiliary digraph for computing flow based edge connectivity
    
    If the input graph is undirected, we replace each edge (u,v) with
    two reciprocal arcs (u,v) and (v,u) and then we set the attribute 
    'capacity' for each arc to 1. If the input graph is directed we simply
    add the 'capacity' attribute. Part of algorithm 1 in [1]_ .
    
    References
    ----------
    .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms. (this is a 
        chapter, look for the reference of the book).
        http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf
    """
    if G.is_directed():
        if nx.get_edge_attributes(G, 'capacity'):
            return G
        D = G.copy()
        capacity = dict((e,1) for e in D.edges())
        nx.set_edge_attributes(D, 'capacity', capacity)
        return D
    else:
        D = G.to_directed()
        capacity = dict((e,1) for e in D.edges())
        nx.set_edge_attributes(D, 'capacity', capacity)
        return D

def local_edge_connectivity(G, u, v, aux_digraph=None):
    r"""Returns local edge connectivity for nodes s and t in G.

    Local edge connectivity for two nodes s and t is the minimum number 
    of edges that must be removed to disconnect them.     

    This is a flow based implementation of edge connectivity. We compute the
    maximum flow on an auxiliary digraph build from the original
    network (see below for details). This is equal to the local edge 
    connectivity because the value of a maximum s-t-flow is equal to the 
    capacity of a minimum s-t-cut (Ford and Fulkerson theorem) [1]_ .

    Parameters
    ----------
    G : NetworkX graph
        Undirected or directed graph

    s : node
        Source node

    t : node
        Target node

    aux_digraph : NetworkX DiGraph (default=None)
        Auxiliary digraph to compute flow based edge connectivity. If None
        the auxiliary digraph is build.

    Returns
    -------
    K : integer
        local edge connectivity for nodes s and t

    Examples
    --------
    >>> # Platonic icosahedral graph has edge connectivity 5 
    >>> # for each non adjacent node pair
    >>> G = nx.icosahedral_graph()
    >>> nx.local_edge_connectivity(G,0,6)
    5

    Notes
    -----
    This is a flow based implementation of edge connectivity. We compute the
    maximum flow using the Ford and Fulkerson algorithm on an auxiliary digraph 
    build from the original graph:

    If the input graph is undirected, we replace each edge (u,v) with
    two reciprocal arcs `(u,v)` and `(v,u)` and then we set the attribute 
    'capacity' for each arc to 1. If the input graph is directed we simply
    add the 'capacity' attribute. This is an implementation of algorithm 1 
    in [1]_.
    
    The maximum flow in the auxiliary network is equal to the local edge 
    connectivity because the value of a maximum s-t-flow is equal to the 
    capacity of a minimum s-t-cut (Ford and Fulkerson theorem).

    See also
    --------
    local_node_connectivity
    node_connectivity
    edge_connectivity
    max_flow
    ford_fulkerson 

    References
    ----------
    .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms.
        http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf
 
    """
    if aux_digraph is None: 
        H = _aux_digraph_edge_connectivity(G)
    else:
        H = aux_digraph
    return nx.max_flow(H, u, v)

def edge_connectivity(G, s=None, t=None):
    r"""Returns the edge connectivity of the graph or digraph G.

    The edge connectivity is equal to the minimum number of edges that 
    must be removed to disconnect G or render it trivial. If source 
    and target nodes are provided, this function returns the local edge
    connectivity: the minimum number of edges that must be removed to 
    break all paths from source to target in G.
    
    This is a flow based implementation. The algorithm is based in solving 
    a number of max-flow problems (ie local st-edge connectivity, see 
    local_edge_connectivity) to determine the capacity of the minimum 
    cut on an auxiliary directed network that corresponds to the minimum 
    edge cut of G. It handles both directed and undirected graphs.
 
    Parameters
    ----------
    G : NetworkX graph
        Undirected or directed graph

    s : node
        Source node. Optional (default=None)

    t : node
        Target node. Optional (default=None)
 
    Returns
    -------
    K : integer
        Edge connectivity for G, or local edge connectivity if source 
        and target were provided

    Examples
    --------
    >>> # Platonic icosahedral graph is 5-edge-connected
    >>> G = nx.icosahedral_graph()
    >>> nx.edge_connectivity(G)
    5

    Notes
    -----
    This is a flow based implementation of global edge connectivity. For
    undirected graphs the algorithm works by finding a 'small' dominating 
    set of nodes of G (see algorithm 7 in [1]_ ) and computing local max flow 
    (see local_edge_connectivity) between an arbitrary node in the dominating 
    set and the rest of nodes in it. This is an implementation of 
    algorithm 6 in [1]_ .

    For directed graphs, the algorithm does n calls to the max flow function.
    This is an implementation of algorithm 8 in [1]_ . We use the Ford and 
    Fulkerson algorithm to compute max flow (see ford_fulkerson).
    
    See also
    --------
    local_node_connectivity
    node_connectivity
    local_edge_connectivity
    max_flow
    ford_fulkerson 

    References
    ----------
    .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms. 
        http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf

    """
    # Local edge connectivity
    if s is not None and t is not None:
        if s not in G:
            raise nx.NetworkXError('node %s not in graph' % s)
        if t not in G:
            raise nx.NetworkXError('node %s not in graph' % t)
        return local_edge_connectivity(G, s, t)
    # Global edge connectivity
    if G.is_directed():
        # Algorithm 8 in [1]
        if not nx.is_weakly_connected(G):
            return 0
        # initial value for lambda is min degree (\delta(G))
        L = min(G.degree().values())
        # reuse auxiliary digraph
        H = _aux_digraph_edge_connectivity(G)
        nodes = G.nodes()
        n = len(nodes)
        for i in range(n):
            try:
                L = min(L, local_edge_connectivity(G, nodes[i], 
                                                    nodes[i+1], aux_digraph=H))
            except IndexError: # last node!
                L = min(L, local_edge_connectivity(G, nodes[i], 
                                                    nodes[0], aux_digraph=H))
        return L
    else: # undirected
        # Algorithm 6 in [1]
        if not nx.is_connected(G):
            return 0
        # initial value for lambda is min degree (\delta(G))
        L = min(G.degree().values())
        # reuse auxiliary digraph
        H = _aux_digraph_edge_connectivity(G)
        # A dominating set is \lambda-covering
        # We need a dominating set with at least two nodes
        for node in G:
            D = dominating_set(G, start_with=node)
            v = D.pop()
            if D: break
        else: 
            # in complete graphs the dominating sets will always be of one node
            # thus we return min degree
            return L
        for w in D:
            L = min(L, local_edge_connectivity(G, v, w, aux_digraph=H))
        return L

def dominating_set(G, start_with=None):
    # Algorithm 7 in [1]
    all_nodes = set(G)
    if start_with is None:
        v = set(G).pop() # pick a node
    else:
        if start_with not in G:
            raise nx.NetworkXError('node %s not in G' % start_with)
        v = start_with
    D = set([v])
    ND = set([nbr for nbr in G[v]])
    other = all_nodes - ND - D
    while other:
        w = other.pop()
        D.add(w)
        ND.update([nbr for nbr in G[w] if nbr not in D])
        other = all_nodes - ND - D
    return D

def is_dominating_set(G, nbunch):
    # Proposed by Dan on the mailing list
    allnodes=set(G)
    testset=set(n for n in nbunch if n in G)
    nbrs=set()
    for n in testset:
        nbrs.update(G[n])
    if nbrs - allnodes:  # some nodes left--not dominating
        return False
    else:
        return True