This file is indexed.

/usr/lib/python3/dist-packages/networkx/algorithms/flow/networksimplex.py is in python3-networkx 1.11-2.

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
# -*- coding: utf-8 -*-
"""
Minimum cost flow algorithms on directed connected graphs.
"""

__author__ = """Loïc Séguin-C. <loicseguin@gmail.com>"""
# Copyright (C) 2010 Loïc Séguin-C. <loicseguin@gmail.com>
# All rights reserved.
# BSD license.

__all__ = ['network_simplex']

from itertools import chain, islice, repeat
from math import ceil, sqrt
import networkx as nx
from networkx.utils import not_implemented_for

try:
    from itertools import izip as zip
except ImportError:
    pass
try:
    range = xrange
except NameError:
    pass


@not_implemented_for('undirected')
def network_simplex(G, demand='demand', capacity='capacity', weight='weight'):
    r"""Find a minimum cost flow satisfying all demands in digraph G.

    This is a primal network simplex algorithm that uses the leaving
    arc rule to prevent cycling.

    G is a digraph with edge costs and capacities and in which nodes
    have demand, i.e., they want to send or receive some amount of
    flow. A negative demand means that the node wants to send flow, a
    positive demand means that the node want to receive flow. A flow on
    the digraph G satisfies all demand if the net flow into each node
    is equal to the demand of that node.

    Parameters
    ----------
    G : NetworkX graph
        DiGraph on which a minimum cost flow satisfying all demands is
        to be found.

    demand : string
        Nodes of the graph G are expected to have an attribute demand
        that indicates how much flow a node wants to send (negative
        demand) or receive (positive demand). Note that the sum of the
        demands should be 0 otherwise the problem in not feasible. If
        this attribute is not present, a node is considered to have 0
        demand. Default value: 'demand'.

    capacity : string
        Edges of the graph G are expected to have an attribute capacity
        that indicates how much flow the edge can support. If this
        attribute is not present, the edge is considered to have
        infinite capacity. Default value: 'capacity'.

    weight : string
        Edges of the graph G are expected to have an attribute weight
        that indicates the cost incurred by sending one unit of flow on
        that edge. If not present, the weight is considered to be 0.
        Default value: 'weight'.

    Returns
    -------
    flowCost : integer, float
        Cost of a minimum cost flow satisfying all demands.

    flowDict : dictionary
        Dictionary of dictionaries keyed by nodes such that
        flowDict[u][v] is the flow edge (u, v).

    Raises
    ------
    NetworkXError
        This exception is raised if the input graph is not directed,
        not connected or is a multigraph.

    NetworkXUnfeasible
        This exception is raised in the following situations:

            * The sum of the demands is not zero. Then, there is no
              flow satisfying all demands.
            * There is no flow satisfying all demand.

    NetworkXUnbounded
        This exception is raised if the digraph G has a cycle of
        negative cost and infinite capacity. Then, the cost of a flow
        satisfying all demands is unbounded below.

    Notes
    -----
    This algorithm is not guaranteed to work if edge weights
    are floating point numbers (overflows and roundoff errors can
    cause problems).

    See also
    --------
    cost_of_flow, max_flow_min_cost, min_cost_flow, min_cost_flow_cost

    Examples
    --------
    A simple example of a min cost flow problem.

    >>> import networkx as nx
    >>> G = nx.DiGraph()
    >>> G.add_node('a', demand=-5)
    >>> G.add_node('d', demand=5)
    >>> G.add_edge('a', 'b', weight=3, capacity=4)
    >>> G.add_edge('a', 'c', weight=6, capacity=10)
    >>> G.add_edge('b', 'd', weight=1, capacity=9)
    >>> G.add_edge('c', 'd', weight=2, capacity=5)
    >>> flowCost, flowDict = nx.network_simplex(G)
    >>> flowCost
    24
    >>> flowDict # doctest: +SKIP
    {'a': {'c': 1, 'b': 4}, 'c': {'d': 1}, 'b': {'d': 4}, 'd': {}}

    The mincost flow algorithm can also be used to solve shortest path
    problems. To find the shortest path between two nodes u and v,
    give all edges an infinite capacity, give node u a demand of -1 and
    node v a demand a 1. Then run the network simplex. The value of a
    min cost flow will be the distance between u and v and edges
    carrying positive flow will indicate the path.

    >>> G=nx.DiGraph()
    >>> G.add_weighted_edges_from([('s', 'u' ,10), ('s' ,'x' ,5),
    ...                            ('u', 'v' ,1), ('u' ,'x' ,2),
    ...                            ('v', 'y' ,1), ('x' ,'u' ,3),
    ...                            ('x', 'v' ,5), ('x' ,'y' ,2),
    ...                            ('y', 's' ,7), ('y' ,'v' ,6)])
    >>> G.add_node('s', demand = -1)
    >>> G.add_node('v', demand = 1)
    >>> flowCost, flowDict = nx.network_simplex(G)
    >>> flowCost == nx.shortest_path_length(G, 's', 'v', weight='weight')
    True
    >>> sorted([(u, v) for u in flowDict for v in flowDict[u] if flowDict[u][v] > 0])
    [('s', 'x'), ('u', 'v'), ('x', 'u')]
    >>> nx.shortest_path(G, 's', 'v', weight = 'weight')
    ['s', 'x', 'u', 'v']

    It is possible to change the name of the attributes used for the
    algorithm.

    >>> G = nx.DiGraph()
    >>> G.add_node('p', spam=-4)
    >>> G.add_node('q', spam=2)
    >>> G.add_node('a', spam=-2)
    >>> G.add_node('d', spam=-1)
    >>> G.add_node('t', spam=2)
    >>> G.add_node('w', spam=3)
    >>> G.add_edge('p', 'q', cost=7, vacancies=5)
    >>> G.add_edge('p', 'a', cost=1, vacancies=4)
    >>> G.add_edge('q', 'd', cost=2, vacancies=3)
    >>> G.add_edge('t', 'q', cost=1, vacancies=2)
    >>> G.add_edge('a', 't', cost=2, vacancies=4)
    >>> G.add_edge('d', 'w', cost=3, vacancies=4)
    >>> G.add_edge('t', 'w', cost=4, vacancies=1)
    >>> flowCost, flowDict = nx.network_simplex(G, demand='spam',
    ...                                         capacity='vacancies',
    ...                                         weight='cost')
    >>> flowCost
    37
    >>> flowDict  # doctest: +SKIP
    {'a': {'t': 4}, 'd': {'w': 2}, 'q': {'d': 1}, 'p': {'q': 2, 'a': 2}, 't': {'q': 1, 'w': 1}, 'w': {}}

    References
    ----------
    .. [1] Z. Kiraly, P. Kovacs.
           Efficient implementation of minimum-cost flow algorithms.
           Acta Universitatis Sapientiae, Informatica 4(1):67--118. 2012.
    .. [2] R. Barr, F. Glover, D. Klingman.
           Enhancement of spanning tree labeling procedures for network
           optimization.
           INFOR 17(1):16--34. 1979.
    """
    ###########################################################################
    # Problem essentials extraction and sanity check
    ###########################################################################

    if len(G) == 0:
        raise nx.NetworkXError('graph has no nodes')

    # Number all nodes and edges and hereafter reference them using ONLY their
    # numbers

    N = list(G)                                # nodes
    I = {u: i for i, u in enumerate(N)}        # node indices
    D = [G.node[u].get(demand, 0) for u in N]  # node demands

    inf = float('inf')
    for p, b in zip(N, D):
        if abs(b) == inf:
            raise nx.NetworkXError('node %r has infinite demand' % (p,))

    multigraph = G.is_multigraph()
    S = []  # edge sources
    T = []  # edge targets
    if multigraph:
        K = []  # edge keys
    E = {}  # edge indices
    U = []  # edge capacities
    C = []  # edge weights

    if not multigraph:
        edges = G.edges_iter(data=True)
    else:
        edges = G.edges_iter(data=True, keys=True)
    edges = (e for e in edges
             if e[0] != e[1] and e[-1].get(capacity, inf) != 0)
    for i, e in enumerate(edges):
        S.append(I[e[0]])
        T.append(I[e[1]])
        if multigraph:
            K.append(e[2])
        E[e[:-1]] = i
        U.append(e[-1].get(capacity, inf))
        C.append(e[-1].get(weight, 0))

    for e, c in zip(E, C):
        if abs(c) == inf:
            raise nx.NetworkXError('edge %r has infinite weight' % (e,))
    if not multigraph:
        edges = G.selfloop_edges(data=True)
    else:
        edges = G.selfloop_edges(data=True, keys=True)
    for e in edges:
        if abs(e[-1].get(weight, 0)) == inf:
            raise nx.NetworkXError('edge %r has infinite weight' % (e[:-1],))

    ###########################################################################
    # Quick infeasibility detection
    ###########################################################################

    if sum(D) != 0:
        raise nx.NetworkXUnfeasible('total node demand is not zero')
    for e, u in zip(E, U):
        if u < 0:
            raise nx.NetworkXUnfeasible('edge %r has negative capacity' % (e,))
    if not multigraph:
        edges = G.selfloop_edges(data=True)
    else:
        edges = G.selfloop_edges(data=True, keys=True)
    for e in edges:
        if e[-1].get(capacity, inf) < 0:
            raise nx.NetworkXUnfeasible(
                'edge %r has negative capacity' % (e[:-1],))

    ###########################################################################
    # Initialization 
    ###########################################################################

    # Add a dummy node -1 and connect all existing nodes to it with infinite-
    # capacity dummy edges. Node -1 will serve as the root of the
    # spanning tree of the network simplex method. The new edges will used to
    # trivially satisfy the node demands and create an initial strongly
    # feasible spanning tree.
    n = len(N)  # number of nodes
    for p, d in enumerate(D):
        if d > 0:  # Must be greater-than here. Zero-demand nodes must have
                   # edges pointing towards the root to ensure strong
                   # feasibility.
            S.append(-1)
            T.append(p)
        else:
            S.append(p)
            T.append(-1)
    faux_inf = 3 * max(chain([sum(u for u in U if u < inf),
                              sum(abs(c) for c in C)],
                             (abs(d) for d in D))) or 1
    C.extend(repeat(faux_inf, n))
    U.extend(repeat(faux_inf, n))

    # Construct the initial spanning tree.
    e = len(E)                                           # number of edges
    x = list(chain(repeat(0, e), (abs(d) for d in D)))   # edge flows
    pi = [faux_inf if d <= 0 else -faux_inf for d in D]  # node potentials
    parent = list(chain(repeat(-1, n), [None]))  # parent nodes
    edge = list(range(e, e + n))                 # edges to parents
    size = list(chain(repeat(1, n), [n + 1]))    # subtree sizes
    next = list(chain(range(1, n), [-1, 0]))     # next nodes in depth-first thread 
    prev = list(range(-1, n))                    # previous nodes in depth-first thread
    last = list(chain(range(n), [n - 1]))        # last descendants in depth-first thread

    ###########################################################################
    # Pivot loop
    ###########################################################################

    def reduced_cost(i):
        """Return the reduced cost of an edge i.
        """
        c = C[i] - pi[S[i]] + pi[T[i]]
        return c if x[i] == 0 else -c

    def find_entering_edges():
        """Yield entering edges until none can be found.
        """
        if e == 0:
            return

        # Entering edges are found by combining Dantzig's rule and Bland's
        # rule. The edges are cyclically grouped into blocks of size B. Within
        # each block, Dantzig's rule is applied to find an entering edge. The
        # blocks to search is determined following Bland's rule.
        B = int(ceil(sqrt(e)))  # pivot block size
        M = (e + B - 1) // B    # number of blocks needed to cover all edges
        m = 0                   # number of consecutive blocks without eligible
                                # entering edges
        f = 0                   # first edge in block
        while m < M:
            # Determine the next block of edges.
            l = f + B
            if l <= e:
                edges = range(f, l)
            else:
                l -= e
                edges = chain(range(f, e), range(l))
            f = l
            # Find the first edge with the lowest reduced cost.
            i = min(edges, key=reduced_cost)
            c = reduced_cost(i)
            if c >= 0:
                # No entering edge found in the current block.
                m += 1
            else:
                # Entering edge found.
                if x[i] == 0:
                    p = S[i]
                    q = T[i]
                else:
                    p = T[i]
                    q = S[i]
                yield i, p, q
                m = 0
        # All edges have nonnegative reduced costs. The current flow is
        # optimal.

    def find_apex(p, q):
        """Find the lowest common ancestor of nodes p and q in the spanning
        tree.
        """
        size_p = size[p]
        size_q = size[q]
        while True:
            while size_p < size_q:
                p = parent[p]
                size_p = size[p]
            while size_p > size_q:
                q = parent[q]
                size_q = size[q]
            if size_p == size_q:
                if p != q:
                    p = parent[p]
                    size_p = size[p]
                    q = parent[q]
                    size_q = size[q]
                else:
                    return p

    def trace_path(p, w):
        """Return the nodes and edges on the path from node p to its ancestor
        w.
        """
        Wn = [p]
        We = []
        while p != w:
            We.append(edge[p])
            p = parent[p]
            Wn.append(p)
        return Wn, We

    def find_cycle(i, p, q):
        """Return the nodes and edges on the cycle containing edge i == (p, q)
        when the latter is added to the spanning tree.

        The cycle is oriented in the direction from p to q.
        """
        w = find_apex(p, q)
        Wn, We = trace_path(p, w)
        Wn.reverse()
        We.reverse()
        We.append(i)
        WnR, WeR = trace_path(q, w)
        del WnR[-1]
        Wn += WnR
        We += WeR
        return Wn, We


    def residual_capacity(i, p):
        """Return the residual capacity of an edge i in the direction away
        from its endpoint p.
        """
        return U[i] - x[i] if S[i] == p else x[i]

    def find_leaving_edge(Wn, We):
        """Return the leaving edge in a cycle represented by Wn and We.
        """
        j, s = min(zip(reversed(We), reversed(Wn)),
                   key=lambda i_p: residual_capacity(*i_p))
        t = T[j] if S[j] == s else S[j]
        return j, s, t

    def augment_flow(Wn, We, f):
        """Augment f units of flow along a cycle represented by Wn and We.
        """
        for i, p in zip(We, Wn):
            if S[i] == p:
                x[i] += f
            else:
                x[i] -= f

    def trace_subtree(p):
        """Yield the nodes in the subtree rooted at a node p.
        """
        yield p
        l = last[p]
        while p != l:
            p = next[p]
            yield p

    def remove_edge(s, t):
        """Remove an edge (s, t) where parent[t] == s from the spanning tree.
        """
        size_t = size[t]
        prev_t = prev[t]
        last_t = last[t]
        next_last_t = next[last_t]
        # Remove (s, t).
        parent[t] = None
        edge[t] = None
        # Remove the subtree rooted at t from the depth-first thread.
        next[prev_t] = next_last_t
        prev[next_last_t] = prev_t
        next[last_t] = t
        prev[t] = last_t
        # Update the subtree sizes and last descendants of the (old) acenstors
        # of t.
        while s is not None:
            size[s] -= size_t
            if last[s] == last_t:
                last[s] = prev_t
            s = parent[s]

    def make_root(q):
        """Make a node q the root of its containing subtree.
        """
        ancestors = []
        while q is not None:
            ancestors.append(q)
            q = parent[q]
        ancestors.reverse()
        for p, q in zip(ancestors, islice(ancestors, 1, None)):
            size_p = size[p]
            last_p = last[p]
            prev_q = prev[q]
            last_q = last[q]
            next_last_q = next[last_q]
            # Make p a child of q.
            parent[p] = q
            parent[q] = None
            edge[p] = edge[q]
            edge[q] = None
            size[p] = size_p - size[q]
            size[q] = size_p
            # Remove the subtree rooted at q from the depth-first thread.
            next[prev_q] = next_last_q
            prev[next_last_q] = prev_q
            next[last_q] = q
            prev[q] = last_q
            if last_p == last_q:
                last[p] = prev_q
                last_p = prev_q
            # Add the remaining parts of the subtree rooted at p as a subtree
            # of q in the depth-first thread.
            prev[p] = last_q
            next[last_q] = p
            next[last_p] = q
            prev[q] = last_p
            last[q] = last_p

    def add_edge(i, p, q):
        """Add an edge (p, q) to the spanning tree where q is the root of a
        subtree.
        """
        last_p = last[p]
        next_last_p = next[last_p]
        size_q = size[q]
        last_q = last[q]
        # Make q a child of p.
        parent[q] = p
        edge[q] = i
        # Insert the subtree rooted at q into the depth-first thread.
        next[last_p] = q
        prev[q] = last_p
        prev[next_last_p] = last_q
        next[last_q] = next_last_p
        # Update the subtree sizes and last descendants of the (new) ancestors
        # of q.
        while p is not None:
            size[p] += size_q
            if last[p] == last_p:
                last[p] = last_q
            p = parent[p]

    def update_potentials(i, p, q):
        """Update the potentials of the nodes in the subtree rooted at a node
        q connected to its parent p by an edge i.
        """
        if q == T[i]:
            d = pi[p] - C[i] - pi[q]
        else:
            d = pi[p] + C[i] - pi[q]
        for q in trace_subtree(q):
            pi[q] += d

    # Pivot loop
    for i, p, q in find_entering_edges():
        Wn, We = find_cycle(i, p, q)
        j, s, t = find_leaving_edge(Wn, We)
        augment_flow(Wn, We, residual_capacity(j, s))
        if i != j:  # Do nothing more if the entering edge is the same as the
                    # the leaving edge.
            if parent[t] != s:
                # Ensure that s is the parent of t.
                s, t = t, s
            if We.index(i) > We.index(j):
                # Ensure that q is in the subtree rooted at t.
                p, q = q, p
            remove_edge(s, t)
            make_root(q)
            add_edge(i, p, q)
            update_potentials(i, p, q)

    ###########################################################################
    # Infeasibility and unboundedness detection
    ###########################################################################

    if any(x[i] != 0 for i in range(-n, 0)):
        raise nx.NetworkXUnfeasible('no flow satisfies all node demands')

    if (any(x[i] * 2 >= faux_inf for i in range(e)) or
        any(e[-1].get(capacity, inf) == inf and e[-1].get(weight, 0) < 0
            for e in G.selfloop_edges(data=True))):
        raise nx.NetworkXUnbounded(
            'negative cycle with infinite capacity found')

    ###########################################################################
    # Flow cost calculation and flow dict construction
    ###########################################################################

    del x[e:]
    flow_cost = sum(c * x for c, x in zip(C, x))
    flow_dict = {n: {} for n in N}

    def add_entry(e):
        """Add a flow dict entry.
        """
        d = flow_dict[e[0]]
        for k in e[1:-2]:
            try:
                d = d[k]
            except KeyError:
                t = {}
                d[k] = t
                d = t
        d[e[-2]] = e[-1]

    S = (N[s] for s in S)  # Use original nodes.
    T = (N[t] for t in T)  # Use original nodes.
    if not multigraph:
        for e in zip(S, T, x):
            add_entry(e)
        edges = G.edges_iter(data=True)
    else:
        for e in zip(S, T, K, x):
            add_entry(e)
        edges = G.edges_iter(data=True, keys=True)
    for e in edges:
        if e[0] != e[1]:
            if e[-1].get(capacity, inf) == 0:
                add_entry(e[:-1] + (0,))
        else:
            c = e[-1].get(weight, 0)
            if c >= 0:
                add_entry(e[:-1] + (0,))
            else:
                u = e[-1][capacity]
                flow_cost += c * u
                add_entry(e[:-1] + (u,))

    return flow_cost, flow_dict