Transcript
Page 1: Loom and Graphs in Clojure

Loom and Graphs in Clojure

github.com/aysylu/loom

Aysylu Greenberg @aysylu22; http://aysy.lu

LispNYC, August 13th 2013

Page 2: Loom and Graphs in Clojure

Overview

•  Loom's Graph API

•  Graph Algorithms in Loom

•  Titanium Loom

•  Single Static Assignment (SSA) Loom

Page 3: Loom and Graphs in Clojure

Overview

•  Loom's Graph API

•  Graph Algorithms in Loom

•  Titanium Loom

•  SSA Loom

Page 4: Loom and Graphs in Clojure

Loom's Graph API

•  Graph, Digraph, Weighted Graph

Page 5: Loom and Graphs in Clojure

Loom's Graph API

•  Graph, Digraph, Weighted Graph •  FlyGraph

Page 6: Loom and Graphs in Clojure

Loom's Graph API

•  Graph, Digraph, Weighted Graph •  FlyGraph o  read-only, ad-hoc

Page 7: Loom and Graphs in Clojure

Loom's Graph API

•  Graph, Digraph, Weighted Graph •  FlyGraph o  read-only, ad-hoc o  edges from nodes + successors

Page 8: Loom and Graphs in Clojure

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

Page 9: Loom and Graphs in Clojure

Loom's Graph API

•  Uses Clojure protocols (clojure.org/protocols)

Page 10: Loom and Graphs in Clojure

Loom's Graph API

•  Uses Clojure protocols (clojure.org/protocols) o  specification only, no implementation

Page 11: Loom and Graphs in Clojure

Loom's Graph API

•  Uses Clojure protocols (clojure.org/protocols) o  specification only, no implementation o  single type can implement multiple

protocols

Page 12: Loom and Graphs in Clojure

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

Page 13: Loom and Graphs in Clojure

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")

Page 14: Loom and Graphs in Clojure

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")

Page 15: Loom and Graphs in Clojure

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")

Page 16: Loom and Graphs in Clojure

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")

Page 17: Loom and Graphs in Clojure

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"))

Page 18: Loom and Graphs in Clojure

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")

Page 19: Loom and Graphs in Clojure

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"))

Page 20: Loom and Graphs in Clojure

Loom's Graph API (defprotocol WeightedGraph (weight [g] [g n1 n2] "Return weight of edge [n1 n2]

or (partial weight g)"))

Page 21: Loom and Graphs in Clojure

Overview

•  Loom's Graph API

•  Graph Algorithms in Loom

•  Titanium Loom

•  SSA Loom

Page 22: Loom and Graphs in Clojure

Graph Algorithms in Loom •  DFS/BFS (+ bidirectional)

Page 23: Loom and Graphs in Clojure

Graph Algorithms in Loom •  DFS/BFS (+ bidirectional) •  Topological Sort

Page 24: Loom and Graphs in Clojure

Graph Algorithms in Loom •  DFS/BFS (+ bidirectional) •  Topological Sort •  Single Source Shortest Path (Dijkstra, Bellman-Ford)

Page 25: Loom and Graphs in Clojure

Graph Algorithms in Loom •  DFS/BFS (+ bidirectional) •  Topological Sort •  Single Source Shortest Path (Dijkstra, Bellman-Ford) •  Strongly Connected Components (Kosaraju)

Page 26: Loom and Graphs in Clojure

Graph Algorithms in Loom •  DFS/BFS (+ bidirectional) •  Topological Sort •  Single Source Shortest Path (Dijkstra, Bellman-Ford) •  Strongly Connected Components (Kosaraju) •  Density (edges/nodes)

Page 27: Loom and Graphs in Clojure

Graph Algorithms in Loom •  DFS/BFS (+ bidirectional) •  Topological Sort •  Single Source Shortest Path (Dijkstra, Bellman-Ford) •  Strongly Connected Components (Kosaraju) •  Density (edges/nodes) •  Loner nodes

Page 28: Loom and Graphs in Clojure

Graph Algorithms in Loom •  DFS/BFS (+ bidirectional) •  Topological Sort •  Single Source Shortest Path (Dijkstra, Bellman-Ford) •  Strongly Connected Components (Kosaraju) •  Density (edges/nodes) •  Loner nodes •  2 coloring

Page 29: Loom and Graphs in Clojure

Graph Algorithms in Loom •  DFS/BFS (+ bidirectional) •  Topological Sort •  Single Source Shortest Path (Dijkstra, Bellman-Ford) •  Strongly Connected Components (Kosaraju) •  Density (edges/nodes) •  Loner nodes •  2 coloring •  Max-Flow (Edmonds-Karp)

Page 30: Loom and Graphs in Clojure

Graph Algorithms in Loom •  DFS/BFS (+ bidirectional) •  Topological Sort •  Single Source Shortest Path (Dijkstra, Bellman-Ford) •  Strongly Connected Components (Kosaraju) •  Density (edges/nodes) •  Loner nodes •  2 coloring •  Max-Flow (Edmonds-Karp) •  alg-generic requires only successors + start (where

appropriate)

Page 31: Loom and Graphs in Clojure

Graph Algorithms: Bellman-Ford

A B C

D E

3 4

5

2

-8

Page 32: Loom and Graphs in Clojure

Graph Algorithms: Bellman-Ford

CLRS Introduction to Algorithms

Page 33: Loom and Graphs in Clojure

Graph Algorithms: Bellman-Ford

CLRS Introduction to Algorithms

Page 34: Loom and Graphs in Clojure

Graph Algorithms: Bellman-Ford (defn- init-estimates "Initializes path cost estimates and paths from source to all vertices, for

Bellman-Ford algorithm” [graph start] (let [nodes (disj (nodes graph) start)

path-costs {start 0} paths {start nil} infinities (repeat Double/POSITIVE_INFINITY) nils (repeat nil) init-costs (interleave nodes infinities) init-paths (interleave nodes nils)]

[(apply assoc path-costs init-costs) (apply assoc paths init-paths)]))

Page 35: Loom and Graphs in Clojure

Graph Algorithms: Bellman-Ford

Page 36: Loom and Graphs in Clojure

Graph Algorithms: Bellman-Ford

Page 37: Loom and Graphs in Clojure

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)))

Page 38: Loom and Graphs in Clojure

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)))

Page 39: Loom and Graphs in Clojure

Graph Algorithms: Bellman-Ford

Page 40: Loom and Graphs in Clojure

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)))

Page 41: Loom and Graphs in Clojure

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)))) {}))])))

Page 42: Loom and Graphs in Clojure

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."

Page 43: Loom and Graphs in Clojure

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)]

Page 44: Loom and Graphs in Clojure

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)]

Page 45: Loom and Graphs in Clojure

Graph Algorithms: Bellman-Ford (if (some (fn [[u v :as edge]] (can-relax-edge? edge (wt g u v) costs))

edges) false

Page 46: Loom and Graphs in Clojure

Graph Algorithms: Bellman-Ford [costs

(->> (keys paths) ;remove vertices that are unreachable from source (remove

#(= Double/POSITIVE_INFINITY (get costs %)))

Page 47: Loom and Graphs in Clojure

Graph Algorithms: Bellman-Ford [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)))) {}))])))

Page 48: Loom and Graphs in Clojure

Overview

•  Loom's Graph API

•  Graph Algorithms

•  Titanium Loom

•  SSA Loom

Page 49: Loom and Graphs in Clojure

Titanium Loom

•  Titanium by Clojurewerkz (titanium.clojurewerkz.org)

Page 50: Loom and Graphs in Clojure

Titanium Loom

•  Titanium by Clojurewerkz (titanium.clojurewerkz.org)

•  Clojure graph library built on top of Aurelius Titan (thinkaurelius.github.com/titan)

Page 51: Loom and Graphs in Clojure

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

Page 52: Loom and Graphs in Clojure

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

Page 53: Loom and Graphs in Clojure

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"})

Page 54: Loom and Graphs in Clojure

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)])

Page 55: Loom and Graphs in Clojure

Titanium Loom (view graph)

Page 56: Loom and Graphs in Clojure

Titanium Loom (defn titanium->loom "Converts titanium graph into Loom representation” ([titanium-graph & {:keys [node-fn edge-fn weight-fn]

:or {node-fn (nodes/get-all-vertices) edge-fn (map (juxt edges/tail-vertex edges/head-vertex) (edges/get-all-edges)) weight-fn (constantly 1)}}]

(let [nodes-set (set node-fn) edges-set (set edge-fn)]

Page 57: Loom and Graphs in Clojure

Titanium Loom (reify Graph (nodes [_] nodes-set) (edges [_] edges-set)

(has-node? [g node] (contains? (nodes g) node)) (has-edge? [g n1 n2] (contains? (edges g) [n1 n2])) (successors [g] (partial successors g)) (successors [g node] (filter (nodes g)

(seq (nodes/connected-out-vertices node)))) (out-degree [g node] (count (successors g node)))

Page 58: Loom and Graphs in Clojure

Titanium Loom (reify Graph (nodes [_] nodes-set) (edges [_] edges-set)

(has-node? [g node] (contains? (nodes g) node)) (has-edge? [g n1 n2] (contains? (edges g) [n1 n2])) (successors [g] (partial successors g)) (successors [g node] (filter (nodes g)

(seq (nodes/connected-out-vertices node)))) (out-degree [g node] (count (successors g node))) Digraph (predecessors [g] (partial predecessors g)) (predecessors [g node] (filter (nodes g) (seq (nodes/connected-in-vertices node)))) (in-degree [g node] (count (predecessors g node))) WeightedGraph (weight [g] (partial weight g)) (weight [g n1 n2] (weight-fn n1 n2))))))

Page 59: Loom and Graphs in Clojure

Overview

•  Loom's Graph API

•  Graph Algorithms

•  Titanium Loom

•  SSA Loom

Page 60: Loom and Graphs in Clojure

SSA Loom

•  Single Static Assignment (SSA) form produced by core.async

Page 61: Loom and Graphs in Clojure

SSA Loom

•  Single Static Assignment (SSA) form produced by core.async

•  Generated by parse-to-state-machine function

Page 62: Loom and Graphs in Clojure

SSA Loom (parse-to-state-machine '[(if (> (+ x 1 2 y) 0) (+ x 1) (+ x 2))])

Page 63: Loom and Graphs in Clojure

SSA Loom (parse-to-state-machine '[(if (> (+ x 1 2 y) 0) (+ x 1) (+ x 2))])

[inst_4938 {:current-block 76, :start-block 73, :block-catches {76 nil, 75 nil, 74 nil, 73 nil}, :blocks {76 [{:value :clojure.core.async.impl.ioc-macros/value, :id

inst_4937} {:value inst_4937, :id inst_4938}], 75 [{:refs [clojure.core/+ x 2], :id inst_4935} {:value inst_4935, :block 76, :id inst_4936}], 74 [{:refs [clojure.core/+ x 1], :id inst_4933} {:value inst_4933, :block 76, :id inst_4934}], 73 [{:refs [clojure.core/+ x 1 2 y], :id inst_4930} {:refs [clojure.core/> inst_4930 0], :id inst_4931} {:test inst_4931, :then-block 74, :else-block 75, :id

inst_4932}]}}]

Page 64: Loom and Graphs in Clojure

SSA Loom (def ssa (->> (parse-to-state-machine

'[(if (> (+ x 1 2 y) 0) (+ x 1) (+ x 2))]) second :blocks))

Page 65: Loom and Graphs in Clojure

SSA Loom {76 [{:value :clojure.core.async.impl.ioc-macros/

value, :id inst_4937} {:value inst_4937, :id inst_4938}], 75 [{:refs [clojure.core/+ x 2], :id inst_4935} {:value inst_4935, :block 76, :id inst_4936}], 74 [{:refs [clojure.core/+ x 1], :id inst_4933} {:value inst_4933, :block 76, :id inst_4934}], 73 [{:refs [clojure.core/+ x 1 2 y], :id inst_4930} {:refs [clojure.core/> inst_4930 0], :id

inst_4931} {:test inst_4931, :then-block 74, :else-block

75, :id inst_4932}]}}]

(def ssa (->> (parse-to-state-machine

'[(if (> (+ x 1 2 y) 0) (+ x 1) (+ x 2))]) second :blocks))

Page 66: Loom and Graphs in Clojure

SSA Loom (view (ssa->loom ssa ssa-nodes-fn ssa-edges-fn))

Page 67: Loom and Graphs in Clojure

SSA Loom (view (ssa->loom ssa ssa-nodes-fn ssa-edges-fn))

Page 68: Loom and Graphs in Clojure

SSA Loom (view (ssa->loom ssa ssa-nodes-fn ssa-edges-fn)) (defn ssa->loom "Converts the SSA form generated by core.async into Loom

representation.” ([ssa node-fn edge-fn] (let [nodes (delay (node-fn ssa))

edges (delay (edge-fn ssa))]

Page 69: Loom and Graphs in Clojure

SSA Loom (view (ssa->loom ssa ssa-nodes-fn ssa-edges-fn)) {:graph (reify Graph

(nodes [g] @nodes) (edges [g] @edges) (has-node? [g node] (contains? @nodes node)) (has-edge? [g n1 n2] (contains? @edges [n1 n2])) (successors [g] (partial successors g)) (successors [g node]

(->> @edges (filter (fn [[n1 n2]] (= n1 node))) (map second))) (out-degree [g node] (count (successors g node)))

Page 70: Loom and Graphs in Clojure

SSA Loom (view (ssa->loom ssa ssa-nodes-fn ssa-edges-fn)) Digraph

(predecessors [g] (partial predecessors g)) (predecessors [g node]

(->> @edges (filter (fn [[n1 n2]] (= n2 node))) (map first))) (in-degree [g node] (count (predecessors g node))))

:data ssa})))

Page 71: Loom and Graphs in Clojure

SSA Loom: Dataflow Analysis

•  For each basic block, solve system of equations until reaching fixed point:

•  Use worklist approach

Page 72: Loom and Graphs in Clojure

SSA Loom: Dataflow Analysis (defn dataflow-analysis "Performs dataflow analysis. Nodes have value nil initially.” [& {:keys [start graph join transfer]}] (let [start (cond

(set? start) start (coll? start) (set start) :else #{start})]

Page 73: Loom and Graphs in Clojure

SSA Loom: Dataflow Analysis (loop [out-values {}

[node & worklist] (into clojure.lang.PersistentQueue/EMPTY start) (let [in-value (join (mapv out-values (predecessors graph node)))

out (transfer node in-value) update? (not= out (get out-values node)) out-values (if update? (assoc out-values node out) out-values) worklist (if update? (into worklist (successors graph node)) worklist)]

Page 74: Loom and Graphs in Clojure

SSA Loom: Dataflow Analysis (loop [out-values {}

[node & worklist] (into clojure.lang.PersistentQueue/EMPTY start (let [in-value (join (mapv out-values (predecessors graph node)))

out (transfer node in-value) update? (not= out (get out-values node)) out-values (if update? (assoc out-values node out) out-values) worklist (if update? (into worklist (successors graph node)) worklist)]

(if (seq worklist) (recur out-values worklist) out-values)))))

Page 75: Loom and Graphs in Clojure

SSA Loom: Global Availability (defn global-cse [ssa] (let [{graph :graph node-data :data} (ssa->loom (:blocks ssa) ssa-nodes-fn ssa-edges-fn) start (:start-block ssa)] (letfn [(pure? [instr] (contains? instr :refs)) (global-cse-join [values] (if (seq values) (apply set/intersection values) #{})) (global-cse-transfer [node in-value] (into in-value (map :refs (filter pure? (node-data node)))))]

Page 76: Loom and Graphs in Clojure

SSA Loom: Global Availability (defn global-cse [ssa] (let [{graph :graph node-data :data} (ssa->loom (:blocks ssa) ssa-nodes-fn ssa-edges-fn) start (:start-block ssa)] (letfn [(pure? [instr] (contains? instr :refs)) (global-cse-join [values] (if (seq values) (apply set/intersection values) #{})) (global-cse-transfer [node in-value] (into in-value (map :refs (filter pure? (node-data node)))))] (dataflow-analysis :start start :graph graph :join global-cse-join :transfer global-cse-transfer))))

Page 77: Loom and Graphs in Clojure

SSA Loom: Dataflow Analysis

• Reaching definitions

Page 78: Loom and Graphs in Clojure

SSA Loom: Dataflow Analysis

• Reaching definitions • Liveness analysis (dead code elimination)

Page 79: Loom and Graphs in Clojure

SSA Loom: Dataflow Analysis

• Reaching definitions • Liveness analysis (dead code elimination) • Available expressions

Page 80: Loom and Graphs in Clojure

SSA Loom: Dataflow Analysis

• Reaching definitions • Liveness analysis (dead code elimination) • Available expressions • Constant propagation

Page 81: Loom and Graphs in Clojure

SSA Loom: Dataflow Analysis

• Reaching definitions • Liveness analysis (dead code elimination) • Available expressions • Constant propagation • Other Applications:

Page 82: Loom and Graphs in Clojure

SSA Loom: Dataflow Analysis

• Reaching definitions • Liveness analysis (dead code elimination) • Available expressions • Constant propagation • Other Applications:

o Erdős number

Page 83: Loom and Graphs in Clojure

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)

Page 84: Loom and Graphs in Clojure

My Experience

•  Intuitive way to implement algorithms functionally

• Some mental overhead of transforming data structures

Page 85: Loom and Graphs in Clojure

Open Questions

• How general should a graph API be?

Page 86: Loom and Graphs in Clojure

Open Questions

• How general should a graph API be? • How feature-rich should a graph API be?


Top Related