Beispiel #1
0
def fw_heterogeneous_2(graphs,
                       demands,
                       past=10,
                       max_iter=100,
                       eps=1e-8,
                       q=50,
                       display=0,
                       stop=1e-8):
    '''
    Frank-Wolfe algorithm on the heterogeneous game
    given a list of graphs in the format
    g = [[link_id from to a0 a1 a2 a3 a4]]
    and demand in the format
    d = [[o d flow]]
    '''
    assert past <= q, "'q' must be bigger or equal to 'past'"
    # construct graph and demand objects suiteable for AoN_igraph
    gs = [construct_igraph(graph) for graph in graphs]
    ods = [construct_od(demand) for demand in demands]
    # construct empty vector to be filled in with values
    links = graphs[0].shape[0]
    types = len(graphs)
    # initial flow assignment is null
    f = np.zeros(links * types, dtype="float64")
    fs = np.zeros((links * types, past), dtype="float64")
    L = np.zeros(links * types, dtype="float64")
    grad = np.zeros(links * types, dtype="float64")
    L2 = np.zeros(links * types, dtype="float64")
    grad2 = np.zeros(links * types, dtype="float64")
    error = 'N/A'
    # compute re-normalization constant
    K = sum([total_free_flow_cost(g, od) for g, od in zip(gs, ods)])
    if K < eps:
        K = sum([np.sum(demand[:, 2]) for demand in demands])
    elif display >= 1:
        print 'average free-flow travel time', \
            K / sum([np.sum(demand[:, 2]) for demand in demands])
    # compute iterations
    for i in range(max_iter):
        if display >= 1:
            print 'iteration: {}, error: {}'.format(i + 1, error)
        # construct weighted graph with latest flow assignment
        # print 'f', f
        # print 'reshape', np.reshape(f,(links,types))
        total_f = np.sum(np.reshape(f, (types, links)).T, 1)
        # print 'total flow', total_f
        for j, (graph, g, od) in enumerate(zip(graphs, gs, ods)):
            l, gr = search_direction(total_f, graph, g, od)
            L[(j * links):((j + 1) * links)] = l
            grad[(j * links):((j + 1) * links)] = gr
        # print 'L', L
        # print 'grad', grad
        fs[:, i % past] = L
        w = L - f
        if i >= 1:
            error = -grad.dot(w) / K
            # if error < stop and error > 0.0:
            if error < stop:
                if display >= 1:
                    print 'stop with error: {}'.format(error)
                return np.reshape(f, (types, links)).T
        if i > q:
            # step 3 of Fukushima
            v = np.sum(fs, axis=1) / min(past, i + 1) - f
            norm_v = np.linalg.norm(v, 1)
            if norm_v < eps:
                if display >= 1:
                    print 'stop with norm_v: {}'.format(norm_v)
                return np.reshape(f, (types, links)).T
            norm_w = np.linalg.norm(w, 1)
            if norm_w < eps:
                if display >= 1:
                    print 'stop with norm_w: {}'.format(norm_w)
                return np.reshape(f, (types, links)).T
            # step 4 of Fukushima
            gamma_1 = grad.dot(v) / norm_v
            gamma_2 = grad.dot(w) / norm_w
            if gamma_2 > -eps:
                if display >= 1:
                    print 'stop with gamma_2: {}'.format(gamma_2)
                return np.reshape(f, (types, links)).T
            d = v if gamma_1 < gamma_2 else w
            # step 5 of Fukushima
            s = line_search(
                lambda a: merit(f + a * d, graphs, gs, ods, L2, grad2))
            # print 'step', s
            if s < eps:
                if display >= 1:
                    print 'stop with step_size: {}'.format(s)
                return np.reshape(f, (types, links)).T
            f = f + s * d
        else:
            f = f + 2. * w / (i + 2.)
    return np.reshape(f, (types, links)).T
 def test_line_search(self):
     def f(x):
         return (x-0.3)**2
     out = line_search(f)
     self.assertTrue(np.linalg.norm(out - .3) < 1e-5)
def fw_heterogeneous_2(graphs, demands, past=10, max_iter=100, eps=1e-8, q=50, \
    display=0, stop=1e-8):
    '''
    Frank-Wolfe algorithm on the heterogeneous game
    given a list of graphs in the format 
    g = [[link_id from to a0 a1 a2 a3 a4]]
    and demand in the format
    d = [[o d flow]]
    '''
    assert past <= q, "'q' must be bigger or equal to 'past'"
    # construct graph and demand objects suiteable for AoN_igraph 
    gs = [construct_igraph(graph) for graph in graphs]
    ods = [construct_od(demand) for demand in demands]
    # construct empty vector to be filled in with values
    links = graphs[0].shape[0]
    types = len(graphs)
    f = np.zeros(links*types,dtype="float64") # initial flow assignment is null
    fs = np.zeros((links*types,past),dtype="float64")
    L = np.zeros(links*types,dtype="float64")
    grad = np.zeros(links*types,dtype="float64")
    L2 = np.zeros(links*types,dtype="float64")
    grad2 = np.zeros(links*types,dtype="float64")
    # compute re-normalization constant
    K = sum([total_free_flow_cost(g, od) for g,od in zip(gs, ods)])
    if K < eps:
        K = sum([np.sum(demand[:,2]) for demand in demands])
    elif display >= 1:
        print 'average free-flow travel time', \
            K / sum([np.sum(demand[:,2]) for demand in demands])
    # compute iterations
    for i in range(max_iter):
        if display >= 1:
            if i <= 1:
                print 'iteration: {}'.format(i+1)
            else:            
                print 'iteration: {}, error: {}'.format(i+1, error)
        # construct weighted graph with latest flow assignment
        #print 'f', f
        #print 'reshape', np.reshape(f,(links,types))
        total_f = np.sum(np.reshape(f,(types,links)).T,1)
        #print 'total flow', total_f
        for j, (graph, g, od) in enumerate(zip(graphs, gs, ods)):
            l, gr = search_direction(total_f, graph, g, od)
            L[(j*links) : ((j+1)*links)] = l
            grad[(j*links) : ((j+1)*links)] = gr
        #print 'L', L
        #print 'grad', grad
        fs[:,i%past] = L
        w = L - f
        if i >= 1:
            error = -grad.dot(w) / K
            # if error < stop and error > 0.0:
            if error < stop:
                if display >= 1: print 'stop with error: {}'.format(error)
                return np.reshape(f,(types,links)).T
        if i > q:
            # step 3 of Fukushima
            v = np.sum(fs,axis=1) / min(past,i+1) - f
            norm_v = np.linalg.norm(v,1)
            if norm_v < eps: 
                if display >= 1: print 'stop with norm_v: {}'.format(norm_v)
                return np.reshape(f,(types,links)).T
            norm_w = np.linalg.norm(w,1)
            if norm_w < eps: 
                if display >= 1: print 'stop with norm_w: {}'.format(norm_w)
                return np.reshape(f,(types,links)).T
            # step 4 of Fukushima
            gamma_1 = grad.dot(v) / norm_v
            gamma_2 = grad.dot(w) / norm_w
            if gamma_2 > -eps: 
                if display >= 1: print 'stop with gamma_2: {}'.format(gamma_2)
                return np.reshape(f,(types,links)).T
            d = v if gamma_1 < gamma_2 else w
            # step 5 of Fukushima
            s = line_search(lambda a: merit(f+a*d, graphs, gs, ods, L2, grad2))
            # print 'step', s
            if s < eps: 
                if display >= 1: print 'stop with step_size: {}'.format(s)
                return np.reshape(f,(types,links)).T
            f = f + s*d
        else:
            f = f + 2. * w/(i+2.)
    return np.reshape(f,(types,links)).T
Beispiel #4
0
def fw_heterogeneous_2(graphs, demands, past=10, max_iter=100, eps=1e-8, q=50, \
    display=0, stop=1e-8):
    '''
    Frank-Wolfe algorithm on the heterogeneous game
    given a list of graphs in the format 
    g = [[link_id from to a0 a1 a2 a3 a4]]
    and demand in the format
    d = [[o d flow]]
    '''
    assert past <= q, "'q' must be bigger or equal to 'past'"
    # construct graph and demand objects suiteable for AoN_igraph
    gs = [construct_igraph(graph) for graph in graphs]
    ods = [construct_od(demand) for demand in demands]
    # construct empty vector to be filled in with values
    links = graphs[0].shape[0]
    types = len(graphs)
    f = np.zeros(links * types,
                 dtype="float64")  # initial flow assignment is null
    fs = np.zeros((links * types, past), dtype="float64")
    L = np.zeros(links * types, dtype="float64")
    grad = np.zeros(links * types, dtype="float64")
    L2 = np.zeros(links * types, dtype="float64")
    grad2 = np.zeros(links * types, dtype="float64")
    h = defaultdict(np.float64)  # initial path flow assignment is null
    hs = defaultdict(
        lambda: [0.
                 for _ in range(past)])  # initial path flow assignment is null
    # compute re-normalization constant
    K = sum([total_free_flow_cost(g, od) for g, od in zip(gs, ods)])
    if K < eps:
        K = sum([np.sum(demand[:, 2]) for demand in demands])
    elif display >= 1:
        print 'average free-flow travel time', \
            K / sum([np.sum(demand[:,2]) for demand in demands])
    # compute iterations
    start_time = timeit.default_timer()
    for i in range(max_iter):
        if display >= 1:
            if i <= 1:
                print 'iteration: {}'.format(i + 1)
            else:
                print 'iteration: {}, error: {}'.format(i + 1, error)
        # construct weighted graph with latest flow assignment
        #print 'f', f
        #print 'reshape', np.reshape(f,(links,types))
        total_f = np.sum(np.reshape(f, (types, links)).T, 1)
        #print 'total flow', total_f
        L, grad, path_flows = search_direction_multi(f, graphs, gs, ods, L,
                                                     grad)
        #print 'L', L
        #print 'grad', grad
        fs[:, i % past] = L
        for k in set(h.keys()).union(set(path_flows.keys())):
            hs[k][i % past] = path_flows[k]
        w = L - f
        w_h = defaultdict(np.float64)
        for k in set(h.keys()).union(set(path_flows.keys())):
            w_h[k] = path_flows[k] - h[k]
        if i >= 1:
            error = -grad.dot(w) / K
            # if error < stop and error > 0.0:
            if error < stop:
                if display >= 1: print 'stop with error: {}'.format(error)
                return np.reshape(f, (types, links)).T
        if i > q:
            # step 3 of Fukushima
            v = np.sum(fs, axis=1) / min(past, i + 1) - f
            v_h = np.defaultdict(np.float64)
            for k in set(hs.keys()).union(set(path_flows.keys())):
                v_h[k] = sum(hs[k]) / min(past, i + 1) - h[k]
            norm_v = np.linalg.norm(v, 1)
            if norm_v < eps:
                if display >= 1: print 'stop with norm_v: {}'.format(norm_v)
                return np.reshape(f, (types, links)).T
            norm_w = np.linalg.norm(w, 1)
            if norm_w < eps:
                if display >= 1: print 'stop with norm_w: {}'.format(norm_w)
                return np.reshape(f, (types, links)).T
            # step 4 of Fukushima
            gamma_1 = grad.dot(v) / norm_v
            gamma_2 = grad.dot(w) / norm_w
            if gamma_2 > -eps:
                if display >= 1: print 'stop with gamma_2: {}'.format(gamma_2)
                return np.reshape(f, (types, links)).T
            d = v if gamma_1 < gamma_2 else w
            d_h = v_h if gamma_1 < gamma_2 else w_h
            # step 5 of Fukushima
            s = line_search(
                lambda a: merit(f + a * d, graphs, gs, ods, L2, grad2))
            # print 'step', s
            if s < eps:
                if display >= 1: print 'stop with step_size: {}'.format(s)
                return np.reshape(f, (types, links)).T
            f = f + s * d
            for k in set(hs.keys()).union(set(path_flows.keys())):
                h[k] = h[k] + s * d_h[k]
        else:
            f = f + 2. * w / (i + 2.)
            for k in set(h.keys()).union(set(path_flows.keys())):
                h[k] = h[k] + 2. * (w_h[k]) / (i + 2.)
        print 'iteration', i
        print 'time(sec):', timeit.default_timer() - start_time
        print 'num path flows:', len(h)

        f_h = np.zeros(graph.shape[0],
                       dtype='float64')  # initial flow assignment is null
        for k in h:
            flow = h[k]
            for link in k[2]:
                f_h[link] += flow
        print "path vs link flow diff:", np.sum(
            np.abs(f_h - np.sum(np.reshape(f, (types, links)).T, 1))), f.shape

    # find how many paths each od pair really has
    od_paths = defaultdict(int)
    most_paths = 0
    for k in h.keys():
        od_paths[(k[:2])] += 1
        most_paths = max(most_paths, od_paths[(k[:2])])
    path_counts = [0 for i in range(most_paths + 1)]
    for k in od_paths.keys():
        path_counts[od_paths[k]] += 1
    for i in range(len(path_counts)):
        print i, path_counts[i]

    return np.reshape(f, (types, links)).T