Пример #1
0
def max_cycle_ratio(g, estimate=None):
    maxratio, arg_cycle = None, None
    for scc in nx.strongly_connected_component_subgraphs(g):
        root = next(iter(scc.nodes()))
        scc_mcr, cycle = compute_mcr_component(scc, root, estimate)
        if scc_mcr is None:
            continue

        if maxratio is None or scc_mcr > maxratio:
            maxratio = scc_mcr
            arg_cycle = cycle

    forest = Forest()
    for scc in nx.strongly_connected_component_subgraphs(g, False):
        if scc.number_of_edges() == 0:
            continue

        for (v, w, scc_data) in scc.edges(data=True):
            data = g.get_edge_data(v, w)

            # negate weight so that we can construct a longest paths tree for the current solution
            scc_data['w'] = data.get('weight',
                                     0) - data.get('tokens', 0) * maxratio

        root = w
        parents, _ = longest_distances(scc, root, 'w')
        for child in parents:
            in_edge = parents.get(child)
            if in_edge is not None:
                forest.add_edge(*in_edge)

    return maxratio, arg_cycle, forest
Пример #2
0
    def test_example_2( self ):
        g = nx.MultiDiGraph()
        g.add_edge(1, 5, weight = -5)
        g.add_edge(1, 4, weight = -4)
        g.add_edge(1, 3, weight = -3)
        g.add_edge(1, 2, weight = -1)

        g.add_edge(2, 5, weight = -4)
        g.add_edge(2, 4, weight = -3)
        g.add_edge(2, 3, weight = -1)

        g.add_edge(3, 2, weight = 0)
        g.add_edge(3, 4, weight = -1)

        g.add_edge(4, 2, weight = 1)
        g.add_edge(4, 3, weight = 0)
        g.add_edge(4, 5, weight = -1)

        try:
            parents, distances = longest_distances( g, 1 )
            expected_distances = { 1: 0, 2: -1, 3: -2, 4: -3, 5: -4 }
            expected_parents = {
                1: None,
                2: (1, 2, 0),
                3: (2, 3, 0),
                4: (3, 4, 0),
                5: (4, 5, 0)}

            self.assertDictEqual( distances, expected_distances )
            self.assertDictEqual( parents, expected_parents )

        except PositiveCycle as c:
            self.fail( "False positive cycle detected" )
Пример #3
0
    def test_example_1( self ):
        g = nx.MultiDiGraph()
        g.add_edge(1, 2, weight = 2)
        g.add_edge(1, 3, weight = -3)
        g.add_edge(2, 4, weight = -3)
        g.add_edge(2, 6, weight = -6)
        g.add_edge(3, 2, weight = 3)
        g.add_edge(3, 5, weight = -1)
        g.add_edge(4, 2, weight = 2)
        g.add_edge(4, 3, weight = -1)
        g.add_edge(4, 5, weight = -1)
        g.add_edge(4, 6, weight = -2)
        g.add_edge(4, 7, weight = -2)
        g.add_edge(5, 3, weight = -1)
        g.add_edge(5, 7, weight = -1)
        g.add_edge(6, 7, weight = 1)
        g.add_edge(7, 6, weight = -2)

        try:
            parents, distances = longest_distances( g, 1 )
            expected_distances = { 1: 0, 2: 2, 3: -2, 4: -1, 5: -2, 6: -3, 7: -2 }
            expected_parents = {
                1: None,
                2: (1, 2, 0),
                3: (4, 3, 0),
                4: (2, 4, 0),
                5: (4, 5, 0),
                6: (4, 6, 0),
                7: (6, 7, 0)}

            self.assertDictEqual( distances, expected_distances )
            self.assertDictEqual( parents, expected_parents )

        except PositiveCycle as c:
            self.fail( "False positive cycle detected" )
Пример #4
0
    def test_positive_self_loop(self):
        # create a small graph
        g = nx.MultiDiGraph()
        g.add_edge(1, 2, weight = -1)
        g.add_edge(2, 3, weight = 1)
        g.add_edge(2, 3, weight = -1)
        g.add_edge(3, 3, weight = 1)

        try:
            parents, distances = longest_distances( g, 1 )
        except PositiveCycle as c:
            self.assertListEqual( c.cycle, [(3, 3, 0)] )
        else:
            self.fail( "Positive self-loop not detected" )
Пример #5
0
def strictly_periodic_schedule(graph, admissible=True):
    # transform the graph to its (pessimistic) single-rate aproximation
    apx = transform.single_rate_apx(graph, admissible)

    # transform the single-rate apx to a marked graph
    mg = transform.single_rate_as_marked_graph(apx, True)

    # compute max. cycle ratio for the approximation
    cycle_time, cycle, *_ = mcr.max_cycle_ratio(mg)

    # create a weighted graph representation from mg
    wg = nx.MultiDiGraph()
    for u, v, data in mg.edges(data=True):
        wg.add_edge(u,
                    v,
                    weight=data.get('weight', 0) -
                    data.get('tokens', 0) * cycle_time)

    # choose a critical node
    (a, *_), *_ = cycle

    # compute the longest distances from a critical node
    parents, eigen_vector = graphs.longest_distances(wg, a)

    # compute the period for each actor
    periods = {
        v: (graph.modulus() // graph.repetition_vector()[v]) * cycle_time
        for v in graph
    }

    # transform the eigenvector to a periodic schedule for the graph
    # first element in tuple = time of first firing
    result = {
        v: (eigen_vector[v] +
            (graph.modulus() // graph.repetition_vector()[v] - 1) * cycle_time,
            periods[v])
        for v in periods
    }

    # ensure that time of first firing is non-negative
    min_start_time, _ = min(result.values())
    return {
        v: (eigen_vector[v] - min_start_time +
            (graph.modulus() // graph.repetition_vector()[v] - 1) * cycle_time,
            periods[v])
        for v in periods
    }
Пример #6
0
    def test_positive_small_loop(self):
        # create a small graph
        g = nx.MultiDiGraph()
        g.add_edge(1, 2, weight = -1)
        g.add_edge(2, 3, weight = 1)
        g.add_edge(2, 3, weight = -1)
        g.add_edge(3, 2, weight = 0)

        try:
            parents, distances = longest_distances( g, 1 )
        except PositiveCycle as c:
            # put cycle into "canonical" form
            min_edge = min( c.cycle )
            while c.cycle[ 0 ] > min_edge:
                c.cycle = c.cycle[1:] + [ c.cycle[ 0 ] ]

            self.assertListEqual( c.cycle, [(2, 3, 0),(3, 2, 0)] )
        else:
            self.fail( "Positive cycle not detected" )
Пример #7
0
def compute_mcr_component(g, root, estimate=None):
    ''' Computes the maximum cycle ratio of g.
    NOTES:
        - The weight on each edge must be non-negative
        - The number of tokens on each edge must be non-negative.
        - The graph is assumed to be strongly connected.
    '''

    # initialize:
    distances = {}
    queue = PriorityQueue()

    if estimate is None:
        # determine lower bound for mcr
        estimate = 1
        for (v, w, data) in g.edges(data=True):
            tokens = data.get('tokens', 0)
            weight = data.get('weight', 0)
            estimate = estimate + max(0, weight)

    initial_graph = nx.MultiDiGraph()

    # construct graph with non-parametric path weights
    for v in g:
        initial_graph.add_node(v)

    for v, w, key, data in nx.MultiDiGraph(g).edges(keys=True, data=True):
        tokens = data.get('tokens', 0)
        weight = data.get('weight', 0)
        initial_graph.add_edge(v,
                               w,
                               key,
                               weight=weight - tokens * estimate,
                               dist=pdistance(weight, tokens))

    try:
        parents, _ = longest_distances(initial_graph, root)
        # build tree from parents
        tree = Forest()
        for child in parents:
            in_edge = parents.get(child)
            if in_edge is not None:
                tree.add_edge(*in_edge)

        distances[root] = pdistance(0, 0)
        if root in tree:
            for v, w, key in tree.pre_order_edges(root):
                dv = distances[v]
                data = initial_graph.get_edge_data(v, w, key)
                distances[w] = dv + data.get('dist')

    except PositiveCycle as ex:
        raise InfeasibleException(ex.cycle)

    # fill priority queue:
    # go over all nodes and compute their key
    # print("Distances from root {}: {}".format(root, distances))
    for v in distances:
        update_node_key(initial_graph, v, distances, queue)

    # pivot until cycle is found
    while len(queue) > 0:
        (node, (ratio, (v, w, vw_key))) = queue.pop()
        delta = distances[v] + initial_graph.get_edge_data(
            v, w, vw_key)['dist'] - distances[w]

        for j in tree.pre_order(w):
            # update parametric distance to j
            distances[j] += delta

            if v == j:
                # j is reachable from v -> there's a cycle!
                is_multi = g.is_multigraph()
                path = deque([(v, w, vw_key) if is_multi else (v, w)])
                p = v
                while p != w:
                    k, _, key = tree.parent(p)
                    path.appendleft((k, p, key) if is_multi else (k, p))
                    p = k
                return -ratio, list(path)

            # update successors of j; the node key of a successor k can only increase!
            for _, k, jk_key, data in initial_graph.out_edges(j,
                                                              keys=True,
                                                              data=True):
                # update priority of (j, k)
                ratio_k = None
                if k in queue:
                    ratio_k, _ = queue[k]

                delta_k = distances[j] + data['dist'] - distances[k]
                if delta_k[1] > 0:
                    r = -Fraction(delta_k[0], delta_k[1])
                    if ratio_k is None or r < ratio_k:
                        queue[k] = (r, (j, k, jk_key))

            # recompute vertex key of j
            update_node_key(initial_graph, j, distances, queue)

        tree.add_edge(v, w, vw_key)
    else:
        # no cycle found, any period is admissible
        # Note that this implies that the graph is acyclic
        return None, None