- Lokesh Koshale
h(x) : heuristic value
Each Node has :
( euclidean distance )
10
8
0
6
2
9
7
3
2
2
B
D
H
E
I
G
C
A
J
1
4
Each Edge has positive weight
g(x) : Cost of reaching node x
from source
f(x) = g(x) + h(x)
Approximate cost of reaching destination from source via node x.
s = start_node
e = end_node
open_list = { s }
closed_list = { }
g(s) = 0
h(s) = heuristic_function(s,e)
f(s) = g(s) + h(s)
while open_list is not empty:
m = node in open_list with least f
if m == e:
return
remove m from open_list
add m to closed_list
for each n in child(m):
cost = g(m) + distance(m,n)
if n in open_list and cost < g(n):
remove n from open_list
if n in closed_list and cost < g(n):
remove n from closed_list
if n in open_list and n not in closed_list:
g(n) = cost
h(n) = heuristic_function(n, end)
f(n) = g(n) + h(n)
add n to open_list
return failure
10
10
8
0
6
2
9
7
3
2
2
2
8
10
Open List :
while open_list is not empty:
m = node in open_list with least f
if m == e:
return
remove m from open_list
add m to closed_list
for each n in child(m):
cost = g(m) + distance(m,n)
if n in open_list and cost < g(n):
remove n from open_list
if n in closed_list and cost < g(n):
remove n from closed_list
if n in open_list and n not in closed_list:
g(n) = cost
h(n) = heuristic_function(n, end)
f(n) = g(n) + h(n)
add n to open_list
return failure
10
8
0
6
2
9
7
3
2
2
2
8
10
Open List :
while open_list is not empty:
m = node in open_list with least f
if m == e:
return
remove m from open_list
add m to closed_list
for each n in child(m):
cost = g(m) + distance(m,n)
if n in open_list and cost < g(n):
remove n from open_list
if n in closed_list and cost < g(n):
remove n from closed_list
if n in open_list and n not in closed_list:
g(n) = cost
h(n) = heuristic_function(n, end)
f(n) = g(n) + h(n)
add n to open_list
return failure
10
8
0
6
2
9
7
3
2
2
2
8
10
9
10
17
Open List :
9
10
17
while open_list is not empty:
m = node in open_list with least f
if m == e:
return
remove m from open_list
add m to closed_list
for each n in child(m):
cost = g(m) + distance(m,n)
if n in open_list and cost < g(n):
remove n from open_list
if n in closed_list and cost < g(n):
remove n from closed_list
if n in open_list and n not in closed_list:
g(n) = cost
h(n) = heuristic_function(n, end)
f(n) = g(n) + h(n)
add n to open_list
return failure
10
8
0
6
2
9
7
3
2
2
2
8
10
9
10
17
Open List :
10
17
6
11
11
while open_list is not empty:
m = node in open_list with least f
if m == e:
return
remove m from open_list
add m to closed_list
for each n in child(m):
cost = g(m) + distance(m,n)
if n in open_list and cost < g(n):
remove n from open_list
if n in closed_list and cost < g(n):
remove n from closed_list
if n in open_list and n not in closed_list:
g(n) = cost
h(n) = heuristic_function(n, end)
f(n) = g(n) + h(n)
add n to open_list
return failure
10
8
0
6
2
9
7
3
2
2
2
8
10
9
10
17
Open List :
9
11
6
4
9
17
while open_list is not empty:
m = node in open_list with least f
if m == e:
return
remove m from open_list
add m to closed_list
for each n in child(m):
cost = g(m) + distance(m,n)
if n in open_list and cost < g(n):
remove n from open_list
if n in closed_list and cost < g(n):
remove n from closed_list
if n in open_list and n not in closed_list:
g(n) = cost
h(n) = heuristic_function(n, end)
f(n) = g(n) + h(n)
add n to open_list
return failure
10
8
0
6
2
9
7
3
2
2
2
8
10
9
10
17
Open List :
12
6
4
9
6
12
17
Lemma 1: If we have found a node d which has cost f_d then all the nodes i ∈ V which have cost f_i < f_d are already visited(expanded).
Intuition: We always pick lowest cost node from priority queue.
Proposed in paper “Massively Parallel A* Search on a GPU” by Yichao Zhou and Jianyang Zeng, 2015.
Instead of just using one single priority queue for the open list, allocate a multiple priority queues queues for A* search.
10
8
0
6
2
9
7
3
2
2
2
8
10
9
10
17
Priority Queue:
9
10
17
In next step node with cost 9 will be extracted from priority queue and its children cost will be calculated.
Bottleneck : Sequential nature of PQ.
10
8
0
6
2
9
7
3
2
2
2
8
10
9
10
17
9
10
17
PQ1
PQ2
PQ3
As number of parallel priority queue increases, execution time decreases
Graphs that are subject to an Update operation.
Insert (u, v)
Delete (u, v)
Typical Updates:
Partially dynamic problems: Graphs subject to insertions only, or deletions only, but not both.
Fully dynamic problems: Graphs subject to intermixed sequences of insertions and deletions
In the incremental setting, there are only edge-insertions, i.e. edge u → v is added in G where u ∈ V and v ∈ V.
Lemma 2: Insertion of an edge can not increase the cost of source to destination Optimal Path.
B
D
H
E
I
G
C
A
J
Inserted edges: C → G
D → G
Intitution: Optimal Path is the lowest cost path.
Lemma 3: If we add an edge u → v and
f (u) > f(destination) then addition of this edge will not affect the Optimal Path. *
*For all such graphs where h(x) is constant, i.e Euclidean distance
for each edges(u,v) inserted:
lock(v)
if f_new(v) < f_old(v):
add node v to update_list
unlock(v)
while update_list is not empty:
extract node n from update_list
for each child of n:
lock(child)
if f_new(child) < f_old(child):
add child to update_list
unlock(child)
10
8
0
6
2
9
3
2
2
2
8
10
9
10
17
6
4
9
6
12
5
2
?
Multiple Threads trying to update cost of single node, therefore we require synchronization.
for each edges(u,v) inserted:
lock(v)
if f_new(v) < f_old(v):
add node v to update_list
unlock(v)
while update_list is not empty:
extract node n from update_list
for each child of n:
lock(child)
if f_new(child) < f_old(child):
add child to update_list
unlock(child)
10
8
0
6
2
9
3
2
2
2
8
10
9
10
17
6
4
9
6
12
5
2
9
Propagate updated cost to all affected nodes
3
10
In Decremental setting there are only removal of edges u → v where u ∈ V and v ∈ V.
Lemma 4: Deletion of edge u → v where u ∈ V and v ∈ V can not decrease f (v).
Lemma 6: If edge(u,v) is deleted and
optimal_parent(v) ≠ u, then deletion of such edges doesn’t affect the Optimal Path.
B
D
H
E
I
G
C
A
J
Deleted Edges: A → C
optimal_parent(v): neighbor of node v from which its f is calculated.
for each deleted edge(u,v):
if f(v) != INFINITY and optimal_parent(v)==u:
compute f(v) from all of its parents
chose the least one as optimal_parent(v)
update f(v)
if v doesn’t have any parent then f (v) == INFINITY:
add v to update_list.
while update_list is not empty:
extract node n from update_list.
for each child of n such that optimal_parent(child)==n:
if f_old(child) > f_new(child):
compute f(child) from all parents of child node
chose the least one as optimal_parent
add child to update_list.
10
8
0
6
2
9
3
2
2
2
8
10
9
10
17
6
4
9
6
5
2
9
3
10
12
for each deleted edge(u,v):
if f(v) != INFINITY and optimal_parent(v)==u:
compute f(v) from all of its parents
chose the least one as optimal_parent(v)
update f(v)
if v doesn’t have any parent then f (v) == INFINITY:
add v to update_list.
while update_list is not empty:
extract node n from update_list.
for each child of n such that optimal_parent(child)==n:
if f_old(child) > f_new(child):
compute f(child) from all parents of child node
chose the least one as optimal_parent
add child to update_list.
10
8
0
6
2
9
3
2
2
2
8
10
9
10
17
6
4
9
6
2
3
10
12
13
12
for each deleted edge(u,v):
if f(v) != INFINITY and optimal_parent(v)==u:
compute f(v) from all of its parents
chose the least one as optimal_parent(v)
update f(v)
if v doesn’t have any parent then f (v) == INFINITY:
add v to update_list.
while update_list is not empty:
extract node n from update_list.
for each child of n such that optimal_parent(child)==n:
if f_old(child) > f_new(child):
compute f(child) from all parents of child node
chose the least one as optimal_parent
add child to update_list.
10
8
0
6
2
9
3
2
2
2
8
10
9
10
17
6
4
9
6
2
3
12
12
Infinite propagation due to a cycle in the graph
S
A
B
C
E
D
4
2
5
10
8
S
A
B
C
E
D
4
2
5
10
8
4
S
A
B
C
E
D
4
2
5
10
8
Propagation of deletion never halts, as given in the example. It’s because we chose the wrong optimal_parent(B) = E, as node B
itself is an optimal_ancesstor of E. So cost
of the node E is calculated based on the cost of node B.
Solution: each time we chose the optimal
parent for a node, we check if the cost of that
parent is not calculated from the node or not
4
S
A
B
C
E
D
4
2
5
10
8
S
A
B
C
I
G
H
E
F
D
5
6
2
6
10
2
5
10
6
2
5
2
Edges A→C and B→G are deleted.
Node C, G are added in update list.
S
A
B
C
I
G
H
E
F
D
6
2
6
10
2
5
10
6
2
2
Lemma 8: At the end of propagation for deletion all node n ∈ V such that f_new(n) ≠ ∞ has the latest f(n) which is optimal cost in new graph including deletions.
Lemma 9: At the end of propagation f(destination) might not be the optimal cost of reaching destination from source in new graph.
open_list = {B,C}
closed_list= {S,A,D}
f(E) = ∞
Deleted edge S → D.
after propagation f(D) = ∞
but optimal path exists which is:
S→A→C→E→D
Require A* from last saved state,if there exist node with cost less than destination in PQ.
S
A
B
C
E
D
2
6
5
6
8
3
Real-world graphs are dynamic and edges are changing continuously so there are insertions of edges and deletions of edges at the same time
We can infer the dynamic change in edges at a single instance as addition happening first then deletion or vice versa
while update_list not empty:
extract node n from update list.
for each child of n:
if f(child) decreases:
update f(child) and make n optimal_parent
add child to update list
if f(child) increases and n is optimal_parent(child):
choose the best optimal_parent from all parents of child
add child to update list.
Insertion:
Deletion:
Deletion is computationally costlier than insertions due to the cycle checks.
Initially static A* performs better but as the number of time the graph is subjected to updates increases Dynamic A* performs better.
Table: Compares execution time of Static A* with Dynamic A* for various temporal graph (Time in seconds)
Graph | N | E | Queries | DGA* | multi GA* | Speedup |
---|---|---|---|---|---|---|
live-journal | 3,997,962 | 34,681,189 | 10 | 6.01 | 33.93 | 5x |
wiki-talk | 1,140,149 | 7,833,140 | 10 | 12.24 | 24.84 | 2x |
ask-Ubuntu | 159,316 | 964,437 | 10 | 0.25 | 1.31 | 5x |
Youtube | 1,157,828 | 2,987,624 | 10 | 0.81 | 5.78 | 7x |
math-overflow | 24,818 | 506,550 | 10 | 0.09 | 0.067 | 7x |
superuser | 194,085 | 1,443,339 | 10 | 0.41 | 2.89 | 7x |
live-journal | 3,997,962 | 34,681,189 | 10 | 11.41 | 424.06 | 37x |
Replacing multiple static A* to dynamic A* gives 35x speedup
We achieved 10x speedup by applying the proposed algorithm
path_library = []
path = []
number_pq = set_number_of_threads()
#in Parallel
GPU_A*(source, sink, number_pq, path)
# for path of size n, delete edge(path[n-1],path[n])
remove_edge(Graph,path)
path_library.insert(path)
for i in range(1,K):
#in parallel
propagate_deletions(deleted_edges,N,E)
remove_edge(Graph,path)
path_library.insert(path)
Find K number of shortest path from source to destination.
To find K-shortest path from source to destination authors have proposed to delete the last edge of the optimal path and re-run the A* algorithm from start, K times.
Instead of re-executing A* from source, we have used propagation for deletion to find the next optimal path
Replacing K-times A* algorithm with propagation gave 10x speedup
The heuristic function also plays a key role in determining the correctness and admissibility of the algorithm.
There can be four possible scenarios with heuristics function h :
For dynamic graphs heuristics can be divided into two categories:
Deletion of Edges :
Deletion of edges doesn't affect the correctness and admissibility of the heuristics
S
A
B
C
D
E
2
6
5
6
8
3
0
1
2
1
3
2
In the example graph heuristic is BFS distance from destination node (D) and edge E->D is deleted. The heuristic value of node E and S changes to 4 and 1 respectively.
3
4
As deletion of edges cause increase in heuristic values, thus old values are an underestimate of actual cost. So it only affects the performance not the correctness of the algorithm.
Insertion of Edges:
S
A
B
C
D
E
2
6
5
6
3
0
1
2
4
3
3
11
Insertion of edges might decrease the heuristic values, thus old values are an overestimate of the actual cost and overestimate can lead to incorrect results. Thus there is need to update the heuristic values.
In the example graph edge S->D is inserted.
Without updating heuristic value of node S, A* algorithm returns the path S->A->C->D, which is not optimal.
1
So for Insertion we propagate the new heuristic value of affected nodes towards the source.
Trade-off between performance and accuracy
And special thanks to prof. Rupesh Nasre for his invaluable guidance throughout this project.