import networkx as nx
import matplotlib.pyplot as plt
from IPython.display import SVG
import pygraphviz as pgv
Definition Airline scheduling problem
Observation $|\cal S|$ aircrafts is always a feasible (trivial) solution
Flight | Origin | Destination | Departure | Arrival |
---|---|---|---|---|
1 | Roma | Milano | 6:00 a.m. | 7:00 a.m. |
2 | Venezia | Palermo | 7:00 a.m. | 8:35 a.m. |
3 | Milano | Napoli | 8:00 a.m. | 9:15 a.m. |
4 | Venezia | Catania | 9:30 a.m. | 11:15 p.m. |
5 | Catania | Firenze | 1:15 p.m. | 3:00 p.m. |
6 | Torino | Bari | 5:00 p.m. | 6:30 p.m. |
The sequence of flights $1 \rightarrow 3 \rightarrow 5$ can be realized with the same aircraft. In fact, between arrival time of flight $1$ and departure time of flight $3$ there is enough slack for aircraft maintaince. Then, from Napoli airport the aircraft can flight to Catania on time for the flight $6$
Define a graph $G$ with the following structure:
Each flight is $i$ is represented by a pair of nodes $u_i, b_i$ and an arc directed from $u_i$ to $v_i$ with capacity $1$ and minimum flow requirement $1$
flights = [1,2,3,4,5,6]
m=3
G = nx.DiGraph()
G.add_nodes_from (['u'+str(i) for i in flights])
G.add_nodes_from (['v'+str(i) for i in flights])
for i in flights:
G.add_edge ('u'+str(i), 'v'+str(i), lb=1, ub=1)
An arc from node $v_h$ and node $u_k$ with capacity $1$ is added to $G$ if an aircraft can perform flight $k$ after flight $h$
In our example:
# 1
G.add_edge ('v1','u3', lb=0, ub=1)
G.add_edge ('v1','u5', lb=0, ub=1)
G.add_edge ('v1','u6', lb=0, ub=1)
#2
G.add_edge ('v2','u5', lb=0, ub=1)
G.add_edge ('v2','u6', lb=0, ub=1)
#3
G.add_edge ('v3','u5', lb=0, ub=1)
G.add_edge ('v3','u6', lb=0, ub=1)
#4
G.add_edge ('v4','u6', lb=0, ub=1)
Nodes $s$ and $t$ are added to G. $s$ is connected to all nodes $u$ with an arc of capacity 1. All $v$ nodes are connected to $t$ with an arc of capacity 1
G.add_node('s')
G.add_node('t')
for i in flights:
G.add_edge ('s','u'+str(i), lb= 0, ub=1)
G.add_edge ('v'+str(i), 't', lb= 0, ub=1)
Add an arc with capacity $m$ and flow requirement $m$ from $t$ to $s$
G.add_edge('t','s', lb=m,ub=m)
count = 0
offsetcol = 140
lenghtcol = offsetcol * float(len(flights))
for i in flights:
G.node['u'+str(i)]['pos'] = "%f,%f"%(150, offsetcol * count)
G.node['v'+str(i)]['pos'] = "%f,%f"%(450, offsetcol * count)
count += 1
G.node['s']['pos'] = "%f,%f"%(0.0, lenghtcol / 2.0)
G.node['t']['pos'] = "%f,%f"%(600, lenghtcol /2.0)
for i in G.edges():
if i[0][0] != 'u':
G[i[0]][i[1]]['xlabel'] = '[%d,%d]'%(G[i[0]][i[1]]['lb'],G[i[0]][i[1]]['ub'])
else:
G[i[0]][i[1]]['taillabel'] = '[%d,%d]'%(G[i[0]][i[1]]['lb'],G[i[0]][i[1]]['ub'])
d = nx.to_agraph(G)
d.node_attr.update (fontsize='12', width=0.4, shape='circle')
d.edge_attr.update(fontsize='10', arrowhead='vee', penwidth=0.2)
d.graph_attr.update(splines='true')
d.draw ('img.svg', prog='neato', args='-n2')
SVG(filename='img.svg')
If a feasible flow exists in $G$ then the schedule can be realized with $m$ aircraft
H = G.copy()
for j in H.edges():
H[j[0]][j[1]]['capacity'] = \
H[j[0]][j[1]]['ub'] - H[j[0]][j[1]]['lb']
H.add_node('s1')
H.add_node('t1')
unbalance = {}
for i in G.nodes():
auxunb = 0
for j in G.in_edges(i):
auxunb += G[j[0]][j[1]]['lb']
for j in G.out_edges(i):
auxunb -= G[j[0]][j[1]]['lb']
unbalance[i] = auxunb
for i in unbalance:
if unbalance[i] > 0:
H.add_edge ('s1',i,capacity = unbalance[i])
if unbalance[i] < 0:
H.add_edge (i,'t1',capacity =- unbalance[i])
value, flow = nx.maximum_flow(H,'s1','t1', 'capacity')
valid_circulation = True
for i in H.out_edges('s1'):
print "Flow and capacity of arc (%s,%s):" % (i[0],i[1]), H[i[0]][i[1]]['capacity'], flow[i[0]][i[1]]
if H[i[0]][i[1]]['capacity'] != flow[i[0]][i[1]]:
print
print "\x1b[1;31mValid circulation not found\x1b[0m"
valid_circulation = False
break
for i in G.edges ():
G[i[0]][i[1]]['xlabel'] = ''
G[i[0]][i[1]]['taillabel'] = ''
if flow[i[0]][i[1]] > 0 or G[i[0]][i[1]]['lb'] == 1:
G[i[0]][i[1]]['color'] = 'red'
G[i[0]][i[1]]['penwidth'] = '2'
else:
G[i[0]][i[1]]['penwidth'] = '0.2'
d = nx.to_agraph(G)
d.graph_attr.update(splines='true')
d.node_attr.update (fontsize='12', shape='circle')
d.edge_attr.update(fontsize='10', arrowhead='vee')
d.draw ('img.svg', prog='neato', args='-n2')
SVG(filename='img.svg')