Graph algorithms are cool and fascinating. We'll look at a graph algorithms and visualization library, Loom, which is written in Clojure. We will discuss the graph API, look at implementation of the algorithms and learn about the integration of Loom with Titanium, which allows us to run the algorithms on and visualize data in graph databases.
6. Loom's Graph API
• Graph, Digraph, Weighted Graph
• FlyGraph
o read-only, ad-hoc
7. Loom's Graph API
• Graph, Digraph, Weighted Graph
• FlyGraph
o read-only, ad-hoc
o edges from nodes + successors
8. Loom's Graph API
• Graph, Digraph, Weighted Graph
• FlyGraph
o read-only, ad-hoc
o edges from nodes + successors
o nodes and edges from successors + start
10. Loom's Graph API
• Uses Clojure protocols
(clojure.org/protocols)
o specification only, no implementation
11. Loom's Graph API
• Uses Clojure protocols
(clojure.org/protocols)
o specification only, no implementation
o single type can implement multiple
protocols
12. Loom's Graph API
• Uses Clojure protocols
(clojure.org/protocols)
o specification only, no implementation
o single type can implement multiple
protocols
o interfaces: design-time choice of the type
author, protocols: can be added to a type
at runtime
13. Loom's Graph API
(defprotocol Graph
(add-nodes* [g nodes] "Add nodes to graph g. See add-nodes”
(add-edges* [g edges] "Add edges to graph g. See add-edges")
14. Loom's Graph API
(defprotocol Graph
(add-nodes* [g nodes] "Add nodes to graph g. See add-nodes”
(add-edges* [g edges] "Add edges to graph g. See add-edges”)
(remove-nodes* [g nodes] "Remove nodes from graph g. See
remove-nodes”)
(remove-edges* [g edges] "Removes edges from graph g. See
remove-edges”)
(remove-all [g] "Removes all nodes and edges from graph g")
15. Loom's Graph API
(defprotocol Graph
(add-nodes* [g nodes] "Add nodes to graph g. See add-nodes”
(add-edges* [g edges] "Add edges to graph g. See add-edges”
(remove-nodes* [g nodes] "Remove nodes from graph g. See
remove-nodes”)
(remove-edges* [g edges] "Removes edges from graph g. See
remove-edges”)
(remove-all [g] "Removes all nodes and edges from graph g”
(nodes [g] "Return a collection of the nodes in graph g”)
(edges [g] "Edges in g. May return each edge twice in an undirected
graph")
16. Loom's Graph API
(defprotocol Graph
(add-nodes* [g nodes] "Add nodes to graph g. See add-nodes”
(add-edges* [g edges] "Add edges to graph g. See add-edges”
(remove-nodes* [g nodes] "Remove nodes from graph g. See
remove-nodes”)
(remove-edges* [g edges] "Removes edges from graph g. See
remove-edges”)
(remove-all [g] "Removes all nodes and edges from graph g”
(nodes [g] "Return a collection of the nodes in graph g”)
(edges [g] "Edges in g. May return each edge twice in an undirected
graph”)
(has-node? [g node] "Return true when node is in g”)
(has-edge? [g n1 n2] "Return true when edge [n1 n2] is in g")
17. Loom's Graph API
(defprotocol Graph
(add-nodes* [g nodes] "Add nodes to graph g. See add-nodes”
(add-edges* [g edges] "Add edges to graph g. See add-edges”
(remove-nodes* [g nodes] "Remove nodes from graph g. See
remove-nodes”)
(remove-edges* [g edges] "Removes edges from graph g. See
remove-edges”)
(remove-all [g] "Removes all nodes and edges from graph g”
(nodes [g] "Return a collection of the nodes in graph g”)
(edges [g] "Edges in g. May return each edge twice in an undirected
graph”)
(has-node? [g node] "Return true when node is in g”)
(has-edge? [g n1 n2] "Return true when edge [n1 n2] is in g”)
(successors [g] [g node] "Return direct successors of node, or
(partial successors g)”)
(out-degree [g node] "Return the number of direct successors of
node"))
18. Loom's Graph API
(defprotocol Digraph
(predecessors [g] [g node] "Return direct
predecessors of node, or (partial predecessors g)”)
(in-degree [g node] "Return the number direct
predecessors to node")
19. Loom's Graph API
(defprotocol Digraph
(predecessors [g] [g node] "Return direct
predecessors of node, or (partial predecessors g)”)
(in-degree [g node] "Return the number direct
predecessors to node”)
(transpose [g] "Return a graph with all edges
reversed"))
20. Loom's Graph API
(defprotocol WeightedGraph
(weight [g] [g n1 n2] "Return weight of edge [n1 n2]
or (partial weight g)"))
37. Graph Algorithms: Bellman-Ford
(defn- can-relax-edge?
"Test for whether we can improve the shortest path to v found so far by
going through u.”
[[u v :as edge] weight costs]
(let [vd (get costs v)
ud (get costs u)
sum (+ ud weight)]
(> vd sum)))
38. Graph Algorithms: Bellman-Ford
(defn- relax-edge
"If there's a shorter path from s to v via u, update our map of
estimated path costs and map of paths from source to vertex v”
[[u v :as edge] weight [costs paths :as estimates]]
(let [ud (get costs u)
sum (+ ud weight)]
(if (can-relax-edge? edge weight costs)
[(assoc costs v sum) (assoc paths v u)]
estimates)))
40. Graph Algorithms: Bellman-Ford
(defn- relax-edges
"Performs edge relaxation on all edges in weighted directed graph”
[g start estimates]
(->> (edges g)
(reduce (fn [estimates [u v :as edge]]
(relax-edge edge (wt g u v) estimates))
estimates)))
41. Graph Algorithms: Bellman-Ford
(defn bellman-ford
"Given a weighted, directed graph G = (V, E) with source start, the Bellman-Ford algorithm produces map of single source shortest paths and their
costs if no negative-weight cycle that is reachable from the source exits, and false otherwise, indicating that no solution exists."
[g start]
(let [initial-estimates (init-estimates g start)
;relax-edges is calculated for all edges V-1 times
[costs paths] (reduce (fn [estimates _]
(relax-edges g start estimates))
initial-estimates
(-> g nodes count dec range))
edges (edges g)]
(if (some
(fn [[u v :as edge]]
(can-relax-edge? edge (wt g u v) costs))
edges)
false
[costs
(->> (keys paths)
;remove vertices that are unreachable from source
(remove #(= Double/POSITIVE_INFINITY (get costs %)))
(reduce
(fn [final-paths v]
(assoc final-paths v
; follows the parent pointers
; to construct path from source to node v
(loop [node v
path ()]
(if node
(recur (get paths node) (cons node path))
path))))
{}))])))
42. Graph Algorithms: Bellman-Ford
(defn bellman-ford
"Given a weighted, directed graph G = (V, E) with source start,
the Bellman-Ford algorithm produces map of single source
shortest paths and their costs if no negative-weight cycle that is
reachable from the source exits, and false otherwise, indicating
that no solution exists."
43. Graph Algorithms: Bellman-Ford
[g start]
(let [initial-estimates (init-estimates g start)
;relax-edges is calculated for all edges V-1 times
[costs paths] (reduce (fn [estimates _]
(relax-edges g start estimates))
initial-estimates
(->> g (nodes) (count) (dec) (range)))
edges (edges g)]
44. Graph Algorithms: Bellman-Ford
[g start]
(let [initial-estimates (init-estimates g start)
;relax-edges is calculated for all edges V-1 times
[costs paths] (reduce (fn [estimates _]
(relax-edges g start estimates))
initial-estimates
(->> g (nodes) (count) (dec) (range)))
edges (edges g)]
50. Titanium Loom
• Titanium by Clojurewerkz
(titanium.clojurewerkz.org)
• Clojure graph library built on top of Aurelius
Titan (thinkaurelius.github.com/titan)
51. Titanium Loom
• Titanium by Clojurewerkz
(titanium.clojurewerkz.org)
• Clojure graph library built on top of Aurelius
Titan (thinkaurelius.github.com/titan)
• Various storage backends: Cassandra,
HBase, BerkeleyDB Java Edition
52. Titanium Loom
• Titanium by Clojurewerkz
(titanium.clojurewerkz.org)
• Clojure graph library built on top of Aurelius
Titan (thinkaurelius.github.com/titan)
• Various storage backends: Cassandra,
HBase, BerkeleyDB Java Edition
• No graph visualization
53. Titanium Loom
(let [in-mem-graph (tg/open {"storage.backend" "inmemory"})]
(tg/transact!
(let [
a (nodes/create! {:name "Node A"})
b (nodes/create! {:name "Node B"})
c (nodes/create! {:name "Node C"})
54. Titanium Loom
(let [in-mem-graph (tg/open {"storage.backend" "inmemory"})]
(tg/transact!
(let [
a (nodes/create! {:name "Node A"})
b (nodes/create! {:name "Node B"})
c (nodes/create! {:name "Node C"})
e1 (edges/connect! a "edge A->B" b)
e2 (edges/connect! b "edge B->C" c)
e3 (edges/connect! c "edge C->A” a)
graph (titanium->loom in-mem-graph)])
82. SSA Loom: Dataflow Analysis
• Reaching definitions
• Liveness analysis (dead code elimination)
• Available expressions
• Constant propagation
• Other Applications:
o Erdős number
83. SSA Loom: Dataflow Analysis
• Reaching definitions
• Liveness analysis (dead code elimination)
• Available expressions
• Constant propagation
• Other Applications:
o Erdős number
o Spread of information in systems (e.g. taint)
84. My Experience
• Intuitive way to implement algorithms
functionally
• Some mental overhead of transforming data
structures