Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exploration of routing libraries #51

Merged
merged 25 commits into from
Jan 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
17b6d05
Generate lattice and compute shortest path using igraph
tlestang Aug 29, 2021
3ea7229
Draft computing single source shortest path for jamaica dataset
tlestang Aug 29, 2021
b1d17a8
Create network and compute shortest path using pandana
tlestang Aug 30, 2021
2dc65df
Return vpath for comparison with pandana
tlestang Aug 31, 2021
0e57ce3
Rename igraph script
tlestang Aug 31, 2021
1d52d5c
First draft for single source single sink shortest path benchmark
tlestang Aug 31, 2021
fc29fce
Try to compare pandana and igraph shortest path over many to many sho…
tlestang Aug 31, 2021
5f7fdb0
get cordinates of nodes from goedataframe layer
tlestang Sep 1, 2021
fb00bd6
Only convert node ids to integers before using pandana
tlestang Sep 1, 2021
de908e2
Compute input lists to pandana's shortest paths
tlestang Sep 1, 2021
93adee8
Helper function to go from str node ids to integers
tlestang Sep 1, 2021
3e4763c
Rearrange benchmark script into functions
tlestang Sep 1, 2021
a62231e
Add timings to benchmark script
tlestang Sep 1, 2021
c50eb97
Compute edge path from vertex path for pandana
tlestang Sep 7, 2021
46e55eb
Write edge and vertex lists for both pandana and igraph
tlestang Sep 7, 2021
ebe3743
Plot vertex path on top of jamaica roads
tlestang Sep 7, 2021
55d3c9a
Compute shortest paths and take timings
tlestang Sep 7, 2021
654fa73
Add pngs for vertex paths for both pandana and igraph
tlestang Sep 7, 2021
9dc6d92
Store starting node index in variable
tlestang Sep 7, 2021
d5ce582
Add some cli arguments to benchmark script
tlestang Sep 8, 2021
29a729e
Setup basic timings infrastucture
tlestang Sep 8, 2021
c6c7027
Add implementation for reconstruct_epath function
tlestang Sep 8, 2021
7ecc59f
Store all intermediate timings inside numpy arrays
tlestang Sep 8, 2021
a414aae
reconstruct each vpath
tlestang Sep 8, 2021
68d1f49
Use igraph to compute edge path from vertex path
tlestang Sep 8, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 181 additions & 0 deletions scripts/sources_sinks_routing/benchmark_shortest_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import random
import time
import argparse

import numpy as np
import pandana
from pandas import DataFrame
from igraph import Graph
import geopandas as gpd
from prettytable import PrettyTable


def str_to_int(str_list):
return [int(string[6:]) for string in str_list]


def get_input_list(source, dest):
"""Return the input lists for many-to-many shortest path calculation
with pandana.Network.shortest_paths
>>> list1, list2 = get_input_list(["a", "b", "c"], ["d", "e", "f", "g"])
>>> list1
["a", "a", "a", "a", "b", "b", "b", "b", "c", "c", "c", "c"]
>>> list2
["d", "e", "f", "g", "d", "e", "f", "g", "d", "e", "f", "g"]
"""
nodes_a = []
nodes_b = []
for source_node in source:
nodes_a.extend([source_node] * len(dest))
nodes_b.extend(dest)
return nodes_a, nodes_b


def shortest_paths_igraph(geodf, source_ensbl, dest_ensbl):
"""Perform many-to-many shortest path calculation with python-igraph.

Positional arguments:
geodf -- A geodataframe describing the edges. Must contain
columns "from_node", "to_node" and "length_km".
source_ensbl: A list of node ids for starting nodes.
dest_ensbl: A list of node ids for destination nodes.

Returns:
A list of shortest paths (themselves list of edges).
"""
edges = geodf.loc[:, ["from_node", "to_node", "length_km"]]
g = Graph.DataFrame(edges, directed=False)
dest = g.vs.select(name_in=dest_ensbl)

shortest_paths = []
for source_id in source_ensbl:
start = g.vs.find(name=source_id)
sp = g.get_shortest_paths(
start, dest, weights="length_km", output="epath"
)
shortest_paths.append(sp)

return shortest_paths


def shortest_paths_pandana(geodf, nodes_geodf, source_ensbl, dest_ensbl):
"""Perform many-to-many shortest path calculation with pandana.

Positional arguments:
geodf -- A geodataframe describing the edges. Must contain
columns "from_node", "to_node" and "length_km".
nodes_geodf -- A geodataframe describing the nodes.
source_ensbl -- A list of node ids for starting nodes.
dest_ensbl -- A list of node ids for destination nodes.

Returns:
A list of shortest paths (themselves list of vertices).
"""
list_of_coords = [
(point.x, point.y) for point in nodes_geodf.loc[:, "geometry"]
]
nodes = DataFrame(list_of_coords, columns=["x", "y"])

# Transform source and destination columns into lists of integers
# (node ids)
from_node = str_to_int(gdf["from_node"])
to_node = str_to_int(gdf["to_node"])

edges = DataFrame(
{"from": from_node, "to": to_node, "weight": gdf["length_km"].values}
)
net = pandana.Network(
nodes["x"], nodes["y"], edges["from"], edges["to"], edges[["weight"]]
)
nodes_a, nodes_b = get_input_list(
str_to_int(source_ensbl), str_to_int(dest_ensbl)
)
return net.shortest_paths(nodes_a, nodes_b, imp_name="weight")


def reconstruct_epath(vpath, gdf):
"""Compute edge path from a vertex path.

Positional arguments:
vpath -- A list of node ids
gdf -- A geodataframe describing the edges. Must contain
columns "from_node", "to_node" with nodes as "roadn_<nodeid>"

Returns:
A list of edge IDs.
"""
g = Graph.DataFrame(gdf.loc[:, ["from_node", "to_node"]], directed=False)
igraph_nodes = [g.vs.find(name="roadn_" + str(i)).index for i in vpath]
epath = g.get_eids(path=igraph_nodes, directed=False)
return epath


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="")
parser.add_argument("--sizes", nargs="+")
parser.add_argument("--nreps", type=int, default=10)
parser.add_argument("-r", "--reconstr", action="store_true")

args = parser.parse_args()

gdf = gpd.read_file("jamaica_roads.gpkg")
nodes_gdf = gpd.read_file("jamaica_roads.gpkg", layer="nodes")

igraph_times = np.zeros((args.nreps, len(args.sizes)))
pandana_times = np.zeros((args.nreps, len(args.sizes)))
pandana_reconstruct_times = np.zeros((args.nreps, len(args.sizes)))

for j, size in enumerate(args.sizes):
print(f"Size {j+1} of {len(args.sizes)}")
# Sample source and destination ensembles
for irep in range(args.nreps):
print(f" Rep {irep + 1} of {args.nreps}")
# Some nodes in the "nodes" layer don't seem to have roads
# start of end at them. So picking from from_node and to_node
# columns.
source_ensbl = random.sample(list(gdf["from_node"]), int(size))
dest_ensbl = random.sample(list(gdf["to_node"]), int(size))

print(" Timing igraph")
tic = time.time()
path = shortest_paths_igraph(gdf, source_ensbl, dest_ensbl)
toc = time.time()
igraph_times[irep, j] = toc - tic

print(" Timing pandana")
tic = time.time()
vpaths = shortest_paths_pandana(
gdf, nodes_gdf, source_ensbl, dest_ensbl
)
toc = time.time()
pandana_times[irep, j] = toc - tic

if args.reconstr:
print(" Timing pandana reconstruction step")
tic = time.time()
epaths = [reconstruct_epath(vpath, gdf) for vpath in vpaths]
toc = time.time()
pandana_reconstruct_times[irep, j] = toc - tic

tbl = PrettyTable()
tbl.add_column("Size", args.sizes)
tbl.add_column("Igraph (avg)", np.mean(igraph_times, axis=0))
tbl.add_column("Igraph (std)", np.std(igraph_times, axis=0))
tbl.add_column("pandana (avg)", np.mean(pandana_times, axis=0))
tbl.add_column("pandana (std)", np.std(pandana_times, axis=0))
if args.reconstr:
tbl.add_column(
"reconstr (avg)", np.mean(pandana_reconstruct_times, axis=0)
)
tbl.add_column(
"reconstr (std)", np.std(pandana_reconstruct_times, axis=0)
)

from prettytable import MARKDOWN

tbl.float_format = "1.2"
tbl.set_style(MARKDOWN)
with open("table.txt", "w") as f:
f.write(tbl.get_string())
f.write("\n")
print(tbl)
Binary file added scripts/sources_sinks_routing/igraph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added scripts/sources_sinks_routing/pandana.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions scripts/sources_sinks_routing/shortest_paths_igraph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from igraph import Graph
import geopandas as gpd
import matplotlib.pyplot as plt

gdf = gpd.read_file("jamaica_roads.gpkg")
edges = gdf.loc[:, ["from_node", "to_node", "length_km"]]

g = Graph.DataFrame(edges, directed=False)

start = g.vs.find(name="roadn_0")
finish = g.vs.find(name="roadn_15891")

# Return list of list of edge IDs
# Return vpath for comparison with pandana
sp = g.get_shortest_paths(
start.index, finish.index, weights="length_km", output="vpath"
)

# base = gdf.plot()
# for path in sp:
# # Select linestrings according to edge IDs making the path
# # edge ID are 1to1 with row id in dataframe
# sub_gdf = gdf.iloc[path, :]
# sub_gdf.plot(ax=base, color="green")
# plt.show()

nodes_gdf = gpd.read_file("jamaica_roads.gpkg", layer="nodes")
base = gdf.plot()
for path in sp:
nodeslist = [int(g.vs[i]["name"][6:]) for i in path]
# Select linestrings according to edge IDs making the path
# edge ID are 1to1 with row id in dataframe
sub_gdf = nodes_gdf.iloc[nodeslist, :]
sub_gdf.plot(ax=base, color="red", markersize=6)
plt.show()

# edge list
#[[0, 11345, 11356, 15399, 15357, 15356, 526, 1, 521, 522, 524, 523, 520, 509, 510, 519, 517, 516, 518, 515, 513, 512, 511, 506, 3244, 3245, 3260, 3261, 3255, 3256, 3254, 3253, 3250, 3247, 3249, 3251, 3258, 3259, 3777, 3781, 3785, 3784, 3786, 3766, 3765, 3778, 3805, 3804, 542, 539, 540, 685, 684, 683, 682, 680, 688, 687, 686, 679, 678, 689, 690, 694, 706, 700, 699, 698, 697, 696, 695, 705, 704, 715, 693, 691, 716, 717, 718, 720, 719, 711, 708, 709, 710, 713, 701, 702, 707, 712, 976, 989, 988, 990, 978, 977, 983, 982, 996, 995, 993, 986, 987, 992, 994, 984, 985, 979, 8043, 7988, 981, 997, 15476, 15472, 7944, 15473, 4475, 4476, 4470, 4471, 4472, 4481, 4479, 4473, 4474, 4477, 4478, 7833, 15495, 15510, 15509, 15531, 4573, 4572, 4578, 4579, 4581, 4577, 4576, 4575, 4574, 4571, 4570, 785, 809, 1387, 1390, 1382, 1383, 1397, 1395, 1394, 1398, 1378, 1379, 1396, 1388, 1389, 1399, 1391, 1384, 1385, 1380, 1377, 1376, 1393, 1386, 1369, 4614, 4608, 4607, 4606, 4610, 4609, 4597, 4596, 4598, 4599, 4615, 4595, 4594, 4600, 4601, 4603, 4604, 4605, 1310, 1311, 1334, 4988, 8099, 4925, 4915, 4914, 4917, 4916, 4919, 4918, 4920, 4926, 4937, 4932, 4933, 4938, 4939, 4934, 4936, 7611, 4940, 5028, 5029, 7722, 5041, 5044, 5043, 5039, 5040, 5042, 1139, 1154, 1153, 1149, 1150, 1151, 1146, 1147, 8332, 1155, 5345, 5342, 5341, 5387, 5388, 5389, 5484, 5470, 5471, 5486, 5398, 5397, 8199, 5391, 5396, 5394, 5395, 5399, 5393, 5392, 5400, 5427, 5420, 5415, 5403, 5404, 8266, 5428, 5411, 5413, 5406, 5405, 5407, 5429, 5430, 5408, 5409, 5414, 5416, 5417, 5426, 5424, 5425, 5431, 8299, 5402, 1182, 1179, 1173, 1174, 1178, 1180, 1184, 1168, 1167, 1166, 1165, 16002, 16003, 16004, 16009, 15979, 15978, 15980, 15992, 15987, 15986, 15985, 15996, 15995, 15993, 15984, 15983, 15982, 15974, 15973, 15991, 15990, 8310, 15989, 15998, 15997, 6294, 6292, 6293, 6120, 6208, 6305, 6304, 6302, 6298, 8243, 6297, 6296, 6295, 6303, 6554, 6551, 6552, 6558, 6555, 6550, 6543, 6544, 6548, 6549, 6539, 6540, 6546, 6547]]

# vertex list
#[0, 538, 539, 9029, 8992, 8991, 10470, 1, 10466, 10467, 10469, 10468, 10465, 10455, 10456, 10464, 10462, 10461, 10463, 10460, 10459, 10458, 10457, 10452, 12918, 12919, 12932, 12933, 12928, 12929, 12927, 12926, 12923, 12920, 12922, 12924, 12930, 12931, 13398, 13400, 13404, 13403, 13405, 13388, 13387, 13399, 13422, 13421, 10485, 10482, 10483, 10613, 10612, 10611, 10610, 10609, 10616, 10615, 10614, 10608, 10607, 10617, 10618, 10621, 10632, 10627, 10626, 10625, 10624, 10623, 10622, 10631, 10630, 10640, 10620, 10619, 10641, 10642, 10643, 10645, 10644, 10637, 10634, 10635, 10636, 10639, 10628, 10629, 10633, 10638, 10876, 10888, 10887, 10889, 10878, 10877, 10882, 10881, 10894, 10893, 10891, 10885, 10886, 10890, 10892, 10883, 10884, 10879, 24, 235, 10880, 10895, 9099, 9095, 231, 9096, 14025, 14026, 14020, 14021, 14022, 14030, 14029, 14023, 14024, 14027, 14028, 221, 9115, 9129, 9128, 9148, 14113, 14112, 14118, 14119, 14120, 14117, 14116, 14115, 14114, 14111, 14110, 10703, 10725, 11245, 11248, 11240, 11241, 11254, 11252, 11251, 11255, 11237, 11238, 11253, 11246, 11247, 11256, 11249, 11242, 11243, 11239, 11236, 11235, 11250, 11244, 11229, 14150, 14145, 14144, 14143, 14147, 14146, 14135, 14134, 14136, 14137, 14151, 14133, 14132, 14138, 14139, 14140, 14141, 14142, 11176, 11177, 11198, 14488, 245, 14430, 14421, 14420, 14423, 14422, 14425, 14424, 14426, 14431, 14441, 14437, 14438, 14442, 14443, 14439, 14440, 201, 14444, 14523, 14524, 211, 14535, 14538, 14537, 14533, 14534, 14536, 11021, 11035, 11034, 11030, 11031, 11032, 11028, 11029, 266, 11036, 14809, 14806, 14805, 14847, 14848, 14849, 14934, 14921, 14922, 14936, 14857, 14856, 254, 14850, 14855, 14853, 14854, 14858, 14852, 14851, 14859, 14883, 14877, 14872, 14861, 14862, 260, 14884, 14869, 14870, 14864, 14863, 14865, 14885, 14886, 14866, 14867, 14871, 14873, 14874, 14882, 14880, 14881, 14887, 263, 14860, 11060, 11058, 11052, 11053, 11057, 11059, 11062, 11048, 11047, 11046, 11045, 9572, 9573, 9574, 9579, 9551, 9550, 9552, 9563, 9559, 9558, 9557, 9567, 9566, 9564, 9556, 9555, 9554, 9547, 9546, 9562, 9561, 264, 9560, 9569, 9568, 15663, 15661, 15662, 15506, 15586, 15673, 15672, 15670, 15667, 258, 15666, 15665, 15664, 15671, 15898, 15895, 15896, 15900, 15899, 15894, 15888, 15889, 15892, 15893, 15884, 15885, 15890, 15891]
89 changes: 89 additions & 0 deletions scripts/sources_sinks_routing/shortest_paths_pandana.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import pandana
from igraph import Graph
import geopandas as gpd
from pandas import DataFrame
import matplotlib.pyplot as plt
import numpy as np

gdf = gpd.read_file("jamaica_roads.gpkg")

from_node = [int(node_id[6:]) for node_id in gdf["from_node"]]
to_node = [int(node_id[6:]) for node_id in gdf["to_node"]]

nids, idxs = np.unique(from_node + to_node, return_index=True)
x = []
y = []
for nid, idx in zip(nids, idxs):
linestr = gdf.loc[idx % len(from_node), "geometry"]
coords = linestr.coords[int(-1 * (idx // len(from_node)))]
x.append(coords[0])
y.append(coords[1])

nodes = DataFrame({"x": x, "y": y}, index=nids)
edges = DataFrame(
{"from": from_node, "to": to_node, "weight": gdf["length_km"].values}
)
net = pandana.Network(
nodes["x"], nodes["y"], edges["from"], edges["to"], edges[["weight"]]
)

start_node_idx = 15891
sp = net.shortest_path(0, start_node_idx, imp_name="weight")
edges = gdf.loc[:, ["from_node", "to_node"]]
g = Graph.DataFrame(edges, directed=False)
igraph_nodes = [g.vs.find(name="roadn_"+str(i)).index for i in sp]
epath = g.get_eids(path=igraph_nodes, directed=False)

base = gdf.plot()
for path in sp:
# Select linestrings according to edge IDs making the path
# edge ID are 1to1 with row id in dataframe
sub_gdf = gdf.iloc[epath, :]
sub_gdf.plot(ax=base, color="green", linewidth=5)
plt.show()

# edge list
# [8649, 8646, 8643, 8642, 8592, 10194, 10192, 10193, 10187, 10190, 10189, 10188, 10186, 10177, 10191, 10184, 10182, 10183, 10185, 10180, 10179, 10178, 10181, 12907, 12897, 12911, 12912, 12913, 12906, 12909, 12905, 12904, 12900, 12899, 12901, 12908, 12910, 13394, 13397, 13406, 13403, 13404, 13407, 13387, 13395, 13427, 13428, 13426, 10211, 10208, 10351, 10346, 10350, 10345, 10344, 10349, 10348, 10354, 10347, 10343, 10352, 10353, 10356, 10365, 10364, 10361, 10360, 10359, 10358, 10357, 10367, 10363, 10374, 10379, 10355, 10380, 10375, 10376, 10378, 10381, 10377, 10370, 10368, 10369, 10372, 10373, 10362, 10366, 10371, 10632, 10646, 10641, 10650, 10642, 10633, 10649, 10637, 10651, 10648, 10652, 10644, 10640, 10643, 10645, 10647, 10638, 10639, 10634, 10635, 10636, 10654, 10653, 8735, 8731, 8732, 14077, 14084, 14078, 14074, 14075, 14086, 14085, 14082, 14076, 14083, 14079, 14080, 14081, 8811, 8764, 8778, 14180, 14179, 14185, 14186, 14187, 14188, 14184, 14183, 14182, 14181, 14178, 14177, 10462, 11025, 11027, 11028, 11021, 11037, 11035, 11032, 11040, 11036, 11019, 11033, 11034, 11026, 11038, 11039, 11029, 11022, 11023, 11020, 11018, 11031, 11030, 11024, 14221, 14220, 14213, 14212, 14216, 14214, 14218, 14202, 14203, 14204, 14225, 14222, 14201, 14205, 14208, 14207, 14209, 14211, 14210, 10958, 10976, 14576, 14578, 14577, 14517, 14504, 14513, 14505, 14510, 14506, 14507, 14512, 14524, 14526, 14522, 14525, 14527, 14528, 14523, 14529, 14530, 14624, 14607, 14628, 14629, 14632, 14634, 14631, 14627, 14630, 14633, 10813, 10810, 10809, 10806, 10807, 10812, 10805, 10817, 10818, 14926, 14917, 14914, 14958, 14959, 14960, 15055, 15054, 15038, 15059, 15060, 14966, 14965, 14961, 14970, 14964, 14963, 14967, 14968, 14962, 14969, 15003, 14991, 14984, 14980, 14972, 14992, 14993, 15004, 14978, 14985, 14973, 14974, 15002, 14994, 14996, 14975, 14979, 14987, 14981, 14995, 14990, 14989, 14997, 14998, 14999, 14971, 10837, 10835, 10829, 10834, 10836, 10839, 10841, 10825, 10833, 10824, 10827, 9219, 9220, 9227, 9226, 9197, 9198, 9216, 9207, 9203, 9202, 9214, 9225, 9210, 9208, 9201, 9200, 9211, 9195, 9212, 9206, 9205, 9204, 9232, 9215, 15817, 15818, 15815, 15816, 15814, 15829, 15991, 15990, 15996, 15993, 15992, 15999, 15997, 15994, 16000, 15995, 15987, 15986, 15985, 16001, 15998, 15988, 15989, 15984, 16009, 16012, 16013, 16010, 16011, 16007, 16005, 16006, 16004, 16003, 16002, 16008, 16098, 16100, 16105, 16102, 16083, 16085, 16086, 16088, 16087, 16099, 16091, 16173, 16174, 16179, 16177, 16171, 16178, 16168, 16172, 16176, 16169]

# vertex list
## [ 0, 538, 539, 9029, 8992, 8991, 10470, 1, 10466,
# 10467, 10469, 10468, 10465, 10455, 10456, 10464, 10462, 10461,
# 10463, 10460, 10459, 10458, 10457, 10452, 12918, 12919, 12932,
# 12933, 12928, 12929, 12927, 12926, 12923, 12920, 12922, 12924,
# 12930, 12931, 13398, 13400, 13404, 13403, 13405, 13388, 13387,
# 13399, 13422, 13421, 10485, 10482, 10483, 10613, 10612, 10611,
# 10610, 10609, 10616, 10615, 10614, 10608, 10607, 10617, 10618,
# 10621, 10632, 10627, 10626, 10625, 10624, 10623, 10622, 10631,
# 10630, 10640, 10620, 10619, 10641, 10642, 10643, 10645, 10644,
# 10637, 10634, 10635, 10636, 10639, 10628, 10629, 10633, 10638,
# 10876, 10888, 10887, 10889, 10878, 10877, 10882, 10881, 10894,
# 10893, 10891, 10885, 10886, 10890, 10892, 10883, 10884, 10879,
# 24, 235, 10880, 10895, 9099, 9095, 231, 9096, 14025,
# 14026, 14020, 14021, 14022, 14030, 14029, 14023, 14024, 14027,
# 14028, 221, 9115, 9129, 9128, 9148, 14113, 14112, 14118,
# 14119, 14120, 14117, 14116, 14115, 14114, 14111, 14110, 10703,
# 10725, 11245, 11248, 11240, 11241, 11254, 11252, 11251, 11255,
# 11237, 11238, 11253, 11246, 11247, 11256, 11249, 11242, 11243,
# 11239, 11236, 11235, 11250, 11244, 11229, 14150, 14145, 14144,
# 14143, 14147, 14146, 14135, 14134, 14136, 14137, 14151, 14133,
# 14132, 14138, 14139, 14140, 14141, 14142, 11176, 11177, 11198,
# 14488, 245, 14430, 14421, 14420, 14423, 14422, 14425, 14424,
# 14426, 14431, 14441, 14437, 14438, 14442, 14443, 14439, 14440,
# 201, 14444, 14523, 14524, 211, 14535, 14538, 14537, 14533,
# 14534, 14536, 11021, 11035, 11034, 11030, 11031, 11032, 11028,
# 11029, 266, 11036, 14809, 14806, 14805, 14847, 14848, 14849,
# 14934, 14921, 14922, 14936, 14857, 14856, 254, 14850, 14855,
# 14853, 14854, 14858, 14852, 14851, 14859, 14883, 14877, 14872,
# 14861, 14862, 260, 14884, 14869, 14870, 14864, 14863, 14865,
# 14885, 14886, 14866, 14867, 14871, 14873, 14874, 14882, 14880,
# 14881, 14887, 263, 14860, 11060, 11058, 11052, 11053, 11057,
# 11059, 11062, 11048, 11047, 11046, 11045, 9572, 9573, 9574,
# 9579, 9551, 9550, 9552, 9563, 9559, 9558, 9557, 9567,
# 9566, 9564, 9556, 9555, 9554, 9547, 9546, 9562, 9561,
# 264, 9560, 9569, 9568, 15663, 15661, 15662, 15506, 15586,
# 15673, 15821, 15820, 15824, 15823, 15822, 15828, 15826, 15825,
# 15827, 15817, 15816, 15815, 15814, 15829, 15819, 15818, 15813,
# 15812, 15836, 15839, 15838, 15837, 15834, 15833, 15832, 15831,
# 506, 507, 15830, 15835, 15920, 15921, 15923, 15906, 15905,
# 15908, 15909, 15911, 15910, 15915, 15914, 15987, 15988, 15989,
# 15985, 15984, 15980, 15979, 15986, 15982, 15981])