Exemple #1
0
def MCCFRobustInstance( network, capacity, supply, cost, U ) :
    """
    this wrapper transforms any convex cost flow instance into an equivalent one for which
    every Delta-residual graph is strongly connected
    """
    network_aug = mygraph()
    capacity_rename = {}
    cost_aug = {}
    
    for e in network.edges() :
        i,j = network.endpoints(e)
        newedge = (ALGGLOBAL.REGULAR,e)
        
        network_aug.add_edge( newedge, i, j )
        if e in capacity : capacity_rename[ newedge ] = capacity[e]
        if e in cost : cost_aug[ newedge ] = cost[e]
        
    # add a directed cycle, with prohibitive cost
    CBOUND = sum([ c(U) for c in cost.values() ])
    # since costs are convex, a feasible flow cannot have cost greater than CBOUND
    prohibit = line(CBOUND)
    
    NODES = network.nodes()
    edgegen = itertools.count()
    for i,j in zip( NODES, NODES[1:] + NODES[:1] ) :
        frwd = (ALGGLOBAL.AUGMENTING, edgegen.next() )
        network_aug.add_edge( frwd, i, j )
        cost_aug[frwd] = prohibit
        
    return network_aug, capacity_rename, cost_aug
def SOLVER(roadnet, surplus, objectives):
    network = mygraph()
    capacity = {}
    supply = {i: 0. for i in roadnet.nodes()}
    cost = {}  # functions
    #
    oneway_offset = {}  # for one-way roads

    for i, j, road, data in roadnet.edges_iter(keys=True, data=True):
        supply[j] += surplus[road]
        cost_data = objectives[road]

        # edge construction
        if data.get('oneway', False):
            # if one-way road

            # record minimum allowable flow on road
            zmin = cost_data.max_key()
            oneway_offset[road] = zmin
            supply[i] -= zmin
            supply[j] += zmin

            # shift and record the cost function on a forward edge only
            cc = roadbm.costWrapper(cost_data)
            cc_offset = offsetWrapper(cc, zmin)
            network.add_edge(road, i, j)
            cost[road] = cc_offset

        else:
            # if bi-directional road... currently, instantiate two edges
            cc = roadbm.costWrapper(cost_data)
            ncc = negativeWrapper(
                cc)  # won't have to worry about the C(0) offset

            network.add_edge((road, +1), i, j)
            cost[(road, +1)] = cc
            #
            network.add_edge((road, -1), j, i)
            cost[(road, -1)] = ncc

    # we need to compute the size U of the first cvxcost algorithm phase
    #U = sum([ len( obj_dict ) for obj_dict in objectives.values() ])
    # below is almost certainly just as good a bound, but I'm a scaredy-cat
    U = sum([len(obj_dict) - 2 for obj_dict in objectives.values()])

    f = MinConvexCostFlow(network, {}, supply, cost, U)

    flow = {}
    for i, j, road in roadnet.edges_iter(keys=True):
        if road in oneway_offset:
            flow[road] = f[road] + oneway_offset[road]
        else:
            flow[road] = f[(road, +1)] - f[(road, -1)]

        flow[road] = int(flow[road])

    #print flow
    return flow
def SOLVER(roadnet, surplus, objectives):
    network = mygraph()
    capacity = {}
    supply = {i: 0.0 for i in roadnet.nodes()}
    cost = {}  # functions
    #
    oneway_offset = {}  # for one-way roads

    for i, j, road, data in roadnet.edges_iter(keys=True, data=True):
        supply[j] += surplus[road]
        cost_data = objectives[road]

        # edge construction
        if data.get("oneway", False):
            # if one-way road

            # record minimum allowable flow on road
            zmin = cost_data.max_key()
            oneway_offset[road] = zmin
            supply[i] -= zmin
            supply[j] += zmin

            # shift and record the cost function on a forward edge only
            cc = roadbm.costWrapper(cost_data)
            cc_offset = offsetWrapper(cc, zmin)
            network.add_edge(road, i, j)
            cost[road] = cc_offset

        else:
            # if bi-directional road... currently, instantiate two edges
            cc = roadbm.costWrapper(cost_data)
            ncc = negativeWrapper(cc)  # won't have to worry about the C(0) offset

            network.add_edge((road, +1), i, j)
            cost[(road, +1)] = cc
            #
            network.add_edge((road, -1), j, i)
            cost[(road, -1)] = ncc

    # we need to compute the size U of the first cvxcost algorithm phase
    # U = sum([ len( obj_dict ) for obj_dict in objectives.values() ])
    # below is almost certainly just as good a bound, but I'm a scaredy-cat
    U = sum([len(obj_dict) - 2 for obj_dict in objectives.values()])

    f = MinConvexCostFlow(network, {}, supply, cost, U)

    flow = {}
    for i, j, road in roadnet.edges_iter(keys=True):
        if road in oneway_offset:
            flow[road] = f[road] + oneway_offset[road]
        else:
            flow[road] = f[(road, +1)] - f[(road, -1)]

        flow[road] = int(flow[road])

    # print flow
    return flow
Exemple #4
0
def SOLVER( roadnet, surplus, measure_dict, congestion_dict ) :
    from setiptah.nxopt.cvxcostflow import MinConvexCostFlow
    from setiptah.basic_graph.mygraph import mygraph
    
    # a rather crucial measure of the problem's complexity;
    # see bm.SOLVER for relevant commentary
    U = sum([ len(m) - 1 for m in measure_dict.values() ]) 

    # instantiate cvxcostflow components    
    network = mygraph()
    capacity = {}
    supply = { i : 0. for i in roadnet.nodes() }
    cost = {}   # functions
    #
    oneway_offset = {}  # to process one-way roads
    
    for i,j, road, data in roadnet.edges_iter( keys=True, data=True ) :
        supply[j] += surplus[road]
        measure = measure_dict[road]
        rho = congestion_dict[road]
                
        fobj = CONGESTION_OBJECTIVE( measure, rho, U )  # U, here, restricts domain
        
        # edge construction
        if data.get( 'oneway', False ) :
            # if one-way road
            
            # record minimum allowable flow on road
            zmin = -measure.min_key()   # i.e., z + min key of measure >= 0 
            oneway_offset[road] = zmin
            # create a 'bias point'
            supply[i] -= zmin
            supply[j] += zmin
            
            # shift and record the cost function on only a forward edge
            fobj_offset = roadbm.offsetWrapper( fobj, zmin )
            network.add_edge( road, i, j )
            cost[ road ] = fobj_offset
            
        else :
            # if bi-directional road... instantiate pair of edges
            #cc = roadbm.costWrapper( cost_data )
            n_fobj = roadbm.negativeWrapper( fobj )     # won't have to worry about the C(0) offset
            
            network.add_edge( (road,+1), i, j )
            cost[ (road,+1) ] = fobj
            #
            network.add_edge( (road,-1), j, i )
            cost[ (road,-1) ] = n_fobj

    f = MinConvexCostFlow( network, {}, supply, cost, U )   # U, here, determines phase count
    
    flow = {}
    for i, j, road in roadnet.edges_iter( keys=True ) :
        if road in oneway_offset :
            flow[road] = f[road] + oneway_offset[road]
        else :
            flow[road] = f[(road,+1)] - f[(road,-1)]
            
        flow[road] = int( flow[road] )
    
    return flow
def SOLVER(roadnet, surplus, measure_dict):
    network = mygraph()
    capacity = {}
    supply = {i: 0. for i in roadnet.nodes()}
    cost = {}  # functions
    #
    oneway_offset = {}  # for one-way roads

    for i, j, road, data in roadnet.edges_iter(keys=True, data=True):
        supply[j] += surplus[road]
        measure = measure_dict[road]

        fobj = OBJECTIVE_FUNC(measure)

        # edge construction
        if data.get('oneway', False):
            # if one-way road

            # record minimum allowable flow on road
            zmin = -measure.min_key()  # i.e., z + min key of measure >= 0
            oneway_offset[road] = zmin
            # create a 'bias point'
            supply[i] -= zmin
            supply[j] += zmin

            # shift and record the cost function on only a forward edge
            fobj_offset = offsetWrapper(fobj, zmin)
            network.add_edge(road, i, j)
            cost[road] = fobj_offset

        else:
            # if bi-directional road... instantiate pair of edges
            #cc = roadbm.costWrapper( cost_data )
            n_fobj = negativeWrapper(
                fobj)  # won't have to worry about the C(0) offset

            network.add_edge((road, +1), i, j)
            cost[(road, +1)] = fobj
            #
            network.add_edge((road, -1), j, i)
            cost[(road, -1)] = n_fobj
    """
    compute the width U of the first cvxcost algorithm phase;
    a bound on the optimal flow on any edge; 
    Logic: there cannot be more flow on a given road in the graph
    than there are total intervals between levels in the network
    (Proof Sketch):
    1. U <= M ;
    2. (Prove...) Given any matching instance which
        induces a measure network w/ U' total intervals between levels,
        a new matching instance realizing the same measure network can be constructed
        on just U' points in each set
    """
    # safe-ish...
    #U = sum([ len(m) + 1 for m in measure_dict.values() ])
    # below is almost certainly just as good a bound, but I'm a scaredy-cat
    U = sum([len(m) - 1 for m in measure_dict.values()])

    f = MinConvexCostFlow(network, {}, supply, cost, U)

    flow = {}
    for i, j, road in roadnet.edges_iter(keys=True):
        if road in oneway_offset:
            flow[road] = f[road] + oneway_offset[road]
        else:
            flow[road] = f[(road, +1)] - f[(road, -1)]

        flow[road] = int(flow[road])

    #print flow
    return flow
Exemple #6
0
def FragileMCCF( network, capacity_in, supply, cost, U, epsilon=None ) :
    """
    network is a mygraph (above)
    capacity is a dictionary from E -> real capacities
    supply is a dictionary from E -> real supplies
    cost is a dictionary from E -> lambda functions of convex cost edge costs
    
    1. Assumes supply is conservative (sum to zero).
    2. Assumes every Delta-residual graph is strongly connected,
    i.e., there exists a path with inf capacity b/w any two nodes;
    """
    if epsilon is None : epsilon = 1
    
    # initialize algorithm data
    rgraph = mygraph()
    lincost = {}
    redcost = {}
    excess = {}
    
    """ ALGORITHM """
    # computing U from supplies was wrong, it must be passed in now
    #U = sum([ b for b in supply.values() if b > 0. ])
    #print 'total supply: %f' % U
    
    # trimming infinite capacities to U allows negative initial slopes 
    # the initial flow may not be Delta-optimal at the beginning of Stage One,
    # but achieves Delta-optimality by the end, by saturating any negative cost edges.
    # most treatments fail to consider negative initial slope, which is totally possible... 
    capacity = {}
    for e in network.edges() :
        capacity[e] = min( U, capacity_in.get( e, np.Inf ) )
        
    try :
        temp = math.floor( math.log(U,2) )
    except Exception as ex :
        ex.U = U
        raise ex
        
    Delta = 2.**temp
    print 'Delta: %d' % Delta
    
    
    flow = { e : 0. for e in network.edges() }
    Excess( excess, flow, network, supply )
    
    potential = { i : 0. for i in network.nodes() }
        
    while Delta >= epsilon :
        print '\nnew phase: Delta=%f' % Delta
        
        # Delta is fresh, so we need to [re-] linearize the costs and compute residual graph 
        LinearizeCost( lincost, cost, flow, Delta, network )
        ReducedCost( redcost, lincost, potential, network )
        ResidualGraph( rgraph, flow, capacity, Delta, network )
        #
        cert = { re : c for (re,c) in redcost.iteritems() if re in rgraph.edges() }
        print 'reduced costs on res. graph, phase init: %s' % repr( cert )
        
        """ Stage 1. """
        # for every arc (i,j) in the residual network G(x)
        for resedge in rgraph.edges() :
            e,dir = resedge
            # theory says, we only need to do this at most once per edge...
            # wouldn't want to question theory
            # ... keep an eye out for a flip-flop; in theory, shouldn't happen
            if redcost[resedge] < 0. :
                print 'correcting negative red. cost on resedge %s: %f' % ( resedge, redcost[resedge] )
                
                # no augment, just saturate!
                flow[e] += dir * Delta
                Excess( excess, flow, network, supply, edge=e )
                #print 'flow correction: %s' % repr( flow )
                
                LinearizeCost( lincost, cost, flow, Delta, network, edge=e )
                ResidualGraph( rgraph, flow, capacity, Delta, network, edge=e )
                ReducedCost( redcost, lincost, potential, network, edge=e )
                
        # at end of each stage, verify the optimality certificate (should be empty every time)
        CERT = { re : c for (re,c) in redcost.iteritems() if re in rgraph.edges() and c < 0. }
        print 'certificate, end stage ONE: %s' % repr( CERT )
        #if len( CERT ) > 0 : print "STAGE ONE CERTIFICATE CORRUPT!"
        # am considering removing this assertion, but leaving the stage two one
        # could be running into problems where the functional form is defined beyond saturation bounds
        RELAXCERT = { re : c for (re,c) in redcost.iteritems() if re in rgraph.edges() and c < -PHASE_ERROR }
        if len( RELAXCERT ) > 0 : 
            print RELAXCERT
            print "STAGE ONE CERTIFICATE CORRUPT!"
        print RELAXCERT
        assert len( RELAXCERT ) <= 0
        
                
        """ Stage 2. """
        # while there are imbalanced nodes
        while True :
            print 'flow: %s' % repr( flow )
            #excess = Excess( flow, network, supply )        # last function that needs to be increment-ized
            #print 'excess: %s' % repr(excess)
            
            SS = [ i for i,ex in excess.iteritems() if ex >= Delta ]
            TT = [ i for i,ex in excess.iteritems() if ex <= -Delta ]
            print 'surplus nodes: %s' % repr( SS )
            print 'deficit nodes: %s' % repr( TT )
            if len( SS ) <= 0 or len( TT ) <= 0 : break
            
            s = SS[0] ; t = TT[0]
            print 'shall augment %s to %s' % ( repr(s), repr(t) )
            
            #print 'potentials: %s' % repr( potential )
            cert = { re : c for (re,c) in redcost.iteritems() if re in rgraph.edges() }
            #print 'reduced costs on res. graph, for shortest paths: %s' % repr( cert )
            
            dist, upstream = Dijkstra( rgraph, redcost, s )
            #print 'Dijkstra shortest path distances: %s' % repr( dist )
            #print 'Dijkstra upstreams: %s' % repr( upstream )
            
            # find shortest path w.r.t. reduced costs (just follow ancestry links to the root)
            try :
                PATH = [] ; j = t
                while j != s :      # previously was "is"... that created problems non-deterministically
                    e = upstream[j]
                    i,_ = rgraph.endpoints(e)
                    PATH.insert( 0, e )
                    j = i
                    
            except Exception as e :
                e.rgraph = rgraph
                e.redcost = redcost
                e.s = s
                
                e.path_so_far = PATH
                e.j = j
                
                raise e
                    
            print 'using path: %s' % repr( PATH )
            
            # augment Delta flow along the path P
            for e,dir in PATH :
                flow[e] += dir * Delta
                Excess( excess, flow, network, supply, edge=e )
                
                LinearizeCost( lincost, cost, flow, Delta, network, edge=e )    # all edges
                ResidualGraph( rgraph, flow, capacity, Delta, network, edge=e )
                
            # update the potentials; 
            # by connectivity, should touch *every* node
            for i in network.nodes() : potential[i] -= dist[i]
            
            # re-compute the reduced costs... everywhere? (all the potentials have changed)
            ReducedCost( redcost, lincost, potential, network )
            
            
        # at end of each stage, verify the optimality certificate (should be empty every time)
        CERT = { re : c for (re,c) in redcost.iteritems() if re in rgraph.edges() and c < 0. }
        print 'certificate, end stage TWO: %s' % repr( CERT )
        RELAXCERT = { re : c for (re,c) in redcost.iteritems() if re in rgraph.edges() and c < -PHASE_ERROR }
        if len( RELAXCERT ) > 0 :
            print RELAXCERT
            print "STAGE TWO CERTIFICATE CORRUPT!"
        assert len( RELAXCERT ) <= 0
                    
        # end the phase
        if Delta <= epsilon : break
        Delta = Delta / 2
    
    return flow
Exemple #7
0
 """
 convert linear instances on non-multi graphs to networkx format
 for comparison against nx.min_cost_flow() algorithm
 """
 def mincostflow_nx( network, capacity, supply, weight ) :
     digraph = nx.DiGraph()
     for i in network.nodes() :
         digraph.add_node( i, demand=-supply.get(i, 0. ) )
         
     for e in network.edges() :
         i,j = network.endpoints(e)
         digraph.add_edge( i, j, capacity=capacity.get(e, np.Inf ), weight=weight.get(e, 1. ) )
     return digraph
 
 
 g = mygraph()
 
 if False :
     g.add_edge( 'a', 0, 1 )
     g.add_edge( 'b', 1, 2 )
     g.add_edge( 'c', 2, 3 )
     g.add_edge( 'd', 3, 0 )
     
     u = { e : 10. for e in g.edges() }
     supply = { 0 : 1., 1 : 2., 2 : -3., 3 : 0. }
     c = { 'a' : 10., 'b' : 5., 'c' : 1., 'd' : .5 }
 else :
     u = {}
     c = {}
     s = {}
     
def SOLVER( roadnet, surplus, measure_dict ) :
    network = mygraph()
    capacity = {}
    supply = { i : 0. for i in roadnet.nodes() }
    cost = {}   # functions
    #
    oneway_offset = {}  # for one-way roads
    
    for i,j, road, data in roadnet.edges_iter( keys=True, data=True ) :
        supply[j] += surplus[road]
        measure = measure_dict[road]
        
        fobj = OBJECTIVE_FUNC( measure )
        
        # edge construction
        if data.get( 'oneway', False ) :
            # if one-way road
            
            # record minimum allowable flow on road
            zmin = -measure.min_key()   # i.e., z + min key of measure >= 0 
            oneway_offset[road] = zmin
            # create a 'bias point'
            supply[i] -= zmin
            supply[j] += zmin
            
            # shift and record the cost function on only a forward edge
            fobj_offset = offsetWrapper( fobj, zmin )
            network.add_edge( road, i, j )
            cost[ road ] = fobj_offset
            
        else :
            # if bi-directional road... instantiate pair of edges
            #cc = roadbm.costWrapper( cost_data )
            n_fobj = negativeWrapper( fobj )     # won't have to worry about the C(0) offset
            
            network.add_edge( (road,+1), i, j )
            cost[ (road,+1) ] = fobj
            #
            network.add_edge( (road,-1), j, i )
            cost[ (road,-1) ] = n_fobj

    """
    compute the width U of the first cvxcost algorithm phase;
    a bound on the optimal flow on any edge; 
    Logic: there cannot be more flow on a given road in the graph
    than there are total intervals between levels in the network
    (Proof Sketch):
    1. U <= M ;
    2. (Prove...) Given any matching instance which
        induces a measure network w/ U' total intervals between levels,
        a new matching instance realizing the same measure network can be constructed
        on just U' points in each set
    """
    # safe-ish...
    #U = sum([ len(m) + 1 for m in measure_dict.values() ])
    # below is almost certainly just as good a bound, but I'm a scaredy-cat
    U = sum([ len(m) - 1 for m in measure_dict.values() ])
    
    f = MinConvexCostFlow( network, {}, supply, cost, U )
    
    flow = {}
    for i, j, road in roadnet.edges_iter( keys=True ) :
        if road in oneway_offset :
            flow[road] = f[road] + oneway_offset[road]
        else :
            flow[road] = f[(road,+1)] - f[(road,-1)]
            
        flow[road] = int( flow[road] )
    
    #print flow
    return flow