Пример #1
0
def test_feasible( csdfg, bindings, target_mcr ):
    # compute pessimistic approximation
    pess = transform.single_rate_apx( csdfg, bindings = bindings )

    # set path weights
    for u, v, data in pess.edges_iter( data = True ):
        tokens = data.get( __PARAM_TOKENS__, 0 )
        weight = pess.node[ v ].get( __PARAM_WCET__ )
        data['weight'] = tokens * target_mcr - weight

    # compute longest paths
    try:
        bfct.find_shortest_paths( pess, None )
        return None
    except NegativeCycleException as ex:
        return ex.cycle
Пример #2
0
def compute_vertex_keys(graph, variable, q, parameter):
    root = next(graph.nodes_iter())
    # construct shortest paths tree using token = parameter
    for v, w, data in graph.edges_iter(data=True):
        tokens = data.get(__param__tokens, 0)
        weight = data.get(__param__weight, 0)
        data['dist'] = pdistance(weight, tokens)
        data['w'] = tokens * parameter - weight

    distances = dict()
    try:
        tree, _, _ = bfct.find_shortest_paths(graph, root, 'w')
        distances[root] = pdistance(0, 0)
        for v, i in tree.dfs(root):
            if i == 0:
                dv = distances[v]
                for w in tree.children(v):
                    distances[w] = dv + graph.get_edge_data(v, w).get('dist')

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

    # compute vertex keys
    for v in graph:
        ratio, edge = compute_vertex_key(graph, v, distances)
        q[(variable, v)] = (-ratio, edge)

    # clean up 'dist' and 'w' parameters
    for v, w, data in graph.edges_iter(data=True):
        del data['dist']
        del data['w']

    # return shortest paths tree that is valid for the parameter
    return tree
Пример #3
0
def compute_mcm(g, estimate=None, pweight='weight'):
    maxmean, arg_cycle = None, None
    for scc in nx.strongly_connected_component_subgraphs(g):
        root = next(scc.nodes_iter())
        scc_mcm, cycle = compute_mcm_component(scc, root, estimate, pweight)
        if scc_mcm is None:
            continue

        if maxmean is None or scc_mcm > maxmean:
            maxmean = scc_mcm
            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_iter(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'] = maxmean - data.get('weight', 0)

        root = w
        lpp_tree, _ = bfct.find_shortest_paths(scc, root, arg='w')
        forest.add_forest(lpp_tree)

    return maxmean, arg_cycle, forest
Пример #4
0
def test_feasible(csdfg, bindings, target_mcr):
    # compute pessimistic approximation
    pess = transform.single_rate_apx(csdfg, bindings=bindings)

    # set path weights
    for u, v, data in pess.edges_iter(data=True):
        tokens = data.get(__PARAM_TOKENS__, 0)
        weight = pess.node[v].get(__PARAM_WCET__)
        data['weight'] = tokens * target_mcr - weight

    # compute longest paths
    try:
        bfct.find_shortest_paths(pess, None)
        return None
    except NegativeCycleException as ex:
        return ex.cycle
Пример #5
0
def compute_mcm( g, estimate = None, pweight = 'weight' ):
    maxmean, arg_cycle = None, None
    for scc in nx.strongly_connected_component_subgraphs( g ):
        root = next( scc.nodes_iter() )
        scc_mcm, cycle = compute_mcm_component( scc, root, estimate, pweight )
        if scc_mcm is None:
            continue

        if maxmean is None or scc_mcm > maxmean:
            maxmean = scc_mcm
            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_iter( 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'] = maxmean - data.get( 'weight', 0 )

        root = w
        lpp_tree, _ = bfct.find_shortest_paths( scc, root, arg = 'w' )
        forest.add_forest( lpp_tree )

    return maxmean, arg_cycle, forest
Пример #6
0
def compute_vertex_keys( graph, variable, q, parameter ):
    root = next( graph.nodes_iter() )
    # construct shortest paths tree using token = parameter
    for v, w, data in graph.edges_iter(data=True):
        tokens = data.get(__param__tokens, 0)
        weight = data.get(__param__weight, 0)
        data['dist'] = pdistance( weight, tokens )
        data['w'] = tokens * parameter - weight

    distances = dict()
    try:
        tree, _, _ = bfct.find_shortest_paths(graph, root, 'w')
        distances[root] = pdistance(0, 0)
        for v, i in tree.dfs( root ):
            if i == 0:
                dv = distances[ v ]
                for w in tree.children( v ):
                    distances[ w ] = dv + graph.get_edge_data(v, w).get('dist')

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

    # compute vertex keys
    for v in graph:
        ratio, edge = compute_vertex_key( graph, v, distances )
        q[(variable, v)] = ( -ratio, edge )

    # clean up 'dist' and 'w' parameters
    for v, w, data in graph.edges_iter( data = True ):
        del data['dist']
        del data['w']

    # return shortest paths tree that is valid for the parameter
    return tree
Пример #7
0
def path_weight( graph, start, end ):
    # compute shortest paths
    _, dists, _ = bfct.find_shortest_paths( graph, start )
    return dists.get( end, None )
Пример #8
0
def ensure_liveness(g, factor = 100):
    """ Assigns tokens to channels of an SDF graph such that a minimum throughput is attained.
    Works by finding cycles of positive weight in the graph's pessimistic approximation
    and resolving them by adding sufficient tokens.

    Parameters:
    ----------

    g:      the SDF graph
    factor: indicates the maximum number of time units a single graph iteration may take,
            multiplied by the length of a fully sequential iteration.
            Lower numbers result in higher throughputs
    """

    assert g.is_consistent()
    # assert nx.is_strongly_connected( g )
    seqperiod = 0
    for v, data in g.nodes_iter( data = True ):
        wcet = data.get('wcet').sum()
        # periods = g.q[v] // data.get('phases')
        periods = 1
        seqperiod += ( wcet * periods )

    target_mcr = Fraction( seqperiod, g.tpi ) / factor

    added_tokens = 0
    m = 4 * g.number_of_edges()
    while m > 0:
        m = m - 1
        # transform the graph to its single-rate approximation
        pess = transform.single_rate_apx( g )

        # create graph where each token represents a weight of -factor
        lpg = nx.DiGraph()
        for u, v, data in pess.edges( data = True ):
            wcet = pess.node[v].get('wcet')
            tokens = data.get('tokens', 0)
            lpg.add_edge( u, v, weight = tokens * target_mcr - wcet )

        # find positive cycle
        if m == 0:
            import pdb; pdb.set_trace()
        try:
            bfct.find_shortest_paths( lpg, u )
            break
        except NegativeCycleException as ex:
            # find edge with heaviest weight
            weight, tokens = 0, 0
            max_weight = 0
            for u, v in ex.cycle:
                weight += pess.node[ v ].get('wcet')
                tokens += pess.get_edge_data(u, v).get('tokens', 0)
                suv = g.s[ (u, v) ]
                if suv > max_weight:
                    max_weight = suv
                    arg_max = (u, v)

            delta = Fraction( Fraction(weight, target_mcr) - tokens, max_weight ).__ceil__()
            edge_data = g.get_edge_data( *arg_max )
            edge_data['tokens'] = edge_data.get('tokens', 0) + delta #math.ceil(extra_factor * delta)
            added_tokens += delta
            # print("Added {} tokens".format( delta ))
    else:
        assert False, "too many iterations"
            
    return added_tokens
Пример #9
0
def test_random(n=50, p=0.1, runs=1000, debug=None):
    for run in range(runs) if debug is None else [debug]:
        g = fast_gnp_random_graph(n, p, seed=run + 1, directed=True)

        # add source connected to all nodes
        source = 100 * (n // 100) + 200
        for v in list(g.nodes()):
            g.add_edge(source, v, weight=0, tokens=0)

        # add random weights and tokens
        wsum = 1
        for _, _, data in g.edges_iter(data=True):
            data['weight'] = randrange(1, 10)
            data['tokens'] = randrange(-1, 8)
            wsum += data['weight']

        # create shortest path formulation for initial tree
        for _, _, data in g.edges_iter(data=True):
            data['sp'] = data['tokens'] * wsum - data['weight']

        # ensure that the graph admits a feasible solution
        its = 0
        while True:
            try:
                its += 1
                tree, distances = bfct.find_shortest_paths(g, source, arg='sp')
                break
            except NegativeCycleException as ex:
                toks = sum(
                    map(lambda vw: g.get_edge_data(*vw).get('tokens'),
                        ex.cycle))
                edge_data = g.get_edge_data(*choice(ex.cycle))
                edge_data['tokens'] += (1 - toks)
                edge_data[
                    'sp'] = edge_data['tokens'] * wsum - edge_data['weight']

        if debug is not None:
            import pdb
            pdb.set_trace()

        negative_toks = False
        for _, _, data in g.edges_iter(data=True):
            if data['tokens'] < 0:
                negative_toks = True
                break

        print("Run {}: negative tokens: {}, iterations: {}".format(
            run, negative_toks, its))

        ratio, cycle = compute_mcr(g, source)
        assert ratio is not None, "Deadlocked cycle found"
        if not cycle:
            # verify that the graph is acyclic
            assert is_directed_acyclic_graph(
                g), "[run = {}] Graph is not acyclic".format(run)
        else:
            wsum, tsum = 0, 0
            for v, w in cycle:
                data = g.get_edge_data(v, w)
                wsum += data['weight']
                tsum += data['tokens']

            assert Fraction(
                wsum, tsum
            ) == ratio, "[run = {}] computed MCR {} does not match ratio of critical cycle {}".format(
                run, ratio, Fraction(wsum, tsum))

            for v, w, data in g.edges_iter(data=True):
                data['weight'] = data['tokens'] * ratio - data['weight']

            try:
                bellman_ford(g, source)
            except Exception:
                print("Exception during run {}".format(run))
Пример #10
0
def compute_mcm_component(g, root, estimate=None, pweight='weight'):
    """ Computes the maximum cycle mean 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 = pq.priority_queue()

    # determine lower bound for mcr
    init_mcr = 1
    for (v, w, data) in g.edges_iter(data=True):
        weight = data.get(pweight, 0)
        init_mcr = init_mcr + max(0, weight)
        data['dist'] = pdistance(weight, 1)

    if estimate is not None:
        # compute initial tree by computing shortest paths tree
        # print("Computing initial tree from estimate {}".format(estimate))
        for (v, w, data) in g.edges_iter(data=True):
            dist = data['dist']
            data['w'] = estimate - dist[0]

        try:
            tree, _ = bfct.find_shortest_paths(g, root, arg='w')
            distances[root] = pdistance(0, 0)
            for v, i in tree.dfs(root):
                if i == 0:
                    dv = distances[v]
                    for w in tree.children(v):
                        distances[w] = dv + g.get_edge_data(v, w).get('dist')
        except NegativeCycleException as ex:
            # print("Infeasible solution for estimate {}, due to cycle {}".format(estimate, [(u, v, data) for u, v, data in g.edges_iter(data = True) if (u, v) in ex.cycle]))
            raise InfeasibleException(ex.cycle)
    else:
        # create tree rooted at root
        tree = DFSTree(root)

        # run Dijkstra on tokens
        distances[root] = 0
        queue[root] = 0
        while len(queue) > 0:
            v, _ = queue.pop()
            dv = distances[v]
            for _, w, data in g.out_edges_iter(v, True):
                dw = distances.get(w, None)
                if (dw is None) or dv + 1 < dw:
                    distances[w] = dv + 1
                    queue[w] = distances[w]
                    tree.append_child(v, w)

        # DFS on shortest paths DAG
        # (this is the DAG induced by the union of all shortest path trees)
        pre, post, index = dict(), dict(), 0
        post_order = list()
        for v, i in tree.dfs(root):
            if i == 0:
                pre[v] = index
                index += 1
                dv = distances[v]
                for _, w, data in g.out_edges_iter(v, True):
                    dw = distances[w]
                    if dv + 1 == dw and w not in pre:
                        tree.append_child(v, w)
            else:
                post[v] = index
                index += 1
                post_order.append(v)

        # scan in topological order
        distances.clear()
        distances[root] = pdistance(0, 0)
        while post_order:
            v = post_order.pop()
            dv = distances[v]
            for _, w, data in g.out_edges_iter(v, True):
                dist = data['dist']
                dw = distances.get(w, None)
                newdist = dv + data['dist']
                if dw is None or newdist[1] < dw[1]:
                    distances[w] = newdist
                    tree.append_child(v, w)
                elif (newdist[1] == dw[1] and newdist[0] - dw[0] > 0):
                    # can't have back edges
                    assert post[w] < post[v]
                    distances[w] = newdist
                    tree.append_child(v, w)

    def compute_node_key(node):
        maxratio, argmax = None, None
        # go over all incoming edges of the node
        for u, v, data in g.in_edges_iter(node, data=True):
            if u in distances:
                delta = distances[u] + data['dist'] - distances[v]
                # print("Delta for {} = {}".format((u, v), delta))
                if delta[1] > 0:
                    ratio = Fraction(delta[0], delta[1])
                    if argmax is None or ratio > maxratio:
                        maxratio = ratio
                        argmax = (u, v)

        # store the node key for v
        if argmax is not None:
            queue[node] = (-maxratio, argmax)
        elif node in queue:
            del queue[node]

        return maxratio

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

    # pivot until cycle is found
    path_changes = 0
    while len(queue) > 0:
        (node, (ratio, (v, w))) = queue.pop()

        delta = distances[v] + g.get_edge_data(v, w)['dist'] - distances[w]
        for j in tree.pre_order(w):
            distances[j] += delta
            if v == j:
                cycle = list()
                if v != w:
                    child = j
                    for p in tree.ancestors(child):
                        cycle.append((p, j))
                        if p == w:
                            break
                        j = p

                    cycle.reverse()

                cycle.append((v, w))
                assert __is_cycle(
                    cycle
                ), "Found wrong cycle after pivoting with back edge ({}, {})".format(
                    v, w)
                return -ratio, cycle

            for (_, k, data) in g.out_edges_iter(j, True):
                # update priority of (j, k)
                ratio_k = None
                if k in queue:
                    (ratio_k, edge) = 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))

            # recompute vertex key of j
            compute_node_key(j)

        tree.append_child(v, w)
    else:
        # no cycle found, any period is admissible
        # Note that this implies that the graph is acyclic
        return None, None
Пример #11
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 = pq.priority_queue()

    # print("Computing MCR for graph {}".format( g.edges( data = True )))
    # determine lower bound for mcr
    # while doing so, determine vertices with outgoing tokenless
    init_mcr = 1
    negative_tokens_or_weights = False
    for (v, w, data) in g.edges_iter(data=True):
        tokens = data.get('tokens', 0)
        weight = data.get('weight', 0)
        init_mcr = init_mcr + max(0, weight)

        data['dist'] = pdistance(weight, tokens)
        if tokens < 0 or weight < 0:
            negative_tokens_or_weights = True

    if estimate is not None:
        # compute initial tree by computing shortest paths tree
        # print("Computing initial tree from estimate {}".format(estimate))
        for (v, w, data) in g.edges_iter(data=True):
            dist = data['dist']
            data['w'] = dist[1] * estimate - dist[0]

        try:
            tree, _ = bfct.find_shortest_paths(g, root, arg='w')
            distances[root] = pdistance(0, 0)
            for v, i in tree.dfs(root):
                if i == 0:
                    dv = distances[v]
                    for w in tree.children(v):
                        distances[w] = dv + g.get_edge_data(v, w).get('dist')
        except NegativeCycleException as ex:
            # print("Infeasible solution for estimate {}, due to cycle {}".format(estimate, [(u, v, data) for u, v, data in g.edges_iter(data = True) if (u, v) in ex.cycle]))
            raise InfeasibleException(ex.cycle)
    elif True:
        # print("Computing initial tree from estimate {}".format(init_mcr))
        # compute initial tree by computing shortest paths tree
        for (v, w, data) in g.edges_iter(data=True):
            dist = data['dist']
            data['w'] = dist[1] * init_mcr - dist[0]

        try:
            tree, _ = bfct.find_shortest_paths(g, root, arg='w')
            distances[root] = pdistance(0, 0)
            for v, i in tree.dfs(root):
                if i == 0:
                    dv = distances[v]
                    for w in tree.children(v):
                        distances[w] = dv + g.get_edge_data(v, w).get('dist')
            if False:
                # verify distances
                for v in distances:
                    distv = distances[v]
                    distv = distv[0] - distv[1] * init_mcr
                    for _, w, data in g.out_edges_iter(v, True):
                        distw = distances[w]
                        distw = distw[0] - distw[1] * init_mcr

                        edge_dist = data['dist']
                        edge_dist = edge_dist[0] - edge_dist[1] * init_mcr

                        assert distv + edge_dist <= distw

        except NegativeCycleException as ex:
            # print("Infeasible solution for estimate {}, due to cycle {}".format(init_mcr, [(u, v, data) for u, v, data in g.edges_iter(data = True) if (u, v) in ex.cycle]))
            raise InfeasibleException(ex.cycle)
    else:
        # create tree rooted at root
        tree = DFSTree(root)

        # run Dijkstra on tokens
        distances[root] = 0
        queue[root] = 0
        while len(queue) > 0:
            v, _ = queue.pop()
            dv = distances[v]
            for _, w, data in g.out_edges_iter(v, True):
                dist = data['dist']
                dw = distances.get(w, dv + dist[1] + 1)
                if dv + dist[1] - dw < 0:
                    distances[w] = dv + dist[1]
                    queue[w] = distances[w]
                    tree.append_child(v, w)

        # DFS on shortest paths DAG
        # (this is the DAG induced by the union of all shortest path trees)
        pre, post, index = dict(), dict(), 0
        post_order = list()
        for v, i in tree.dfs(root):
            if i == 0:
                pre[v] = index
                index += 1
                dv = distances[v]
                for _, w, data in g.out_edges_iter(v, True):
                    tokens = data['dist'][1]
                    dw = distances[w]
                    if dv + tokens == dw and w not in pre:
                        tree.append_child(v, w)
            else:
                post[v] = index
                index += 1
                post_order.append(v)

        # scan in topological order
        distances.clear()
        distances[root] = pdistance(0, 0)
        while post_order:
            v = post_order.pop()
            dv = distances[v]
            for _, w, data in g.out_edges_iter(v, True):
                dist = data['dist']
                dw = distances.get(w, None)
                newdist = dv + data['dist']
                if dw is None or newdist[1] < dw[1]:
                    distances[w] = newdist
                    tree.append_child(v, w)
                elif (newdist[1] == dw[1] and newdist[0] - dw[0] > 0):
                    # check that (v, w) is not a back edge!
                    if post[w] >= post[v]:
                        # back edge, MCR not defined
                        prev = v
                        cycle = list()
                        if v != w:
                            for p in tree.ancestors(v):
                                cycle.append((p, prev))
                                if p == w: break
                                prev = p
                            cycle.reverse()
                        cycle.append((v, w))

                        assert __is_cycle(cycle), "Not a cycle: {}".format(
                            cycle)

                        # print(cycle)
                        # print("Positive cycle found: {}".format( cycle ))
                        raise InfeasibleException(cycle)
                    distances[w] = newdist
                    tree.append_child(v, w)

    def compute_node_key(node):
        maxratio, argmax = None, None
        # go over all incoming edges of the node
        for (u, v, data) in g.in_edges_iter(nbunch=[node], data=True):
            if u in distances:
                delta = distances[u] + data['dist'] - distances[v]
                # print("Delta for {} = {}".format((u, v), delta))
                if delta[1] > 0:
                    ratio = Fraction(delta[0], delta[1])
                    if argmax is None or ratio > maxratio:
                        maxratio = ratio
                        argmax = (u, v)

        # store the node key for v
        if argmax is not None:
            queue[node] = (-maxratio, argmax)
            # print("Vertex key for {} = {}, attained by edge {}".format(node, maxratio, argmax))
        elif node in queue:
            del queue[node]

        return maxratio

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

    # pivot until cycle is found
    path_changes = 0
    pivots = 0
    while len(queue) > 0:
        (node, (ratio, (v, w))) = queue.pop()

        pivots += 1
        delta = distances[v] + g.get_edge_data(v, w)['dist'] - distances[w]
        # print("Pivoting with edge ({}, {}), delta = {}".format(v, w, delta))
        for j in tree.pre_order(w):
            # update parametric distance to j
            distances[j] += delta

            path_changes += 1

            if v == j:
                cycle = list()
                if v != w:
                    child = j
                    for p in tree.ancestors(child):
                        cycle.append((p, j))
                        if p == w:
                            break
                        j = p

                    cycle.reverse()

                cycle.append((v, w))
                assert __is_cycle(
                    cycle
                ), "Found wrong cycle after pivoting with back edge ({}, {})".format(
                    v, w)
                return -ratio, cycle

            for (_, k, data) in g.out_edges_iter(j, True):
                # update priority of (j, k)
                ratio_k = None
                if k in queue:
                    (ratio_k, edge) = queue[k]

                delta_k = distances[j] + data['dist'] - distances[k]
                # commented out line below to allow a negative MCR
                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))
                        # print("New arc key for ({}, {}) = {}, delta = {} + {} - {} = {}".format(j, k, -r, distances[j], data['dist'], distances[k], delta_k))

            # recompute vertex key of j
            compute_node_key(j)

        tree.append_child(v, w)
    else:
        # no cycle found, any period is admissible
        # Note that this implies that the graph is acyclic
        return None, None
Пример #12
0
def path_weight(graph, start, end):
    # compute shortest paths
    _, dists, _ = bfct.find_shortest_paths(graph, start)
    return dists.get(end, None)
Пример #13
0
def test_random(n = 50, p = 0.1, runs = 1000, debug = None):
    for run in range(runs) if debug is None else [debug]:
        g = fast_gnp_random_graph(n, p, seed = run + 1, directed = True)

        # add source connected to all nodes
        source = 100 * (n // 100) + 200
        for v in list(g.nodes()): g.add_edge(source, v, weight = 0, tokens = 0)

        # add random weights and tokens
        wsum = 1
        for _, _, data in g.edges_iter( data = True ):
            data['weight'] = randrange(1, 10)
            data['tokens'] = randrange(-1, 8)
            wsum += data['weight']

        # create shortest path formulation for initial tree
        for _, _, data in g.edges_iter( data = True ):
            data['sp'] = data['tokens'] * wsum - data['weight']

        # ensure that the graph admits a feasible solution
        its = 0
        while True:
            try:
                its += 1
                tree, distances = bfct.find_shortest_paths(g, source, arg = 'sp')
                break
            except NegativeCycleException as ex:
                toks = sum(map(lambda vw : g.get_edge_data(*vw).get('tokens'), ex.cycle))
                edge_data = g.get_edge_data(*choice(ex.cycle))
                edge_data['tokens'] += (1 - toks)
                edge_data['sp'] = edge_data['tokens'] * wsum - edge_data['weight']

        if debug is not None:
            import pdb; pdb.set_trace()

        negative_toks = False
        for _, _, data in g.edges_iter( data = True ):
            if data['tokens'] < 0:
                negative_toks = True; break

        print("Run {}: negative tokens: {}, iterations: {}".format(run, negative_toks, its))

        ratio, cycle = compute_mcr(g, source)
        assert ratio is not None, "Deadlocked cycle found"
        if not cycle:
            # verify that the graph is acyclic
            assert is_directed_acyclic_graph(g), "[run = {}] Graph is not acyclic".format(run)
        else:
            wsum, tsum = 0, 0
            for v, w in cycle:
                data = g.get_edge_data(v, w)
                wsum += data['weight']
                tsum += data['tokens']

            assert Fraction( wsum, tsum ) == ratio, "[run = {}] computed MCR {} does not match ratio of critical cycle {}".format(run, ratio, Fraction(wsum, tsum))

            for v, w, data in g.edges_iter( data = True ):
                data['weight'] = data['tokens'] * ratio - data['weight']

            try:
                bellman_ford(g, source)
            except Exception:
                print("Exception during run {}".format(run))
Пример #14
0
def compute_mcm_component( g, root, estimate = None, pweight = 'weight' ):
    """ Computes the maximum cycle mean 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 = pq.priority_queue()

    # determine lower bound for mcr
    init_mcr = 1
    for (v, w, data) in g.edges_iter(data=True):
        weight = data.get(pweight, 0)
        init_mcr = init_mcr + max(0, weight)
        data['dist'] = pdistance(weight, 1)

    if estimate is not None:
        # compute initial tree by computing shortest paths tree
        # print("Computing initial tree from estimate {}".format(estimate))
        for (v, w, data) in g.edges_iter(data=True):
            dist = data['dist']
            data['w'] = estimate - dist[0]

        try:
            tree, _ = bfct.find_shortest_paths(g, root, arg = 'w')
            distances[root] = pdistance(0, 0)
            for v, i in tree.dfs( root ):
                if i == 0:
                    dv = distances[ v ]
                    for w in tree.children( v ):
                        distances[ w ] = dv + g.get_edge_data(v, w).get('dist')
        except NegativeCycleException as ex:
            # print("Infeasible solution for estimate {}, due to cycle {}".format(estimate, [(u, v, data) for u, v, data in g.edges_iter(data = True) if (u, v) in ex.cycle]))
            raise InfeasibleException( ex.cycle )
    else:
        # create tree rooted at root
        tree = DFSTree( root )

        # run Dijkstra on tokens
        distances[root] = 0
        queue[ root ] = 0
        while len(queue) > 0:
            v, _ = queue.pop()
            dv = distances[ v ]
            for _, w, data in g.out_edges_iter(v, True):
                dw = distances.get(w, None)
                if (dw is None) or dv + 1 < dw:
                    distances[w] = dv + 1
                    queue[ w ] = distances[w]
                    tree.append_child( v, w )

        # DFS on shortest paths DAG
        # (this is the DAG induced by the union of all shortest path trees)
        pre, post, index = dict(), dict(), 0
        post_order = list()
        for v, i in tree.dfs( root ):
            if i == 0:
                pre[v] = index
                index += 1
                dv = distances[v]
                for _, w, data in g.out_edges_iter(v, True):
                    dw = distances[w]
                    if dv + 1 == dw and w not in pre:
                        tree.append_child( v, w )
            else:
                post[v] = index
                index += 1
                post_order.append(v)

        # scan in topological order
        distances.clear()
        distances[root] = pdistance(0, 0)
        while post_order:
            v = post_order.pop()
            dv = distances[v]
            for _, w, data in g.out_edges_iter(v, True):
                dist = data['dist']
                dw = distances.get(w, None)
                newdist = dv + data['dist']
                if dw is None or newdist[1] < dw[1]:
                    distances[w] = newdist
                    tree.append_child( v, w )
                elif (newdist[1] == dw[1] and newdist[0] - dw[0] > 0):
                    # can't have back edges
                    assert post[w] < post[v]
                    distances[w] = newdist
                    tree.append_child( v, w )

    def compute_node_key(node):
        maxratio, argmax = None, None
        # go over all incoming edges of the node
        for u, v, data in g.in_edges_iter( node, data = True ):
            if u in distances:
                delta = distances[u] + data['dist'] - distances[v]
                # print("Delta for {} = {}".format((u, v), delta))
                if delta[1] > 0:
                    ratio = Fraction(delta[0], delta[1])
                    if argmax is None or ratio > maxratio:
                        maxratio = ratio
                        argmax = (u, v)

        # store the node key for v
        if argmax is not None:
            queue[node] = (-maxratio, argmax)
        elif node in queue:
            del queue[node]

        return maxratio


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

    # pivot until cycle is found
    path_changes = 0
    while len(queue) > 0:
        (node, (ratio, (v, w))) = queue.pop()

        delta = distances[v] + g.get_edge_data(v, w)['dist'] - distances[w]
        for j in tree.pre_order(w):
            distances[j] += delta
            if v == j:
                cycle = list()
                if v != w:
                    child = j
                    for p in tree.ancestors(child):
                        cycle.append( (p, j) )
                        if p == w:
                            break
                        j = p

                    cycle.reverse()

                cycle.append( (v, w) )
                assert __is_cycle( cycle ), "Found wrong cycle after pivoting with back edge ({}, {})".format( v, w )
                return -ratio, cycle
            
            for (_, k, data) in g.out_edges_iter(j, True):
                # update priority of (j, k)
                ratio_k = None
                if k in queue:
                    (ratio_k, edge) = 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))

            # recompute vertex key of j
            compute_node_key(j)

        tree.append_child( v, w )
    else:
        # no cycle found, any period is admissible
        # Note that this implies that the graph is acyclic
        return None, None
Пример #15
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 = pq.priority_queue()

    # print("Computing MCR for graph {}".format( g.edges( data = True )))
    # determine lower bound for mcr
    # while doing so, determine vertices with outgoing tokenless 
    init_mcr = 1
    negative_tokens_or_weights = False
    for (v, w, data) in g.edges_iter(data=True):
        tokens = data.get('tokens', 0)
        weight = data.get('weight', 0)
        init_mcr = init_mcr + max(0, weight)

        data['dist'] = pdistance(weight, tokens)
        if tokens < 0 or weight < 0:
            negative_tokens_or_weights = True

    if estimate is not None:
        # compute initial tree by computing shortest paths tree
        # print("Computing initial tree from estimate {}".format(estimate))
        for (v, w, data) in g.edges_iter(data=True):
            dist = data['dist']
            data['w'] = dist[1] * estimate - dist[0]

        try:
            tree, _ = bfct.find_shortest_paths(g, root, arg = 'w')
            distances[root] = pdistance(0, 0)
            for v, i in tree.dfs( root ):
                if i == 0:
                    dv = distances[ v ]
                    for w in tree.children( v ):
                        distances[ w ] = dv + g.get_edge_data(v, w).get('dist')
        except NegativeCycleException as ex:
            # print("Infeasible solution for estimate {}, due to cycle {}".format(estimate, [(u, v, data) for u, v, data in g.edges_iter(data = True) if (u, v) in ex.cycle]))
            raise InfeasibleException( ex.cycle )
    elif True:
        # print("Computing initial tree from estimate {}".format(init_mcr))
        # compute initial tree by computing shortest paths tree
        for (v, w, data) in g.edges_iter(data=True):
            dist = data['dist']
            data['w'] = dist[1] * init_mcr - dist[0]

        try:
            tree, _ = bfct.find_shortest_paths(g, root, arg = 'w')
            distances[root] = pdistance(0, 0)
            for v, i in tree.dfs( root ):
                if i == 0:
                    dv = distances[ v ]
                    for w in tree.children( v ):
                        distances[ w ] = dv + g.get_edge_data(v, w).get('dist')
            if False:
                # verify distances
                for v in distances:
                    distv = distances[v]
                    distv = distv[0] - distv[1] * init_mcr
                    for _, w, data in g.out_edges_iter( v, True ):
                        distw = distances[w]
                        distw = distw[0] - distw[1] * init_mcr

                        edge_dist = data['dist']
                        edge_dist = edge_dist[0] - edge_dist[1] * init_mcr

                        assert distv + edge_dist <= distw

        except NegativeCycleException as ex:
            # print("Infeasible solution for estimate {}, due to cycle {}".format(init_mcr, [(u, v, data) for u, v, data in g.edges_iter(data = True) if (u, v) in ex.cycle]))
            raise InfeasibleException( ex.cycle )
    else:
        # create tree rooted at root
        tree = DFSTree( root )

        # run Dijkstra on tokens
        distances[root] = 0
        queue[ root ] = 0
        while len(queue) > 0:
            v, _ = queue.pop()
            dv = distances[ v ]
            for _, w, data in g.out_edges_iter(v, True):
                dist = data['dist']
                dw = distances.get(w, dv + dist[1] + 1)
                if dv + dist[1] - dw < 0:
                    distances[w] = dv + dist[1]
                    queue[ w ] = distances[w]
                    tree.append_child( v, w )

        # DFS on shortest paths DAG
        # (this is the DAG induced by the union of all shortest path trees)
        pre, post, index = dict(), dict(), 0
        post_order = list()
        for v, i in tree.dfs( root ):
            if i == 0:
                pre[v] = index
                index += 1
                dv = distances[v]
                for _, w, data in g.out_edges_iter(v, True):
                    tokens = data['dist'][1]
                    dw = distances[w]
                    if dv + tokens == dw and w not in pre:
                        tree.append_child( v, w )
            else:
                post[v] = index
                index += 1
                post_order.append(v)

        # scan in topological order
        distances.clear()
        distances[root] = pdistance(0, 0)
        while post_order:
            v = post_order.pop()
            dv = distances[v]
            for _, w, data in g.out_edges_iter(v, True):
                dist = data['dist']
                dw = distances.get(w, None)
                newdist = dv + data['dist']
                if dw is None or newdist[1] < dw[1]:
                    distances[w] = newdist
                    tree.append_child( v, w )
                elif (newdist[1] == dw[1] and newdist[0] - dw[0] > 0):
                    # check that (v, w) is not a back edge!
                    if post[w] >= post[v]:
                        # back edge, MCR not defined
                        prev = v
                        cycle = list()
                        if v != w:
                            for p in tree.ancestors(v):
                                cycle.append( (p, prev) )
                                if p == w: break
                                prev = p
                            cycle.reverse()
                        cycle.append( (v, w) )

                        assert __is_cycle( cycle ), "Not a cycle: {}".format( cycle )

                        # print(cycle)
                        # print("Positive cycle found: {}".format( cycle ))
                        raise InfeasibleException(cycle)
                    distances[w] = newdist
                    tree.append_child( v, w )

    def compute_node_key(node):
        maxratio, argmax = None, None
        # go over all incoming edges of the node
        for (u, v, data) in g.in_edges_iter(nbunch=[node], data=True):
            if u in distances:
                delta = distances[u] + data['dist'] - distances[v]
                # print("Delta for {} = {}".format((u, v), delta))
                if delta[1] > 0:
                    ratio = Fraction(delta[0], delta[1])
                    if argmax is None or ratio > maxratio:
                        maxratio = ratio
                        argmax = (u, v)

        # store the node key for v
        if argmax is not None:
            queue[node] = (-maxratio, argmax)
            # print("Vertex key for {} = {}, attained by edge {}".format(node, maxratio, argmax))
        elif node in queue:
            del queue[node]

        return maxratio


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

    # pivot until cycle is found
    path_changes = 0
    pivots = 0
    while len(queue) > 0:
        (node, (ratio, (v, w))) = queue.pop()

        pivots += 1
        delta = distances[v] + g.get_edge_data(v, w)['dist'] - distances[w]
        # print("Pivoting with edge ({}, {}), delta = {}".format(v, w, delta))
        for j in tree.pre_order(w):
            # update parametric distance to j
            distances[j] += delta

            path_changes += 1

            if v == j:
                cycle = list()
                if v != w:
                    child = j
                    for p in tree.ancestors(child):
                        cycle.append( (p, j) )
                        if p == w:
                            break
                        j = p

                    cycle.reverse()

                cycle.append( (v, w) )
                assert __is_cycle( cycle ), "Found wrong cycle after pivoting with back edge ({}, {})".format( v, w )
                return -ratio, cycle
            
            for (_, k, data) in g.out_edges_iter(j, True):
                # update priority of (j, k)
                ratio_k = None
                if k in queue:
                    (ratio_k, edge) = queue[k]

                delta_k = distances[j] + data['dist'] - distances[k]
                # commented out line below to allow a negative MCR
                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))
                        # print("New arc key for ({}, {}) = {}, delta = {} + {} - {} = {}".format(j, k, -r, distances[j], data['dist'], distances[k], delta_k))

            # recompute vertex key of j
            compute_node_key(j)

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