Skip to content

Commit 5125166

Browse files
authored
Add files via upload
1 parent cd66518 commit 5125166

File tree

2 files changed

+220
-97
lines changed

2 files changed

+220
-97
lines changed

README.Rmd

Lines changed: 78 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -69,25 +69,30 @@ Data has to be a 3 columns data.frame or matrix containing from, to and a cost/d
6969
###Path algorithms
7070
Path algorithms proposed by the package are :
7171

72-
- uni-directional Dijkstra algorithm,
73-
- bi-directional Dijkstra algorithm,
74-
- uni-directional A* algorithm
75-
- New bi-directional A* algorithm (Piljs & Post, 2009 : see http://repub.eur.nl/pub/16100/ei2009-10.pdf)
76-
- *one-to-one* bi-directional Dijkstra adapted to contraction hierarchies (Geisberger & al., 2008)
77-
- *many-to-many* bi-directional Dijkstra adapted to contraction hierarchies (Geisberger & al., 2008)
72+
- **1** uni-directional Dijkstra algorithm,
73+
- **2** bi-directional Dijkstra algorithm,
74+
- **3** uni-directional A* algorithm
75+
- **4** New bi-directional A* algorithm (Piljs & Post, 2009 : see http://repub.eur.nl/pub/16100/ei2009-10.pdf)
76+
- **5** *one-to-one* bi-directional Dijkstra adapted to contraction hierarchies (Geisberger & al., 2008)
77+
- **6** *many-to-many* bi-directional Dijkstra adapted to contraction hierarchies (Geisberger & al., 2008)
78+
- **7** PHAST algorithm (Hardware-accelerated shortest path trees), *one-to-all* algorithm adapted to contraction hierarchies (Delling & al., 2011)
7879

7980

80-
The choice between all the algorithms is available for *one-to-one* calculation like `get_distance_pair` and `get_path_pair` on a non-contracted graph.
81+
*1*, *2*, *3* and *4* are available for **one-to-one** calculation in `get_distance_pair` and `get_path_pair` functions on a **non-contracted** graph.
8182
In these functions, uni-directional Dijkstra algorithm is stopped when the destination node is reached.
8283
`A*` and `NBA*` are relevant if geographic coordinates of all nodes are provided. Note that coordinates should be expressed in a **projection system**.
8384
To be accurate and efficient, `A*` and `NBA*` algorithms should use an admissible heuristic function (here the Euclidean distance), i.e cost and heuristic function must be expressed in the same unit.
8485
In `cppRouting`, heuristic function `h` for a node (n) is defined such that :
8586
**h(n,d) = ED(n,d) / k**
8687
with *h* the heuristic, *ED* the Euclidean distance, *d* the destination node and a constant *k*.
87-
8888
So in the case where coordinates are expressed in meters and cost is expressed in time, *k* is the maximum speed allowed on the road. By default, constant is 1 and is designed for graphs with cost expressed in the same unit than coordinates (for example in meters).
89-
90-
If coordinates cannot be provided, bi-directional Dijkstra algorithm is the best option in terms of performance.
89+
If coordinates cannot be provided, bi-directional Dijkstra algorithm is the best option in terms of performance.
90+
91+
*5* is used for **one-to-one** calculation in `get_distance_pair` and `get_path_pair` functions on a **contracted** graph.
92+
93+
*1* is used for **one-to-many** calculation in `get_distance_matrix` function on a **non-contracted** graph.
94+
95+
*6* and *7* are available for **one-to-many** calculation in `get_distance_matrix` function on a **contracted** graph.
9196

9297
##Examples
9398
###Prepare data
@@ -314,7 +319,7 @@ Initially created for *one-to-one* queries, it has been extended to *many-to-man
314319
This technique is composed of two phases:
315320

316321
- preprocessing phase called *contraction* with `cpp_contract` function
317-
- query phase : a slightly modified version of bidirectional search for `one-to-one` query, available in `get_distance_pair` and `get_path_pair`; and a `many-to-many` algorithm using buckets available in `get_distance_matrix` function.
322+
- query phase : a slightly modified version of bidirectional search for `one-to-one` query, available in `get_distance_pair` and `get_path_pair`; PHAST algorithm and a `many-to-many` algorithm using buckets available in `get_distance_matrix` function.
318323

319324
Contraction phase consists of iteratively removing a vertex **v** from the graph and creating a shortcut for each pair **(u,w)** of **v**'s neighborhood if the shortest path from **u** to **w** contains **v**. To be efficient and avoid creating too much shortcuts, vertices have to be ordered according to several heuristics. The two heuristics used by `cppRouting` are :
320325

@@ -362,7 +367,7 @@ kable(bench, align = "c",row.names = F) %>%
362367
"nba : new bidirectional A* on the original graph"), notation="none")
363368
364369
```
365-
Here are the plots (in log-log) of query time improvement factor :
370+
Here are the plots (in log-log) of query time improvement factor of *one to one CH* algorithm compared to bidirectional Dijkstra and NBA :
366371
```{r,echo=FALSE,message=FALSE,warning=FALSE}
367372
library(ggthemes)
368373
bench<-read.csv2("benchmark_contract_pair.csv")
@@ -391,40 +396,44 @@ p1
391396
As we can see on the plot, the larger is the graph, the higher is the benefit of using contraction hierarchies. For OSM Europe, query time can be faster by a factor of 1000 compared to bidirectional Dijkstra and 600 to NBA.
392397

393398
#####Distance matrix
394-
Here are the measurements of contraction time and query time (in second) of contraction hierarchies on different graphs.
395-
Matrix are square (i.e the sets of source and target nodes are of equal length)
399+
Here are the measurements of query time (in second) of contraction hierarchies on different graphs.
400+
We compare *PHAST* and *many to many CH* to Dijkstra algorithm on square matrix (i.e the sets of source and target nodes are of equal length).
401+
396402
```{r,echo=FALSE,message=FALSE,warning=FALSE}
397403
bench<-read.csv2("benchmark_contract_matrix.csv",stringsAsFactors = F)
398-
bench<-bench[bench$algorithm %in% c("ch","normal"),]
404+
bench<-bench[bench$algorithm %in% c("mch","phast","normal"),]
399405
colnames(bench)<-gsub("\\."," ",colnames(bench))
400406
colnames(bench)<-gsub("X","",colnames(bench))
401407
bench$algorithm[bench$algorithm=="normal"]<-"Dijkstra"
402408
403409
kable(bench, align = "c",row.names = F) %>%
404410
kable_styling(full_width = F) %>%
405411
column_spec(1, bold = T) %>%
406-
row_spec(c(1,3,5), bold = TRUE) %>%
412+
row_spec(c(1,4,7), bold = TRUE) %>%
407413
collapse_rows(columns = 1:2, valign = "middle") %>%
408414
add_header_above(c(" " = 2, "|S|=|T|" = 4))%>%
409-
add_footnote(c("ch : many-to-many bidirectional search on the contracted graph",
415+
add_footnote(c("mch : many-to-many bidirectional search on the contracted graph",
416+
"phast : phast algorithm on the contracted graph",
410417
"Dijkstra : Dijkstra search on the original graph",
411418
"|S| : number of source nodes",
412419
"|T| : number of target nodes"), notation="none")
413420
414421
```
415-
Here are the plots (in log-log) of query time improvement factor for matrix :
422+
Here are the plots (in log-log) of query time improvement factor of *PHAST* and *many to many CH* compared to Dijkstra algorithm :
423+
416424
```{r,echo=FALSE,message=FALSE,warning=FALSE}
417425
library(ggthemes)
418426
bench<-read.csv2("benchmark_contract_matrix.csv")
419-
bench<-bench[bench$algorithm %in% c("ratio"),c(1,3:ncol(bench))]
427+
bench<-bench[bench$algorithm %in% c("ratio mch","ratio phast"),]
420428
421-
test2<-melt(bench,id.vars = c("Graph"))
429+
test2<-melt(bench,id.vars = c("Graph","algorithm"))
422430
test2$variable<-as.character(test2$variable)
423431
test2$variable<-as.numeric(gsub("X","",test2$variable))
432+
test2$algorithm<-gsub("ratio ","",test2$algorithm)
424433
425434
options(scipen=999)
426435
p2<-ggplot(data=test2,
427-
aes(x=variable,y=value,colour=Graph))+
436+
aes(x=variable,y=value,colour=Graph,linetype=algorithm))+
428437
geom_line(size=1)+
429438
geom_rangeframe(colour="black")+
430439
theme_tufte()+
@@ -438,7 +447,45 @@ p2=p2+scale_y_log10(labels=function(x) format(x, big.mark = ",", scientific = FA
438447
p2
439448
```
440449

441-
Benefits are less important than *one-to-one* queries but still interesting. For OSM Europe, query time can be faster by a factor of 90.
450+
Benefits are less important than *one-to-one* queries but still interesting. For OSM Europe, query time can be faster by a factor of 90.
451+
PHAST's improvement is constant since it iteratively perform an *one-to-all* search, just like original Dijkstra.
452+
*many to many CH* is well adapted for **square matrix**.
453+
454+
Here are the plots of query time of *PHAST* and *many to many CH* on assymetric matrix (i.e. number of source and number of target are unequal) with *|S| / |T|* the number of sources divided by the number of targets :
455+
456+
```{r,echo=FALSE,message=FALSE,warning=FALSE}
457+
test<-read.csv2("assymetry_8000.csv")
458+
test$lab<-paste0(2^test$p2/2^test$p1)
459+
test<-test[,c("lab","seconds","dijkstra","phast","graph")]
460+
test<-melt(test,id.vars=c("lab","graph"))
461+
462+
labb<-test$lab[1:13]
463+
test<-test[test$lab %in% labb[4:13],]
464+
labb<-labb[4:13]
465+
test$id<-rep(1:10,9)
466+
test$variable<-as.character(test$variable)
467+
test$variable<-ifelse(test$variable=="seconds","Many-to-many CH",test$variable)
468+
test$graph<-as.character(test$graph)
469+
test$graph<-ifelse(test$graph=="europe","OSM Europe",test$graph)
470+
test$graph<-ifelse(test$graph=="France OSM","OSM France",test$graph)
471+
test$graph<-ifelse(test$graph=="France","ROUTE 500",test$graph)
472+
473+
options(scipen=999)
474+
p<-ggplot(data=test[test$variable!="dijkstra",],aes(x=id,y=value,colour=graph,linetype=variable))+
475+
geom_line(size=1)+
476+
scale_x_continuous(labels=labb,breaks=1:10)+
477+
scale_y_log10()+
478+
theme_minimal()+
479+
theme(axis.text.x = element_text(angle = 45, hjust = 1))+
480+
labs(colour="Graph",linetype="Algorithm")+
481+
ylab("seconds")+
482+
xlab("|S| / |T|")
483+
484+
p
485+
```
486+
487+
Note that this plot is the same for *|T| / |S|*.
488+
*PHAST* algorithm is much faster for rectangular matrix. The rate *|S| / |T|* where *many to many CH* is better varies according the graph size. For example, if we have to calculate a distance matrix between 10000 sources and 10 targets (or 10 sources and 10000 targets) on OSM France, we must use *PHAST*. On the other hand, if we want a matrix of 10000 sources and 8000 targets, we use *many to many CH* algorithm.
442489

443490
###Compute isochrones
444491
Let's compute isochrones around Dijon city
@@ -475,6 +522,7 @@ p
475522
476523
```
477524

525+
478526
#Applications
479527

480528
**Except application 4, all indicators are calculated at the country scale but for the limited `R`'s ability to plot large shapefile, only one region is mapped.**
@@ -538,11 +586,11 @@ The shortest travel time is computed with the `cppRouting` function `get_distanc
538586
In order to compute multiple distances from one source, original uni-directional Dijkstra algorithm is ran without early stopping.
539587
We compute travel time from all commune nodes to all maternity ward nodes (i.e ~36000*400 distances).
540588
```{r,echo=TRUE,message=FALSE,warning=FALSE,include=TRUE}
541-
#Distance matrix (few seconds to compute)
542-
dists<-get_distance_matrix(graph,
589+
#Distance matrix on contracted graph
590+
dists<-get_distance_matrix(graph3,
543591
from=ndcom$id_noeud,
544592
to=ndcom$id_noeud[ndcom$com %in% maternity$CODGEO],
545-
allcores=TRUE)
593+
algorithm = "phast")#because of the rectangular shape of the matrix
546594
#We extract each minimum travel time for all the communes
547595
dists2<-data.frame(node=ndcom$id_noeud,mindist=apply(dists,1,min,na.rm=T))
548596
#Joining commune IDs to nodes
@@ -675,7 +723,8 @@ roads2$to_id<-as.character(roads2$to_id)
675723
microbenchmark(igraph=test_igraph<-distances(graph_igraph,origin,to=destination,weights = E(graph_igraph)$weight,mode="out"),
676724
dodgr=test_dodgr<-dodgr_dists(graph=data.frame(roads2),from=origin,to=destination,parallel=FALSE),
677725
cpprouting=test_cpp<-get_distance_matrix(graph,origin,destination,allcores = FALSE),
678-
contracted=test_cpp_cont<-get_distance_matrix(graph3,origin,destination,allcores = FALSE),
726+
contr_mch=test_mch<-get_distance_matrix(graph3,origin,destination,algorithm = "mch",allcores = FALSE),
727+
contr_phast=test_phast<-get_distance_matrix(graph3,origin,destination,algorithm = "phast",allcores = FALSE),
679728
times=1)
680729
681730
```
@@ -684,14 +733,15 @@ Even if we add the preprocessing time (i.e. 20s) to the query time, the whole pr
684733

685734
**Ouput**
686735
```{r,echo=TRUE}
687-
head(cbind(test_igraph[,1],test_dodgr[,1],test_cpp[,1],test_cpp_cont[,1]))
736+
head(cbind(test_igraph[,1],test_dodgr[,1],test_cpp[,1],test_mch[,1],test_phast[,1]))
688737
```
689738

690739
**Distance matrix : parallel**
691740
```{r,echo=TRUE,warning=FALSE}
692741
microbenchmark(dodgr=test_dodgr<-dodgr_dists(graph=data.frame(roads2),from=origin,to=destination,parallel=TRUE),
693742
cpprouting=test_cpp<-get_distance_matrix(graph,origin,destination,allcores = TRUE),
694-
contracted=test_cpp_cont<-get_distance_matrix(graph3,origin,destination,allcores = TRUE),
743+
contr_mch=test_mch<-get_distance_matrix(graph3,origin,destination,algorithm = "mch",allcores = TRUE),
744+
contr_phast=test_phast<-get_distance_matrix(graph3,origin,destination,algorithm = "phast",allcores = TRUE),
695745
times=1)
696746
```
697747

0 commit comments

Comments
 (0)