Compare commits

..

No commits in common. "master" and "dijkstra_debug" have entirely different histories.

27 changed files with 701 additions and 1470 deletions

1
.gitignore vendored
View file

@ -1,2 +1 @@
/target
/landmarks

347
Cargo.lock generated
View file

@ -146,6 +146,24 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
[[package]]
name = "byteorder"
version = "1.4.3"
@ -158,6 +176,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.0.73"
@ -185,6 +209,17 @@ dependencies = [
"generic-array",
]
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"bitflags",
"textwrap 0.11.0",
"unicode-width",
]
[[package]]
name = "clap"
version = "3.2.16"
@ -199,7 +234,7 @@ dependencies = [
"once_cell",
"strsim",
"termcolor",
"textwrap",
"textwrap 0.15.0",
]
[[package]]
@ -260,6 +295,87 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "criterion"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
dependencies = [
"atty",
"cast",
"clap 2.34.0",
"criterion-plot",
"csv",
"itertools",
"lazy_static",
"num-traits",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_cbor",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
"crossbeam-utils",
"memoffset",
"once_cell",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
dependencies = [
"cfg-if 1.0.0",
"once_cell",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -270,6 +386,28 @@ dependencies = [
"typenum",
]
[[package]]
name = "csv"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
dependencies = [
"bstr",
"csv-core",
"itoa 0.4.8",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
dependencies = [
"memchr",
]
[[package]]
name = "ctr"
version = "0.8.0"
@ -343,7 +481,8 @@ name = "fapra_osm_2"
version = "0.1.0"
dependencies = [
"bincode",
"clap",
"clap 3.2.16",
"criterion",
"geojson",
"handlebars",
"osmpbfreader",
@ -622,6 +761,12 @@ dependencies = [
"tracing",
]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "handlebars"
version = "4.3.3"
@ -684,7 +829,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
dependencies = [
"bytes",
"fnv",
"itoa",
"itoa 1.0.3",
]
[[package]]
@ -725,7 +870,7 @@ dependencies = [
"http-body",
"httparse",
"httpdate",
"itoa",
"itoa 1.0.3",
"pin-project-lite",
"socket2",
"tokio",
@ -789,12 +934,36 @@ dependencies = [
"libc",
]
[[package]]
name = "itertools"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "itoa"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
[[package]]
name = "js-sys"
version = "0.3.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
@ -872,6 +1041,15 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]]
name = "mime"
version = "0.3.16"
@ -1034,6 +1212,12 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "opaque-debug"
version = "0.3.0"
@ -1184,6 +1368,34 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "plotters"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9428003b84df1496fb9d6eeee9c5f8145cb41ca375eb0dad204328888832811f"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
[[package]]
name = "plotters-svg"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0918736323d1baff32ee0eade54984f6f201ad7e97d5cfb5d6ab4a358529615"
dependencies = [
"plotters-backend",
]
[[package]]
name = "polyval"
version = "0.5.3"
@ -1318,6 +1530,30 @@ dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
dependencies = [
"autocfg",
"crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
@ -1523,6 +1759,16 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde_cbor"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.143"
@ -1540,7 +1786,7 @@ version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
dependencies = [
"itoa",
"itoa 1.0.3",
"ryu",
"serde",
]
@ -1696,6 +1942,15 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "textwrap"
version = "0.15.0"
@ -1737,7 +1992,7 @@ version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db76ff9fa4b1458b3c7f077f3ff9887394058460d21e634355b273aaf11eea45"
dependencies = [
"itoa",
"itoa 1.0.3",
"libc",
"num_threads",
"time-macros",
@ -1749,6 +2004,16 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "tokio"
version = "1.20.1"
@ -1925,6 +2190,12 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.3"
@ -1980,6 +2251,70 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
[[package]]
name = "web-sys"
version = "0.3.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.2.8"

View file

@ -16,3 +16,10 @@ serde = {version="1.0", features=["derive"]}
serde_json = "1.0"
bincode = "1.3.3"
osmpbfreader = "0.15.2"
[dev-dependencies]
criterion = "0.3.6"
[[bench]]
name = "task5"
harness = false

171
README.md
View file

@ -1,171 +0,0 @@
# FaPra Algorithms on OSM Data
This repository contains implementations for the Fachpraktikum Algorithms on
OSM Data. The code is written in Rust, a stable rust compiler and `cargo`, the
rust build tool/package manager is required.
# Building
simply run `cargo build --release` to build release versions of all binaries.
# Tasks
## Task 1
There is no real implementation work for task 1, next!
## Task 2 + Task 3 + Task 4
Reading the data from an OSM PDF file and converting it to a graph is done in
`src/bin/generate_grid.rs`.
The implementation of the spherical point in polygon test is done in `src/polygon.rs`
with the function `Polygon::contains`.
There is one polygon in the graph, for which no valid outside polygon can be found.
I did not have the time to investigate this further.
### Extracting Coastlines from the PBF file
The code uses the osmpbfreader crate.
Sadly this module uses ~10GB of memory to extract the data from the PBF file
with all the coastlines.
### Point in Polygon
The test by Bevis and Chatelain is implemented.
Instead of using a point that is inside the polygon a point outside of the
polygon is used, because here we can simply specify several "well-known" outside
points that are somewhere in the ocean and therefore are outside of every polygon.
### Grid Graph
The Grid Graph is implemented in `gridgraph.rs`.
A regular grid graph can be generated with the `generate_regular_grid` function.
Import and Export from/to a file can be done with the `from_fmi_file` and `write_fmi_file` functions.
## Task 5
### Dijkstra Benchmarks
Dijkstras algorithm is implenented in `gridgraph.rs` with `GridGraph::shortest_path`.
It uses a Heap to store the nodes.
For details on how to run benchmarks see the benchmarks section at the end.
## Task 6
The UI is a Web-UI based on leaflet.
To start it, run `task6` with a .fmi file as a graph layout and a set of landmarks
(see Task 7 for details).
The start and end nodes can be placed on arbitrary positions and an algorithm
searches for the closes grid node and then runs the Routing.
The webserver listens on http://localhost:8000
Currently there is a display bug when routes wrap around the globe at 180 degrees
longitude. Instead of continuing the line as expected the line is drawn around
the globe in the "wrong" direction.
This is however just a display bug, the route itself is correct.
## Task 7
I implemented ALT, as described in [1].
Additionally A\* is available with a simple, unoptimized haversine distance
as the heuristic.
A\* is implemented in `src/astar.rs` and the heuristics for ALT are implemented
in `src/alt.rs`.
### Landmarks for ALT
currently 3 different landmark generation methods are available
- random selection
- greedy, distance-maximizing selection
- manual selection (from a GeoJSON file)
These can be generated with the `gen_landmarks_random`, `gen_landmarks_greedy`
and `gen_landmarks_geojson` binaries. The random and greedy methods take
the number of landmarks to select from a parameter.
A handy wrapper for that can be found as a Python script in `utils/generate_landmarks.py` that
generates landmarks for 4, 8, 16, 32 and 64 landmarks, both greedy and random.
# Running the benchmarks
First a set of queries is needed.
These can be generated with `generate_benchmark_targets --graph <graph> > targets.json`.
This generates 1000 random, distinct source and destination pairs.
The `--amount` parameter allows to adjust the number of pairs generated.
The actual benchmark is located in `benchmark`.
It needs a set of queries and a graph.
If the `--dijkstra` option is given it runs Dijkstras algorithm.
If the `--astar` option is given it runs A\* with the haversine distance
If `--landmarks <landmarkfile>` is given it runs ALT with that landmark file.
By setting `--alt_best_size <number>` one can select how many of the landmarks
are used to answer the query.
The benchmark prints out how many nodes were popped from the heap for
each run and the average time per route.
`utils/run_benchmarks.py` is a wrapper script that runs the benchmarks for a
big set of parameters.
`utils/plot_results.py` generates several plots of the results.
# Results
These are some quick tests, further results will be presented later.
Everything was run on a Thinkpad X260 laptop with an Intel i7-6600U CPU @ 2.60GHz
processor.
Each test used the same 1000 queries.
Rust v1.57.0 was used for all tests.
The ALT variants were used with the 4 best landmarks.
Further tests on the performance of more landmarks will be presented laster.
The set of 44 handpicked landmarks were spread around the extremeties of the
continents and into "dead ends" like the Mediteranean and the Gulf of Mexico
with the goal to provide landmarks that are "behind" the source or target
node.
All benchmarks were run on the provided benchmark graph.
## raw data:
```
# name, (avg. heap pops per query, avg. time)
{'astar': (155019.451, 0.044386497025),
'dijkstra': (423046.796, 0.058129875474999995),
'greedy_32': (42514.751, 0.013299024275000002),
'greedy_64': (35820.461, 0.011887869759),
'handpicked_44': (70868.721, 0.01821366828),
'random_32': (58830.082, 0.016845884717),
'random_64': (51952.261, 0.015234422699)}
```
## Interpretation
Dijkstra needs ~58ms per route, while the best version is greedy\_64 (that is
with 64 landmarks) needs only 12 seconds, which is ~5 times faster.
We also see, that the greedy versions perform slightly better than their
random counterparts with the same amount of nodes.
While the 44 handpicked landmarks outperformed A\* and Dijkstra, they are beaten
by both the random and greedy landmark selections which had fewer nodes.
## Memory Consumption
The landmarks are basically arrays of the cost to each node.
Since the distances are currently calculates with 64 bit integers
each landmark needs 8 byte per node in the graph.
With a graph that has about 700k nodes this leads to ~5.5MB of memory per
landmark.
So 64 landmarks need ~350MB of memory.
One could also use 32 bit integers which would half the memory requirements.
# References
[1](Computing the Shortest Path: A\* meets Graph Theory, A. Goldberg and C. Harrelson, Microsoft Research, Technical Report MSR-TR-2004-24, 2004)

View file

@ -1,25 +0,0 @@
7
16
0 -90 180
1 -90 -60.00000000000001
2 -90 59.999999999999986
3 -30.000000000000004 180
4 -30.000000000000004 59.999999999999986
5 29.999999999999993 180
6 29.999999999999993 -60.00000000000001
0 2 0
0 1 0
0 3 6671695
1 0 0
1 2 0
2 1 0
2 0 0
2 4 6671695
3 4 10806007
3 5 6671695
3 0 6671695
4 3 10806007
4 2 6671695
5 6 10806007
5 3 6671695
6 5 10806007

View file

@ -1,489 +0,0 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-68.5546875,
-56.237244700410315
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
19.84130859375,
-35.281500657891186
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
146.5576171875,
-44.18220395771564
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
168.4423828125,
-47.75409797968002
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-18.6328125,
17.97873309555617
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-33.046875,
-6.315298538330033
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-83.84765625,
-3.513421045640032
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-33.57421875,
83.81102365639774
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
21.62109375,
80.64703474739618
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
103.974609375,
77.89725517594933
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
191.42578125,
65.94647177615738
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
26.894531249999996,
71.46912418989677
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
68.115234375,
77.07878389624943
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-43.681640625,
59.265880628258095
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-90.439453125,
25.085598897064752
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-126.38671874999999,
40.17887331434696
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-155.7421875,
71.74643171904148
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-160.02685546875,
54.380557368630654
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-93.42773437499999,
74.3785127963314
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
157.236328125,
50.84757295365389
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
134.033203125,
39.30029918615029
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
111.884765625,
8.146242825034385
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
129.638671875,
-5.878332109674314
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
58.798828125,
18.145851771694467
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
31.289062500000004,
33.32134852669881
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
34.453125,
43.16512263158296
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-9.9755859375,
38.90813299596705
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
1.5380859375,
51.01375465718821
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
13.2275390625,
54.901882187385006
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
80.7275390625,
5.528510525692801
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
119.99267578124999,
24.647017162630366
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
154.51171875,
-28.613459424004414
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
111.4453125,
-26.03704188651583
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
142.55859375,
74.44935750063425
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-42.1875,
29.53522956294847
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
150.46875,
20.96143961409684
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-18.984375,
-44.840290651397986
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
76.9921875,
-21.616579336740593
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-111.09374999999999,
-32.54681317351514
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-20.0390625,
60.23981116999893
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-55.37109374999999,
-62.226996036319726
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
25.6640625,
-69.41124235697255
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
122.6953125,
-65.21989393613208
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-122.6953125,
-72.81607371878991
]
}
}
]
}

View file

@ -1 +0,0 @@
[{"source":51,"destination":11}]

View file

@ -1 +0,0 @@
[{"source":2,"destination":5}]

View file

@ -1,64 +1,83 @@
use crate::gridgraph::{EdgeCost, GraphNode, GridGraph, NodeId};
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use crate::utils::DijkstraElement;
/// a single Landmark
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Landmark {
pub node: GraphNode,
pub distances: Vec<EdgeCost>,
}
/// A set of Landmarks
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LandmarkSet {
pub landmarks: Vec<Landmark>,
}
/// The LandmarkBestSet is the datastructure in which the indices of the
/// best landmarks for a certain query are stored.
#[derive(Debug, Clone)]
pub struct LandmarkBestSet<'a> {
pub landmark_set: &'a LandmarkSet,
pub best_size: usize,
best_landmarks: Vec<usize>,
}
impl Landmark {
/// generates a landmark (calculates all distances) for a given node.
pub fn generate(node: GraphNode, graph: &GridGraph) -> Landmark {
// This is running a simplified version of dijkstra.
// It also does not track the ancestors of a node, because it is
// not needed for generating hte landmarks.
let mut landmark = Landmark {
node,
distances: vec![EdgeCost::MAX; graph.nodes.len()],
};
landmark.node = node;
#[derive(Eq)]
struct DijkstraElement {
index: u32,
cost: EdgeCost,
}
impl Ord for DijkstraElement {
// inverted cmp function, such that the Max-Heap provided by Rust
// can be used as a Min-Heap
fn cmp(&self, other: &Self) -> Ordering {
other.cost.cmp(&self.cost)
}
}
impl PartialOrd for DijkstraElement {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for DijkstraElement {
fn eq(&self, other: &Self) -> bool {
self.cost == other.cost
}
}
let mut heap = BinaryHeap::new();
heap.push(DijkstraElement {
cost: 0,
index: landmark.node.index,
});
let mut counter = 0;
while let Some(DijkstraElement { index, cost }) = heap.pop() {
// the heap does not support "update" operations, so we
// insert elements again and if they come out of the heap but have
// been processed earlier we simply skip them.
if cost > landmark.distances[index as usize] {
if landmark.distances[index as usize] <= cost {
continue;
};
counter += 1;
if counter % 1000 == 0 {
println!("Finished {} nodes", counter);
}
landmark.distances[index as usize] = cost;
for edge in graph.get_edges(index as NodeId) {
let edge_start = graph.edge_offsets[index as usize] as usize;
let edge_end = graph.edge_offsets[(index + 1) as usize] as usize;
for edge in graph.edges[edge_start..edge_end].iter() {
let new_cost = cost + edge.cost;
if new_cost < landmark.distances[edge.neighbor as usize] {
@ -67,11 +86,11 @@ impl Landmark {
index: edge.neighbor,
cost: new_cost,
});
landmark.distances[edge.neighbor as usize] = new_cost;
}
}
}
// now the costs to all reachable nodes is calculated.
// now the shortest paths to all reachable nodes is calculated.
landmark
}
@ -79,28 +98,23 @@ impl Landmark {
/// calculates the lower-bounding distance estimate between the 2 nodes
/// via the landmark.
/// If one or more of the nodes are not reachable from the landmark
/// an estimate of `EdgeCost::MAX` is returned.
/// an estimate of `0` is returned.
pub fn estimate(&self, from: NodeId, to: NodeId) -> EdgeCost {
let l_to = self.distances[to];
let l_from = self.distances[from];
if l_to == EdgeCost::MAX || l_from == EdgeCost::MAX {
EdgeCost::MAX
if l_to == EdgeCost::MAX {
0
} else {
// since we are working on an undirected graph we can
// use the distances once from and once to the landmark.
// This leads to l_to - l_from and l_from - l_to (as signed subtractions)
// which except for the sign are the same value.
// We can simply take the bigger one, which is handled
// nicely the abs() function
(l_to as i64 - l_from as i64).abs() as EdgeCost
l_to.saturating_sub(l_from)
}
}
}
impl LandmarkSet {
pub fn random_set(size: usize, graph: &GridGraph) -> Self {
pub fn random_set(size: usize, best_size: usize, graph: &GridGraph) -> Self {
let mut set = LandmarkSet::default();
set.best_size = best_size;
let nodes = graph.get_random_nodes(size);
@ -111,13 +125,11 @@ impl LandmarkSet {
set
}
}
impl LandmarkBestSet<'_> {
pub fn select_best(&mut self, from: NodeId, to: NodeId) {
let mut results = vec![];
for (index, landmark) in self.landmark_set.landmarks.iter().enumerate() {
for (index, landmark) in self.landmarks.iter().enumerate() {
results.push((index, landmark.estimate(from, to)));
}
@ -125,35 +137,24 @@ impl LandmarkBestSet<'_> {
results.reverse();
self.best_landmarks.clear();
for result in results[..(self.best_size.min(results.len()))].iter() {
for result in results[..self.best_size].iter() {
self.best_landmarks.push(result.0);
}
}
pub fn estimate(&self, from: NodeId, to: NodeId) -> EdgeCost {
let mut distance = 0;
for index in &self.best_landmarks {
let candidate = self.landmark_set.landmarks[*index].estimate(from, to);
distance = distance.max(self.landmarks[*index].estimate(from, to));
};
if candidate == EdgeCost::MAX {
continue;
if distance == 0 {
distance = EdgeCost::MAX;
}
distance = distance.max(candidate)
}
//println!("calculated estimate {:?} for {} to {}", distance, from, to);
distance
}
pub fn new<'a>(best_size: usize, landmark_set: &'a LandmarkSet) -> LandmarkBestSet<'a> {
LandmarkBestSet {
best_size,
landmark_set,
best_landmarks: Vec::new(),
}
}
}

View file

@ -4,27 +4,22 @@ use crate::utils::EARTH_RADIUS;
use std::cmp::Ordering;
use std::collections::BinaryHeap;
/// datastructure to hold data required by the A* algorithm
pub struct AStar<'a> {
pub graph: &'a GridGraph,
pub struct AStar {
pub graph: GridGraph,
}
/// An element on the Heap for A*.
/// The Ord and PartialOrd Traits are inverted so that the MaxHeap works as a
/// MinHeap.
/// index is the index of the Node, cost is the cost which the heap sorts after.
/// path_cost is the actual cost of the path to that node.
#[derive(Eq, PartialEq)]
#[derive(Eq)]
struct HeapElement {
index: u32,
// the cost so far plus the estimated cost until we reach the destination
cost: EdgeCost,
cost: EdgeCost, // the cost so far plus the estimated cost until we reach the
// destination
path_cost: EdgeCost, // the cost to reach this node from the start node
ancestor: Option<u32>,
}
impl Ord for HeapElement {
// inverted cmp function, such that the Max-Heap provided by Rust
// can be used as a Min-Heap
fn cmp(&self, other: &Self) -> Ordering {
other.cost.cmp(&self.cost)
}
@ -36,84 +31,80 @@ impl PartialOrd for HeapElement {
}
}
/// A simple haversine distance heuristic.
impl PartialEq for HeapElement {
fn eq(&self, other: &Self) -> bool {
self.cost == other.cost
}
}
pub fn estimate_haversine(node: &GraphNode, destination: &GraphNode) -> EdgeCost {
// simple haversine distance
(node.position.distance_to(&destination.position) * EARTH_RADIUS) as EdgeCost
// let lat_dist_a = (node.position.lat - destination.position.lat).abs();
// let lat_dist_b = (destination.position.lat - node.position.lat).abs();
// (lat_dist_a.min(lat_dist_b) * EARTH_RADIUS) as EdgeCost
}
/// a simple heuristic based on the difference in lattitude between two points
/// The idea is that it is cheaper to calculate than the haversine distance.
pub fn estimate_latitude(node: &GraphNode, destination: &GraphNode) -> EdgeCost {
let lat_dist = (node.position.lat - destination.position.lat).abs();
let lat_dist_a = (node.position.lat - destination.position.lat).abs();
let lat_dist_b = (destination.position.lat - node.position.lat).abs();
(lat_dist * EARTH_RADIUS) as EdgeCost
(lat_dist_a.min(lat_dist_b) * EARTH_RADIUS) as EdgeCost
}
impl AStar {
impl AStar<'_> {
/// calculates the shortest path from start to end given the `estimate`
/// heuristic function.
///
/// Returns `None` if no path exists.
pub fn shortest_path<F>(&self, start: &GraphNode, end: &GraphNode, estimate: F) -> Option<Route>
where
F: Fn(&GraphNode, &GraphNode) -> EdgeCost,
{
where F: Fn(&GraphNode, &GraphNode) -> EdgeCost {
let mut heap = BinaryHeap::new();
heap.push(HeapElement {
cost: estimate(start, end),
path_cost: 0,
index: start.index,
ancestor: None,
});
let mut distance = vec![EdgeCost::MAX; self.graph.nodes.len()];
let mut ancestor: Vec<Option<u32>> = vec![None; self.graph.nodes.len()];
distance[start.index as usize] = 0;
let mut popcount = 0;
while let Some(HeapElement {
index, path_cost, ..
index,
cost, // the cost value, no longer needed, because it is only important for the
// distance estimate
path_cost,
ancestor: prev,
}) = heap.pop()
{
//println!("working on node {} with cost {} and path cost {}", index, cost, path_cost);
popcount += 1;
// the heap does not support "update" operations, so we
// insert elements again and if they come out of the heap but have
// been processed earlier (which implies a shorter distance)
// we simply skip them.
if path_cost > distance[index as usize] {
if distance[index as usize] <= path_cost {
continue;
};
popcount += 1;
distance[index as usize] = path_cost;
ancestor[index as usize] = prev;
if index == end.index {
break;
}
for edge in self.graph.get_edges(index as usize).iter() {
//println!("working on edge {:?}", edge);
let new_cost = path_cost + edge.cost;
if new_cost < distance[edge.neighbor as usize] {
//println!("adding new element to heap");
let remaining_cost = estimate(&self.graph.nodes[edge.neighbor as usize], end);
// check for unreachable nodes
if remaining_cost == EdgeCost::MAX {
continue;
}
distance[edge.neighbor as usize] = new_cost;
ancestor[edge.neighbor as usize] = Some(index);
heap.push(HeapElement {
heap.push(HeapElement{
index: edge.neighbor,
cost: new_cost + remaining_cost,
cost: new_cost + estimate(&self.graph.nodes[edge.neighbor as usize], end),
path_cost: new_cost,
ancestor: Some(index),
});
}
}
@ -121,6 +112,26 @@ impl AStar<'_> {
println!("popped {} elements from the heap", popcount);
Route::construct(self.graph, &ancestor, &distance, start, end)
// now the route calculation is done. If a route exist we can construct
// it from the ancestors.
if ancestor[end.index as usize].is_some() {
let mut route = Route {
cost: distance[end.index as usize],
nodes: Vec::new(),
};
let mut current = end.index;
while current != start.index {
route.nodes.push(self.graph.nodes[current as usize].position);
current = ancestor[current as usize].unwrap();
}
route.nodes.push(self.graph.nodes[current as usize].position);
route.nodes.reverse();
return Some(route);
}
None
}
}

View file

@ -1,94 +0,0 @@
use bincode;
use clap::Parser;
use fapra_osm_2::alt::{Landmark, LandmarkSet};
use fapra_osm_2::utils::load_graph;
use fapra_osm_2::coordinates::{RadianCoordinate, DegreeCoordinate};
use std::fs::{read_to_string, File};
use std::io::prelude::*;
use std::process::exit;
use geojson::{GeoJson, Value};
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about=None)]
struct Args {
/// the FMI file to load
#[clap(short, long)]
graph: String,
/// the file to which to write the landmarks
#[clap(short, long)]
output: String,
/// the geojson file from which to load the landmarks
#[clap(short, long)]
geojson: String,
}
fn main() {
let args = Args::parse();
let graph = load_graph(&args.graph);
let mut output = match File::create(args.output.clone()) {
Ok(f) => f,
Err(e) => {
println!("Error while creating the file {}: {}", args.output, e);
exit(2)
}
};
let geojson_str = match read_to_string(args.geojson.clone()) {
Ok(f) => f,
Err(e) => {
println!("Error while opening the file {}: {}", args.geojson, e);
exit(2)
}
};
let geojson: GeoJson = geojson_str.parse::<GeoJson>().unwrap();
let features = match geojson {
GeoJson::FeatureCollection(features) => features.features,
_ => {
println!("Expected a FeatureCollection, didn't get one!");
exit(3);
}
};
// parse the GeoJSON
let mut points: Vec<RadianCoordinate> = Vec::new();
for feature in features.iter() {
if let Some(geometry) = &feature.geometry {
if let Value::Point(point) = &geometry.value {
if let Ok(coordinate) = DegreeCoordinate::from_geojson_position(point.clone()) {
points.push(RadianCoordinate::from(coordinate));
continue;
}
}
}
println!("failed to parse {:?}", feature);
exit(4);
}
let mut set = LandmarkSet::default();
// generate the landmarks
for position in points.iter() {
if let Some(node) = graph.get_nearest_node(*position) {
let landmark = Landmark::generate(*node, &graph);
set.landmarks.push(landmark);
} else {
println!("Could not find a nearest node to {:?}", position);
exit(5);
}
}
let encoded = bincode::serialize(&set).unwrap();
output
.write_all(&encoded)
.expect("Error while writing LandmarkSet data");
}

View file

@ -1,88 +0,0 @@
use bincode;
use clap::Parser;
use fapra_osm_2::alt::{Landmark, LandmarkSet};
use fapra_osm_2::gridgraph::EdgeCost;
use fapra_osm_2::utils::load_graph;
use std::fs::File;
use std::io::prelude::*;
use std::process::exit;
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about=None)]
struct Args {
/// the FMI file to load
#[clap(short, long)]
graph: String,
/// the file to which to write the landmarks
#[clap(short, long)]
output: String,
/// the amount of landmarks to generate
#[clap(short, long)]
amount: usize,
}
fn main() {
let args = Args::parse();
let graph = load_graph(&args.graph);
let mut output = match File::create(args.output.clone()) {
Ok(f) => f,
Err(e) => {
println!("Error while creating the file {}: {}", args.output, e);
exit(2)
}
};
fn compute_next_index(costs: &mut Vec<EdgeCost>, landmark: &Landmark) -> usize {
let mut max_index = 0;
let mut max_cost = 0;
for (i, cost) in landmark.distances.iter().enumerate() {
if *cost < costs[i] {
costs[i] = *cost;
}
if costs[i] > max_cost && costs[i] != EdgeCost::MAX {
max_cost = costs[i];
max_index = i;
}
}
max_index
}
let initial_node = graph.get_random_node().unwrap();
println!("selected initial node: {:?}", initial_node);
let mut landmark = Landmark::generate(*initial_node, &graph);
let mut costs: Vec<EdgeCost> = landmark.distances.clone();
let mut set = LandmarkSet::default();
let mut next = compute_next_index(&mut costs, &landmark);
set.landmarks.push(landmark);
while set.landmarks.len() < args.amount {
let node = graph.nodes[next];
println!(
"the next node will be {:?} with a distance of {}",
node, costs[node.index as usize]
);
landmark = Landmark::generate(node, &graph);
next = compute_next_index(&mut costs, &landmark);
set.landmarks.push(landmark);
}
let encoded = bincode::serialize(&set).unwrap();
output
.write_all(&encoded)
.expect("Error while writing LandmarkSet data");
}

View file

@ -1,4 +1,5 @@
use fapra_osm_2::utils::load_graph;
use fapra_osm_2::gridgraph::GridGraph;
use std::fs::File;
use clap::Parser;
use std::process::exit;
use rand::seq::SliceRandom;
@ -20,7 +21,21 @@ struct Args {
fn main() {
let args = Args::parse();
let graph = load_graph(&args.graph);
let file = match File::open(args.graph.clone()) {
Ok(f) => f,
Err(e) => {
println!("Error while opening the file {}: {}", args.graph, e);
exit(1)
}
};
let graph = match GridGraph::from_fmi_file(file) {
Ok(g) => g,
Err(e) => {
println!("Error while reading the graph: {:?}", e);
exit(1);
}
};
let mut rng = rand::thread_rng();

View file

@ -15,15 +15,6 @@ struct Args {
/// the output file to write the grid to
#[clap(short, long)]
output: String,
/// the latitude resolution
#[clap(short, long)]
lat: usize,
/// the longitude resolution
#[clap(short, long)]
lon: usize,
}
fn main() {
@ -56,7 +47,7 @@ fn main() {
println!("{:?}", coasts.polygons[0].bbox);
let grid = GridGraph::generate_regular_grid(args.lat, args.lon, Some(&coasts));
let grid = GridGraph::generate_regular_grid(10, 10, Some(&coasts));
// let grid = GridGraph::generate_regular_grid(3, 4, None);
match grid.write_fmi_file(output) {

View file

@ -1,18 +1,28 @@
use fapra_osm_2::utils::load_graph;
use fapra_osm_2::gridgraph::GridGraph;
use clap::Parser;
use std::fs::File;
use std::process::exit;
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about=None)]
struct Args {
/// the FMI file to load
#[clap(short, long)]
graph: String,
input: String,
}
fn main() {
let args = Args::parse();
let graph = load_graph(&args.graph);
let file = match File::open(args.input.clone()) {
Ok(f) => f,
Err(e) => {
println!("Error while opening file: {}", e);
exit(1);
}
};
println!("{}", graph.to_geojson().to_string());
let grid = GridGraph::from_fmi_file(file).unwrap();
println!("{}", grid.to_geojson().to_string());
}

View file

@ -1,8 +1,8 @@
use clap::Parser;
use fapra_osm_2::alt::LandmarkBestSet;
use fapra_osm_2::astar::{estimate_haversine, AStar};
use fapra_osm_2::gridgraph::NodeId;
use fapra_osm_2::utils::{load_graph, load_landmarks, RoutingQuery};
use fapra_osm_2::alt::LandmarkSet;
use fapra_osm_2::astar::{estimate_haversine, estimate_latitude, AStar};
use fapra_osm_2::gridgraph::{GridGraph, NodeId};
use fapra_osm_2::utils::RoutingQuery;
use serde_json;
use std::fs::File;
use std::io::BufReader;
@ -20,9 +20,9 @@ struct Args {
#[clap(short, long)]
targets: String,
/// landmark file. ALT Benchmarks are only run, if this is given.
/// landmark file
#[clap(short, long)]
landmarks: Option<String>,
landmarks: String,
/// run dijkstra
#[clap(long, action)]
@ -32,17 +32,29 @@ struct Args {
#[clap(long, action)]
astar: bool,
/// How many of the given landmarks to select
#[clap(long, action, default_value_t = 4)]
alt_best_size: usize,
/// run ALT
#[clap(long, action)]
alt: bool,
}
fn main() {
let args = Args::parse();
let graph = load_graph(&args.graph);
let file = match File::open(args.graph.clone()) {
Ok(f) => f,
Err(e) => {
println!("Error while opening the file {}: {}", args.graph, e);
exit(1)
}
};
let astar = AStar { graph: &(*graph) };
let graph = match GridGraph::from_fmi_file(file) {
Ok(g) => g,
Err(e) => {
println!("Error while reading the graph: {:?}", e);
exit(1);
}
};
let targets = match File::open(args.targets.clone()) {
Ok(f) => f,
@ -54,33 +66,23 @@ fn main() {
let targets: Vec<RoutingQuery> = serde_json::from_reader(BufReader::new(targets)).unwrap();
if let Some(landmark_path) = args.landmarks {
let landmarks = load_landmarks(&landmark_path);
println!("running ALT");
let mut best_landmarks = LandmarkBestSet::new(args.alt_best_size, &landmarks);
let start = Instant::now();
for query in targets.iter() {
// println!("working on {:?}", query);
let source = astar.graph.nodes[query.source];
let destination = astar.graph.nodes[query.destination];
best_landmarks.select_best(source.index as NodeId, destination.index as NodeId);
let _result = astar.shortest_path(&source, &destination, |src, dest| {
best_landmarks.estimate(src.index as NodeId, dest.index as NodeId)
});
let landmarks = match File::open(args.landmarks.clone()) {
Ok(f) => f,
Err(e) => {
println!(
"Error while opening landmark file {}: {:?}",
args.landmarks, e
);
exit(1);
}
};
let elapsed = start.elapsed();
let mut landmarks: LandmarkSet = bincode::deserialize_from(BufReader::new(landmarks)).unwrap();
landmarks.best_size = 4;
let time_per_route = elapsed.as_secs_f64() / (targets.len() as f64);
let astar = AStar { graph: *graph };
println!("It took {} seconds per route for ALT.", time_per_route);
}
println!("{:?}", args);
if args.astar {
println!("running A*");
@ -107,13 +109,13 @@ fn main() {
let start = Instant::now();
for query in targets.iter() {
// println!("working on {:?}", query);
println!("working on {:?}", query);
let source = astar.graph.nodes[query.source];
let destination = astar.graph.nodes[query.destination];
let _result = astar.graph.shortest_path(&source, &destination);
let result = astar.graph.shortest_path(&source, &destination);
//println!("{}", result.unwrap().to_geojson());
println!("{}", result.unwrap().to_geojson());
}
let elapsed = start.elapsed();
@ -121,4 +123,27 @@ fn main() {
let time_per_route = elapsed.as_secs_f64() / (targets.len() as f64);
println!("It took {} seconds per round for Dijkstra.", time_per_route);
}
if args.alt {
println!("running ALT");
// Landmarks
let start = Instant::now();
for query in targets.iter() {
let source = astar.graph.nodes[query.source];
let destination = astar.graph.nodes[query.destination];
landmarks.select_best(source.index as NodeId, destination.index as NodeId);
let _result = astar.shortest_path(&source, &destination, |src, dest| {
landmarks.estimate(src.index as NodeId, dest.index as NodeId)
});
}
let elapsed = start.elapsed();
let time_per_route = elapsed.as_secs_f64() / (targets.len() as f64);
println!("It took {} seconds per route for ALT.", time_per_route);
}
}

24
src/bin/polygon_print.rs Normal file
View file

@ -0,0 +1,24 @@
use fapra_osm_2::coordinates::RadianCoordinate;
use fapra_osm_2::polygon::Polygon;
use geojson::FeatureCollection;
/// a simple program to show the geojson export of polygons
fn main() {
let nodes = vec![
RadianCoordinate::from_degrees(10.0, 15.0),
RadianCoordinate::from_degrees(20.0, 10.0),
RadianCoordinate::from_degrees(20.0, 25.0),
RadianCoordinate::from_degrees(10.0, 15.0),
];
let outside1 = RadianCoordinate::from_degrees(30.0, 15.0);
let outside2 = RadianCoordinate::from_degrees(-10.0, 15.0);
let polygon =
Polygon::build_polygon(nodes, outside1, outside2).expect("error while building polygon");
let fc: FeatureCollection = polygon.into();
println!("{}", fc.to_string());
}

View file

@ -1,33 +1,26 @@
#[macro_use]
extern crate rocket;
#[macro_use] extern crate rocket;
use clap::Parser;
use fapra_osm_2::alt::{LandmarkBestSet, LandmarkSet};
use fapra_osm_2::astar::{estimate_haversine, AStar};
use fapra_osm_2::coordinates::{DegreeCoordinate, RadianCoordinate};
use fapra_osm_2::gridgraph::{GridGraph, NodeId, Route};
use fapra_osm_2::utils::{load_graph, load_landmarks};
use std::process::exit;
use std::fs::File;
use fapra_osm_2::gridgraph::{GridGraph, Route};
use fapra_osm_2::coordinates::{RadianCoordinate, DegreeCoordinate};
use rand::seq::SliceRandom;
use serde::Serialize;
use std::time::Instant;
use rocket::State;
use rocket::response::status::BadRequest;
use rocket::form::Form;
use rocket::fs::FileServer;
use rocket::response::status::BadRequest;
use rocket::serde::json::Json;
use rocket::State;
use rocket_dyn_templates::{context, Engines, Template};
use rocket_dyn_templates::{Template, context, Engines};
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about=None)]
struct Args {
/// name of the FMI file to read
#[clap(short, long)]
graph: String,
/// the landmarks to load
#[clap(short, long)]
landmarks: String,
filename: String,
}
#[get("/")]
@ -54,107 +47,73 @@ fn random_route(graphwrapper: &State<GraphWrapper>) -> String {
struct RouteQuery<'r> {
r#from: &'r str,
r#to: &'r str,
r#algorithm: &'r str,
}
#[derive(Debug, Serialize)]
#[derive(Debug)]
#[derive(Serialize)]
pub struct RouteResponse {
success: bool,
route: Option<Route>,
algorithm: String,
}
#[post("/route", data = "<routequery>")]
fn route(
routequery: Form<RouteQuery<'_>>,
graphwrapper: &State<GraphWrapper>,
) -> Result<Json<RouteResponse>, BadRequest<String>> {
fn route(routequery: Form<RouteQuery<'_>>, graphwrapper: &State<GraphWrapper>) -> Result<Json<RouteResponse>, BadRequest<String>> {
let from = match DegreeCoordinate::from_string_tuple(routequery.from) {
Ok(from) => RadianCoordinate::from(from),
Err(e) => {
return Result::Err(BadRequest(Some(format!(
"Error while parsing from field: {:?}",
e
))));
}
Err(e) => {return Result::Err(BadRequest(Some(format!("Error while parsing from field: {:?}", e))));}
};
let to = match DegreeCoordinate::from_string_tuple(routequery.to) {
Ok(to) => RadianCoordinate::from(to),
Err(e) => {
return Result::Err(BadRequest(Some(format!(
"Error while parsing to field: {:?}",
e
))));
}
Err(e) => {return Result::Err(BadRequest(Some(format!("Error while parsing to field: {:?}", e))));}
};
let from = graphwrapper.graph.get_nearest_node(from).unwrap();
let to = graphwrapper.graph.get_nearest_node(to).unwrap();
println!("Working on a route from {:?} to {:?}", from, to);
let route = graphwrapper.graph.shortest_path(from, to);
let astar = AStar {
graph: &graphwrapper.graph,
};
println!("from: {:?}, to: {:?}", from, to);
let mut algorithm = routequery.algorithm;
let start = Instant::now();
let route = if algorithm == "astar-haversine" {
println!("running A* with haversine distance");
astar.shortest_path(from, to, estimate_haversine)
} else if algorithm == "alt" {
println!("running ALT");
let mut best_landmarks = LandmarkBestSet::new(4, &graphwrapper.landmarks);
best_landmarks.select_best(from.index as usize, to.index as usize);
astar.shortest_path(from, to, |src, dest| {
best_landmarks.estimate(src.index as NodeId, dest.index as NodeId)
})
} else {
println!("running dijkstra");
algorithm = "dijkstra";
graphwrapper.graph.shortest_path(from, to)
};
let time = start.elapsed();
println!("The query took {} ms", time.as_millis());
let response = RouteResponse {
success: route.is_some(),
route,
algorithm: algorithm.to_string(),
};
let response = RouteResponse{success: route.is_some(), route};
Ok(Json(response))
}
struct GraphWrapper {
graph: GridGraph,
landmarks: LandmarkSet,
graph: Box<GridGraph>
}
#[launch]
fn rocket() -> _ {
let args = Args::parse();
let graph = load_graph(&args.graph);
println!("Loading file from {}", args.filename);
let file = match File::open(args.filename.clone()) {
Ok(f) => f,
Err(e) => {
println!("Error while opening the file {}: {}", args.filename, e);
exit(1)
}
};
let landmarks = load_landmarks(&args.landmarks);
let graph = match GridGraph::from_fmi_file(file) {
Ok(g) => g,
Err(e) => {
println!("Error while reading the graph: {:?}", e);
exit(1);
}
};
println!("Listening on http://localhost:8000");
println!("Loaded graph file");
// let graph = GridGraph::generate_regular_grid(10,10);
rocket::build()
.manage(GraphWrapper {
graph: *graph,
landmarks,
})
.manage(GraphWrapper{graph: graph})
.mount("/", routes![index])
.mount("/", routes![random_route])
.mount("/", routes![route])
.mount("/static", FileServer::from("static"))
.attach(Template::custom(|engines: &mut Engines| {
engines.handlebars.set_dev_mode(true);
}))
.attach(Template::custom(|engines: &mut Engines| {engines.handlebars.set_dev_mode(true);}))
}

View file

@ -1,7 +1,7 @@
use bincode;
use clap::Parser;
use fapra_osm_2::alt::LandmarkSet;
use fapra_osm_2::utils::load_graph;
use fapra_osm_2::gridgraph::GridGraph;
use std::fs::File;
use std::io::prelude::*;
use std::process::exit;
@ -11,7 +11,7 @@ use std::process::exit;
struct Args {
/// the FMI file to load
#[clap(short, long)]
graph: String,
input: String,
/// the file to which to write the landmarks
#[clap(short, long)]
@ -25,7 +25,16 @@ struct Args {
fn main() {
let args = Args::parse();
let graph = load_graph(&args.graph);
let file = match File::open(args.input.clone()) {
Ok(f) => f,
Err(e) => {
println!("Error while opening file: {}", e);
exit(1);
}
};
let grid = GridGraph::from_fmi_file(file).unwrap();
println!("finished loading grid from file");
let mut output = match File::create(args.output.clone()) {
Ok(f) => f,
@ -35,7 +44,7 @@ fn main() {
}
};
let set = LandmarkSet::random_set(args.amount, &graph);
let set = LandmarkSet::random_set(args.amount, 4, &grid);
let encoded = bincode::serialize(&set).unwrap();

View file

@ -51,7 +51,6 @@ impl From<RadianCoordinate> for DegreeCoordinate {
}
}
impl RadianCoordinate {
/// Builds a RadianCoordinate from latitude and longitude given in
/// degrees.
@ -155,20 +154,6 @@ impl DegreeCoordinate {
Ok(DegreeCoordinate{lat, lon})
}
/// tries to parse a DegreeCoordinate from a GeoJSON Position
pub fn from_geojson_position(position: geojson::Position) -> Result<Self, String> {
if position.len() != 2 {
return Err("String has more than 2 values".to_string());
}
let lat = position[1];
let lon = position[0];
Ok(DegreeCoordinate { lat, lon })
}
}
/// normalizes longitude values given in radians to the range (-PI, PI]

View file

@ -1,16 +1,16 @@
use crate::coordinates::{DegreeCoordinate, RadianCoordinate};
use crate::polygonset::PolygonSet;
use crate::utils::EARTH_RADIUS;
use geojson::{Feature, FeatureCollection, Geometry, Position, Value};
use rand::seq::SliceRandom;
use serde::ser::{SerializeStruct, Serializer};
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::{BinaryHeap, HashMap};
use std::f64::consts::{FRAC_PI_2, PI, TAU};
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::vec::Vec;
use crate::utils::EARTH_RADIUS;
use rand::seq::SliceRandom;
use serde::{Serialize, Deserialize};
/// Type for all edge costs
/// Allows for easy adjustments for bigger/smaller number types if that is
@ -107,34 +107,6 @@ impl LookupGrid {
}
impl Route {
/// Constructs a route from the start to the end node on the graph
/// based on the ancestor and distance lists of a routing algorithm.
pub fn construct(graph: &GridGraph, ancestors: &Vec<Option<u32>>, distance: &Vec<EdgeCost>, start: &GraphNode, end: &GraphNode) -> Option<Self>{
if ancestors[end.index as usize].is_some() {
let mut route = Route {
cost: distance[end.index as usize],
nodes: Vec::new(),
};
let mut current = end.index;
while current != start.index {
route
.nodes
.push(graph.nodes[current as usize].position);
current = ancestors[current as usize].unwrap();
}
route
.nodes
.push(graph.nodes[current as usize].position);
route.nodes.reverse();
return Some(route);
}
None
}
pub fn to_geojson(&self) -> Box<FeatureCollection> {
let mut features: Box<FeatureCollection> = Box::new(FeatureCollection {
bbox: None,
@ -162,6 +134,8 @@ impl Route {
}
impl GridGraph {
/// selects a single random graph node.
pub fn get_random_node(&self) -> Option<&GraphNode> {
let mut rng = rand::thread_rng();
@ -169,11 +143,14 @@ impl GridGraph {
self.nodes.choose(&mut rng)
}
/// selects up to `amount` random but distinct nodes from the graph.
pub fn get_random_nodes(&self, amount: usize) -> Vec<&GraphNode> {
pub fn get_random_nodes(&self, amount: usize) -> Vec<&GraphNode>{
let mut rng = rand::thread_rng();
self.nodes.choose_multiple(&mut rng, amount).collect()
}
/// calculates the shortest path from the start node to the end node.
@ -192,10 +169,7 @@ impl GridGraph {
// inverted cmp function, such that the Max-Heap provided by Rust
// can be used as a Min-Heap
fn cmp(&self, other: &Self) -> Ordering {
other
.cost
.cmp(&self.cost)
.then_with(|| self.index.cmp(&other.index))
other.cost.cmp(&self.cost).then_with(|| self.index.cmp(&other.index))
}
}
@ -216,7 +190,11 @@ impl GridGraph {
let mut popcount = 0;
while let Some(DijkstraElement { index, cost }) = heap.pop() {
while let Some(DijkstraElement {
index,
cost,
}) = heap.pop()
{
popcount += 1;
if index == end.index {
@ -231,13 +209,16 @@ impl GridGraph {
continue;
};
//println!("working on node {} with cost {}", index, cost);
// println!("working on node {} with cost {}", index, cost);
for edge in self.get_edges(index as usize).iter() {
let edge_start = self.edge_offsets[index as usize] as usize;
let edge_end = self.edge_offsets[(index + 1) as usize] as usize;
for edge in self.edges[edge_start..edge_end].iter() {
let new_cost = cost + edge.cost;
if new_cost < distance[edge.neighbor as usize] {
//println!("found cheaper edge {:?} with cost {} than previous best {}", edge, new_cost, distance[edge.neighbor as usize]);
// println!("found cheaper edge {:?} with cost {} than previous best {}", edge, new_cost, distance[edge.neighbor as usize]);
distance[edge.neighbor as usize] = new_cost;
ancestor[edge.neighbor as usize] = Some(index);
//println!("adding new element to heap");
@ -246,14 +227,34 @@ impl GridGraph {
cost: new_cost,
});
} else {
//println!("edge {:?} is more expensive ({}) than the previous best {}", edge, new_cost, distance[edge.neighbor as usize]);
// println!("edge {:?} is more expensive ({}) than the previous best {}", edge, new_cost, distance[edge.neighbor as usize]);
}
}
}
println!("popped {} elements from the heap", popcount);
Route::construct(self, &ancestor, &distance, start, end)
// now the route calculation is done. If a route exist we can construct
// it from the ancestors.
if ancestor[end.index as usize].is_some() {
let mut route = Route {
cost: distance[end.index as usize],
nodes: Vec::new(),
};
let mut current = end.index;
while current != start.index {
route.nodes.push(self.nodes[current as usize].position);
current = ancestor[current as usize].unwrap();
}
route.nodes.push(self.nodes[current as usize].position);
route.nodes.reverse();
return Some(route);
}
None
}
/// returns the GraphNode nearest to that positon.
@ -296,6 +297,7 @@ impl GridGraph {
let mut gridgraph = Box::new(GridGraph::default());
let reader = BufReader::new(file);
let lines = reader.lines();
let mut total_node_count: u32 = 0;
@ -303,7 +305,7 @@ impl GridGraph {
let mut node_count: u32 = 0;
let mut edge_count: u32 = 0;
let mut node_map: HashMap<u32, u32> = HashMap::new();
let mut nodes: HashMap<u32, GraphNode> = HashMap::new();
let mut edges: HashMap<u32, Vec<TemporaryGraphEdge>> = HashMap::new();
enum ParserState {
@ -314,7 +316,6 @@ impl GridGraph {
Done,
}
#[derive(Debug)]
struct TemporaryGraphEdge {
src: u32,
dst: u32,
@ -387,46 +388,25 @@ impl GridGraph {
match state {
ParserState::NodeCount => {
total_node_count = parse_int(line)?;
println!("found node count: {}", total_node_count);
state = ParserState::EdgeCount {};
}
ParserState::EdgeCount => {
total_edge_count = parse_int(line)?;
println!("found edge count: {}", total_edge_count);
state = ParserState::Nodes {};
}
ParserState::Nodes => {
let mut node = parse_node(line)?;
// println!("parsed node: {:?}", node);
let node = parse_node(line)?;
nodes.entry(node.index).or_insert(node);
node_count += 1;
let new_index = gridgraph.nodes.len() as u32;
node_map.insert(node.index, new_index);
node.index = new_index;
gridgraph.nodes.push(node);
if node_count == total_node_count {
println!("done parsing nodes: {} nodes parsed", node_count);
state = ParserState::Edges {};
}
}
ParserState::Edges => {
let mut edge = parse_edge(line)?;
// println!("parsed edge: {:?}", edge);
edge_count += 1;
let src = edge.src;
let dst = edge.dst;
edge.src = node_map[&src];
edge.dst = node_map[&dst];
let edge = parse_edge(line)?;
edges.entry(edge.src).or_default().push(edge);
edge_count += 1;
if edge_count == total_edge_count {
println!("done parsing edges: {} edges parsed", edge_count);
state = ParserState::Done;
}
}
@ -443,22 +423,31 @@ impl GridGraph {
return Err(FmiParsingError::WrongEdgeAmount);
}
// println!("{:?}", gridgraph);
// add the nodes to the gridgraph
for (_, mut item) in nodes.iter_mut() {
item.index = gridgraph.nodes.len() as u32;
gridgraph.nodes.push(item.clone());
}
// add the edges
gridgraph.edge_offsets.push(0);
for index in 0..gridgraph.nodes.len() {
//println!("working on the edges for node {}", index);
for edge in edges.entry(index as u32).or_default() {
//println!("working on edge {:?}", edge);
for index in nodes.keys() {
for edge in edges.entry(*index).or_default() {
if !nodes.contains_key(&edge.dst) {
println!(
"An edge refers to the non-existing destination node {} skipping.",
edge.dst
);
continue;
}
let dst_index = nodes[&edge.dst].index;
gridgraph.edges.push(GraphEdge {
cost: edge.cost,
neighbor: edge.dst,
neighbor: dst_index,
});
}
gridgraph.edge_offsets.push(gridgraph.edges.len());
}
// println!("{:?}", gridgraph);
gridgraph.lookup_grid = LookupGrid::new(100, 100, &gridgraph.nodes);
@ -496,6 +485,7 @@ impl GridGraph {
lon_resolution: usize,
polygons: Option<&PolygonSet>,
) -> Box<GridGraph> {
#[derive(Debug)]
struct TemporaryGraphNode {
final_index: u32,
@ -528,11 +518,7 @@ impl GridGraph {
Some(self.lon_index + (self.lat_index - 1) * lon_resolution)
}
pub fn top_neighbor_index(
&self,
lat_resolution: usize,
lon_resolution: usize,
) -> Option<usize> {
pub fn top_neighbor_index(&self, lat_resolution: usize, lon_resolution: usize) -> Option<usize> {
if self.lat_index >= lat_resolution - 1 {
return None;
};
@ -586,6 +572,7 @@ impl GridGraph {
// add all edges to the final graph
for node in temp_nodes.iter() {
println!("working on edges for node {:?}", node);
if !node.used {
continue;

View file

@ -1,78 +1,9 @@
use crate::alt::LandmarkSet;
use crate::gridgraph::{EdgeCost, GridGraph};
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fs::File;
use std::io::BufReader;
use std::process::exit;
/// an approximation of the earths radius.
pub const EARTH_RADIUS: f64 = 6_371_000.0; // meters
/// serialization format for routing queries.
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
pub struct RoutingQuery {
pub struct RoutingQuery{
pub source: usize,
pub destination: usize,
}
/// loads the graph from the given path.
/// exits if an error occurs during loading.
pub fn load_graph(path: &str) -> Box<GridGraph> {
println!("Loading file from {}", path);
let file = match File::open(path) {
Ok(f) => f,
Err(e) => {
println!("Error while opening the file {}: {}", path, e);
exit(1)
}
};
let graph = match GridGraph::from_fmi_file(file) {
Ok(g) => g,
Err(e) => {
println!("Error while reading the graph: {:?}", e);
exit(1);
}
};
println!("Loaded graph file");
graph
}
/// loads a set of landmarks from the given path.
/// exits if an error occurs during loading.
pub fn load_landmarks(path: &str) -> LandmarkSet {
let landmarks = match File::open(path) {
Ok(f) => f,
Err(e) => {
println!("Error while opening landmark file {}: {:?}", path, e);
exit(1);
}
};
bincode::deserialize_from(BufReader::new(landmarks)).unwrap()
}
/// A heap element for Dijkstra's algorithm.
///
/// The comparison functions are inverted, so that Rusts MaxHeap works as a
/// MinHeap.
#[derive(Eq, PartialEq)]
pub struct DijkstraElement {
pub index: u32,
pub cost: EdgeCost,
}
impl Ord for DijkstraElement {
fn cmp(&self, other: &Self) -> Ordering {
other.cost.cmp(&self.cost)
}
}
impl PartialOrd for DijkstraElement {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

View file

@ -67,11 +67,6 @@ function set_route_cost(cost) {
field.value = cost;
}
function set_algorithm_type(alg) {
let field = document.getElementById("algorithm_used");
field.value = alg;
}
async function get_route() {
let form = document.getElementById("queryform");
data = new FormData(form);
@ -79,7 +74,6 @@ async function get_route() {
let response = await(fetch("/route", { method: "POST", body: data }));
let result = await response.json();
set_algorithm_type(result.algorithm);
if (result.success === true) {
set_route_cost(result.route.cost);

View file

@ -31,20 +31,10 @@
<input type="text" class="form-control" name="to" id="to">
</div>
<div class="form group">
<label for="algorithm">Algorithm</label>
<select name="algorithm" id="algorithm" class="form-control">
<option value="dijkstra">Dijkstra</option>
<option value="astar-haversine">A* with haversine</option>
<option value="alt">ALT</option>
</select>
</div>
</form>
<button class="btn btn-primary" onclick="get_route()">Submit</button>
<button class="btn btn-primary" onclick="get_random_route()">Random Route</button>
<p>Route Cost: <input class="form-control " type="text" id="routecost" readonly></p>
<p>Algorithm: <input class="form-control " type="text" id="algorithm_used" readonly></p>
</div>
<div id="map" class="col-9"></div>
<script type="text/javascript" src="/static/js/script.js"></script>

View file

@ -1,17 +0,0 @@
#!/usr/bin/env python3
from os import system
from sys import argv, exit
if len(argv) != 3:
print(f"usage: {argv[0]} <graph> <output_prefix>")
exit(1)
graph = argv[1]
output = argv[2]
for i in range(2, 7):
num = 2**i
system(f"cargo run --release --bin=gen_landmarks_greedy -- --graph={graph} --output={output}_greedy_{ num }.bin --amount { num }")
system(f"cargo run --release --bin=gen_landmarks_random -- --graph={graph} --output={output}_random_{ num }.bin --amount { num }")

View file

@ -1,128 +0,0 @@
#!/usr/bin/env python3
from sys import argv, exit
import os
from typing import Tuple, List
import re
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
if len(argv) != 2:
print(f"Usage: { argv[0] } <results_dir>")
exit(1)
path = argv[1]
files = [f for f in os.listdir(path) if os.path.isfile(f"{ path }/{f}")]
def parse_file(file: str) -> Tuple[float, List[int]]:
pops = list()
time = None
with open(file) as f:
for line in f.readlines():
m = re.match(r"popped\s(?P<pops>\d+)\s.*", line)
if m is not None:
pops.append(int(m.groupdict()["pops"]))
continue
m = re.match(r"It took\s(?P<time>\S+).*", line)
if m is not None:
time = float(m.groupdict()["time"])
if len(pops) == 0:
raise Exception(f"Parsing { file } failed, no heap pops found!")
return time, pops
frames = list()
for i, file in enumerate(files):
time, pops = parse_file(f"{ path }/{ file }")
parts = file.split(".")[0].split("_")
data = {
"name": parts[0],
"time": time,
"pops": int(sum(pops)),
}
if len(parts) > 1:
data["num_landmarks"] = int(parts[1])
if len(parts) > 2:
data["best_size"] = int(parts[2])
frames.append(pd.DataFrame(data, index=[i]))
results = pd.concat(frames)
# dijkstra vs A* vs greedy and random ALT
#condition = ((results["name"] == "astar")
# | (results["name"] == "dijkstra")
# | ((results["num_landmarks"] == 32) & (results["best_size"] == 4)))
#
# basic = results[condition].sort_values(by=["time"], ascending=False)
# names = basic["name"].to_list()
# for i in range(len(names)):
# if names[i] == "greedy":
# names[i] = "ALT 32 greedy"
# if names[i] == "random":
# names[i] = "ALT 32 random"
# times = basic["time"] * 1000
#
# fig, ax = plt.subplots()
#
# bar = ax.bar(names, times)
# ax.set_title("Average query time by algorithm")
# ax.set_ylabel("time in ms")
# ax.bar_label(bar)
# fig.savefig("basic.svg")
# plt.show()
# comparison greedy vs. random, best_size = 4
condition = (((results["name"] == "greedy") | (results["name"] == "random")) & (results["best_size"] == 4))
data = results[condition]
greedy = data[data["name"] == "greedy"]
random = data[data["name"] == "random"]
print(greedy)
print(random)
labels = greedy["num_landmarks"].to_list()
labels = [int(label) for label in labels]
greedy_times = greedy["time"].to_list()
random_times = random["time"].to_list()
width = 0.35
fig, ax = plt.subplots()
x = np.arange(len(labels))
greedy_bars = ax.bar(x - width/2, greedy_times, width, label="Greedy")
random_bars = ax.bar(x + width/2, random_times, width, label="Random")
ax.set_title("Comparison of ALT with random and greedy landmark selection")
ax.set_ylabel("time in ms")
ax.set_xlabel("number of landmarks")
ax.set_xticks(x, labels)
ax.legend()
# ax.bar_label(greedy_bars)
# ax.bar_label(random_bars)
fig.save("random_greedy_comp.svg")
plt.show()
# comparison greedy best_size
# comparison random best_size

View file

@ -1,28 +0,0 @@
#!/usr/bin/env python3
from os import system
from sys import argv, exit
if len(argv) != 4:
print(f"usage: {argv[0]} <graph> <landmark_prefix> <targets>")
exit(1)
graph = argv[1]
landmarks = argv[2]
targets = argv[3]
print("running A*")
system(f"cargo run --release --bin=performance -- --graph={graph} --astar --targets {targets} > results/astar.txt")
print("running Dijkstra")
system(f"cargo run --release --bin=performance -- --graph={graph} --dijkstra --targets {targets} > results/dijkstra.txt")
print("running ALT with 44 handpicked landmarks")
system(f"cargo run --release --bin=performance -- --graph={graph} --landmarks={landmarks}_handpicked_44.bin --targets {targets} > results/handpicked_44.txt")
for i in range(6, 7):
num = 2**i
for best in [12, 24, ]:
for lm_type in ["greedy", "random"]:
print(f"running ALT with {num} {lm_type} landmarks")
system(f"cargo run --release --bin=performance -- --graph={graph} --landmarks={landmarks}_{lm_type}_{ num }.bin --targets {targets} --alt-best-size { best} > results/{lm_type}_{num}_{ best }.txt")