Specification: A path is a sequence of edges such that the target of each edge is the source of the next. Given a graph and a vertex v, list the vertices reachable by a path from v.
Example input/output:
Input:
graph
source | target |
---|---|
a | b |
b | b |
b | c |
c | f |
c | b |
a | d |
e | a |
Input:
query
source |
---|
a |
Output:
[a, b, c, d, f]
Python - Imperative
def reachable(graph, query):
adjacency_list = defaultdict(list)
for edge in graph:
adjacency_list[edge["source"]].append(edge["target"])
source = query[0]["source"]
visited = set()
to_visit = set([source])
while len(to_visit) > 0:
current = to_visit.pop()
if current in visited:
continue
for neighbor in adjacency_list[current]:
to_visit.add(neighbor)
visited.add(current)
return list(visited)
Python - Functional
def reachable(graph, query):
def step(visited):
frontier = set([
edge["target"]
for vertex in visited
for edge in graph
if vertex == edge["source"]
])
return frontier.union(visited)
def fix(f, x):
next = f(x)
return x if next == x else fix(f, next)
source = query[0]["source"]
return list(fix(step, set([source])))
SQL - SQLite
WITH RECURSIVE
closure(source, target) AS (
SELECT source, source FROM graph
UNION
SELECT source, target FROM graph
UNION
SELECT edge.source, path.target
FROM closure as path JOIN graph as edge
ON edge.target = path.source
)
SELECT S.target FROM closure as S
JOIN query ON S.source = query.source
Datalog - Souffle
.decl path(x:symbol,y:symbol)
path(x,y):-graph(x,y).
path(x,y):-graph(x,z),path(z,y).
reachable(source):-query(source).
reachable(x):-query(source),path(source,x).
Q - kdb+
graph: (first') each graph;
nodes: asc distinct graph[`source], graph[`target];
adj_list: `source xgroup graph;
adj_matrix:
{in[;x] each nodes} each nodes ,' (adj_list each nodes) `target;
iterated_matrix: last ({x | any each (x *\: x)} \) adj_matrix;
query_idx: nodes ? (first first query[`source]);
reachable: nodes where iterated_matrix[query_idx]