Price of Anarchy - Barcelona¶
1. Imports and network readin¶
First, we import the paminco and some other packages and download the traffic data for Barcelona from GitHub:
import os
import requests
import paminco
URL = "https://raw.githubusercontent.com/bstabler/TransportationNetworks/master/Barcelona/"
netfile = "Barcelona_net.tntp"
tripsfile = "Barcelona_trips.tntp"
# Load from GitHub and save locally
for f in [netfile, tripsfile]:
url = os.path.join(URL, f)
response = requests.get(url)
with open(f, 'wb') as fh:
fh.write(response.content)
Note that the power factor \(p_e\) for the link travel time \(l_e(x_e) = \text{fft}_e \cdot \left( 1 + B_e \cdot \left(\frac{x}{\text{cap}_e}\right) ^ {p_e} \right)\) is not
an integer for the Barcelona network, where we find power factors such 4.446, 4.924, ...
.
However, we can equip a network with custom cost functions by setting cost_type=symbolic
:
# Setup network
net = paminco.Network.from_tntp(netfile, tripsfile=tripsfile, cost_type="symbolic")
# Cleanup
for f in [netfile, tripsfile]:
os.remove(f)
2. Setting up network cost¶
Second, we set up SymbolicCost, defining the edge cost \(F_e\) and thus the objective function to calculate both user equilibrium (UE) and system optimum (SO). We find minimum cost flows for user equilibrium and system optimum if we transform the edge cost by:
Note that Frank-Wolfe requires the first derivative of the objective function. Thus, we have to specfiy edge cost \(F_e\) and marginal edge cost \(f_e\).
# User equilibrium cost funcs (egde cost and marginal edge cost)
ue_F = "fft * (x + b * x / (p + 1) * (x / c)**p)"
ue_f = "fft * (1 + b * (x / c)**p)"
# System optimum cost funcs (egde cost and marginal edge cost)
so_F = "x * fft * (1 + b * x**p * c**(-p))"
so_f = "fft + fft * b * (p + 1) * c**(-p) * x**(p)"
net.cost.coeffs["fft"] = net.cost.coeffs["free_flow_time"]
net.cost.coeffs["c"] = net.cost.coeffs["capacity"]
net.cost.coeffs["p"] = net.cost.coeffs["power"]
3. Find UE and SO flow¶
Finally, we equip our network with both cost functions instances and run Frank-Wolfe to find a minimum cost flow:
# copy networks
from copy import deepcopy
# user equilibrium
net_ue = deepcopy(net)
net_ue.cost.funcs["F"] = ue_F
net_ue.cost.funcs["f"] = ue_f
fw_ue = paminco.NetworkFW(net_ue)
fw_ue.run(max_iter=10, print=False)
# system optimum
net_so = deepcopy(net)
net_so.cost.funcs["F"] = so_F
net_so.cost.funcs["f"] = so_f
fw_so = paminco.NetworkFW(net_so)
fw_so.run(max_iter=10, print=False)
For example, we can easily access the user equilibrium flow by:
fw_ue.x
array([1062.36033884, 1183.74866116, 0. , ..., 1061.32165241,
0. , 4203.83194677])
4. Price of Anarchy¶
The objective function to calculate the system optimum is the total system travel time (TSTT):
def TTST(x):
return net_so.cost(x).sum()
We find that the total system travel time for Barcelona is increased by about 0.9 percent
if users behave selfishly:
poa = TTST(fw_ue.flow) / TTST(fw_so.flow)
poa
1.00883445011059