Mercurial > lasercutter
diff src/clojure/contrib/graph.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/clojure/contrib/graph.clj Sat Aug 21 06:25:44 2010 -0400 1.3 @@ -0,0 +1,228 @@ 1.4 +;; Copyright (c) Jeffrey Straszheim. All rights reserved. The use and 1.5 +;; distribution terms for this software are covered by the Eclipse Public 1.6 +;; License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can 1.7 +;; be found in the file epl-v10.html at the root of this distribution. By 1.8 +;; using this software in any fashion, you are agreeing to be bound by the 1.9 +;; terms of this license. You must not remove this notice, or any other, 1.10 +;; from this software. 1.11 +;; 1.12 +;; graph 1.13 +;; 1.14 +;; Basic Graph Theory Algorithms 1.15 +;; 1.16 +;; straszheimjeffrey (gmail) 1.17 +;; Created 23 June 2009 1.18 + 1.19 + 1.20 +(ns 1.21 + ^{:author "Jeffrey Straszheim", 1.22 + :doc "Basic graph theory algorithms"} 1.23 + clojure.contrib.graph 1.24 + (use [clojure.set :only (union)])) 1.25 + 1.26 + 1.27 +(defstruct directed-graph 1.28 + :nodes ; The nodes of the graph, a collection 1.29 + :neighbors) ; A function that, given a node returns a collection 1.30 + ; neighbor nodes. 1.31 + 1.32 +(defn get-neighbors 1.33 + "Get the neighbors of a node." 1.34 + [g n] 1.35 + ((:neighbors g) n)) 1.36 + 1.37 + 1.38 +;; Graph Modification 1.39 + 1.40 +(defn reverse-graph 1.41 + "Given a directed graph, return another directed graph with the 1.42 + order of the edges reversed." 1.43 + [g] 1.44 + (let [op (fn [rna idx] 1.45 + (let [ns (get-neighbors g idx) 1.46 + am (fn [m val] 1.47 + (assoc m val (conj (get m val #{}) idx)))] 1.48 + (reduce am rna ns))) 1.49 + rn (reduce op {} (:nodes g))] 1.50 + (struct directed-graph (:nodes g) rn))) 1.51 + 1.52 +(defn add-loops 1.53 + "For each node n, add the edge n->n if not already present." 1.54 + [g] 1.55 + (struct directed-graph 1.56 + (:nodes g) 1.57 + (into {} (map (fn [n] 1.58 + [n (conj (set (get-neighbors g n)) n)]) (:nodes g))))) 1.59 + 1.60 +(defn remove-loops 1.61 + "For each node n, remove any edges n->n." 1.62 + [g] 1.63 + (struct directed-graph 1.64 + (:nodes g) 1.65 + (into {} (map (fn [n] 1.66 + [n (disj (set (get-neighbors g n)) n)]) (:nodes g))))) 1.67 + 1.68 + 1.69 +;; Graph Walk 1.70 + 1.71 +(defn lazy-walk 1.72 + "Return a lazy sequence of the nodes of a graph starting a node n. Optionally, 1.73 + provide a set of visited notes (v) and a collection of nodes to 1.74 + visit (ns)." 1.75 + ([g n] 1.76 + (lazy-walk g [n] #{})) 1.77 + ([g ns v] 1.78 + (lazy-seq (let [s (seq (drop-while v ns)) 1.79 + n (first s) 1.80 + ns (rest s)] 1.81 + (when s 1.82 + (cons n (lazy-walk g (concat (get-neighbors g n) ns) (conj v n)))))))) 1.83 + 1.84 +(defn transitive-closure 1.85 + "Returns the transitive closure of a graph. The neighbors are lazily computed. 1.86 + 1.87 + Note: some version of this algorithm return all edges a->a 1.88 + regardless of whether such loops exist in the original graph. This 1.89 + version does not. Loops will be included only if produced by 1.90 + cycles in the graph. If you have code that depends on such 1.91 + behavior, call (-> g transitive-closure add-loops)" 1.92 + [g] 1.93 + (let [nns (fn [n] 1.94 + [n (delay (lazy-walk g (get-neighbors g n) #{}))]) 1.95 + nbs (into {} (map nns (:nodes g)))] 1.96 + (struct directed-graph 1.97 + (:nodes g) 1.98 + (fn [n] (force (nbs n)))))) 1.99 + 1.100 + 1.101 +;; Strongly Connected Components 1.102 + 1.103 +(defn- post-ordered-visit 1.104 + "Starting at node n, perform a post-ordered walk." 1.105 + [g n [visited acc :as state]] 1.106 + (if (visited n) 1.107 + state 1.108 + (let [[v2 acc2] (reduce (fn [st nd] (post-ordered-visit g nd st)) 1.109 + [(conj visited n) acc] 1.110 + (get-neighbors g n))] 1.111 + [v2 (conj acc2 n)]))) 1.112 + 1.113 +(defn post-ordered-nodes 1.114 + "Return a sequence of indexes of a post-ordered walk of the graph." 1.115 + [g] 1.116 + (fnext (reduce #(post-ordered-visit g %2 %1) 1.117 + [#{} []] 1.118 + (:nodes g)))) 1.119 + 1.120 +(defn scc 1.121 + "Returns, as a sequence of sets, the strongly connected components 1.122 + of g." 1.123 + [g] 1.124 + (let [po (reverse (post-ordered-nodes g)) 1.125 + rev (reverse-graph g) 1.126 + step (fn [stack visited acc] 1.127 + (if (empty? stack) 1.128 + acc 1.129 + (let [[nv comp] (post-ordered-visit rev 1.130 + (first stack) 1.131 + [visited #{}]) 1.132 + ns (remove nv stack)] 1.133 + (recur ns nv (conj acc comp)))))] 1.134 + (step po #{} []))) 1.135 + 1.136 +(defn component-graph 1.137 + "Given a graph, perhaps with cycles, return a reduced graph that is acyclic. 1.138 + Each node in the new graph will be a set of nodes from the old. 1.139 + These sets are the strongly connected components. Each edge will 1.140 + be the union of the corresponding edges of the prior graph." 1.141 + ([g] 1.142 + (component-graph g (scc g))) 1.143 + ([g sccs] 1.144 + (let [find-node-set (fn [n] 1.145 + (some #(if (% n) % nil) sccs)) 1.146 + find-neighbors (fn [ns] 1.147 + (let [nbs1 (map (partial get-neighbors g) ns) 1.148 + nbs2 (map set nbs1) 1.149 + nbs3 (apply union nbs2)] 1.150 + (set (map find-node-set nbs3)))) 1.151 + nm (into {} (map (fn [ns] [ns (find-neighbors ns)]) sccs))] 1.152 + (struct directed-graph (set sccs) nm)))) 1.153 + 1.154 +(defn recursive-component? 1.155 + "Is the component (recieved from scc) self recursive?" 1.156 + [g ns] 1.157 + (or (> (count ns) 1) 1.158 + (let [n (first ns)] 1.159 + (some #(= % n) (get-neighbors g n))))) 1.160 + 1.161 +(defn self-recursive-sets 1.162 + "Returns, as a sequence of sets, the components of a graph that are 1.163 + self-recursive." 1.164 + [g] 1.165 + (filter (partial recursive-component? g) (scc g))) 1.166 + 1.167 + 1.168 +;; Dependency Lists 1.169 + 1.170 +(defn fixed-point 1.171 + "Repeatedly apply fun to data until (equal old-data new-data) 1.172 + returns true. If max iterations occur, it will throw an 1.173 + exception. Set max to nil for unlimited iterations." 1.174 + [data fun max equal] 1.175 + (let [step (fn step [data idx] 1.176 + (when (and idx (= 0 idx)) 1.177 + (throw (Exception. "Fixed point overflow"))) 1.178 + (let [new-data (fun data)] 1.179 + (if (equal data new-data) 1.180 + new-data 1.181 + (recur new-data (and idx (dec idx))))))] 1.182 + (step data max))) 1.183 + 1.184 +(defn- fold-into-sets 1.185 + [priorities] 1.186 + (let [max (inc (apply max 0 (vals priorities))) 1.187 + step (fn [acc [n dep]] 1.188 + (assoc acc dep (conj (acc dep) n)))] 1.189 + (reduce step 1.190 + (vec (replicate max #{})) 1.191 + priorities))) 1.192 + 1.193 +(defn dependency-list 1.194 + "Similar to a topological sort, this returns a vector of sets. The 1.195 + set of nodes at index 0 are independent. The set at index 1 depend 1.196 + on index 0; those at 2 depend on 0 and 1, and so on. Those withing 1.197 + a set have no mutual dependencies. Assume the input graph (which 1.198 + much be acyclic) has an edge a->b when a depends on b." 1.199 + [g] 1.200 + (let [step (fn [d] 1.201 + (let [update (fn [n] 1.202 + (inc (apply max -1 (map d (get-neighbors g n)))))] 1.203 + (into {} (map (fn [[k v]] [k (update k)]) d)))) 1.204 + counts (fixed-point (zipmap (:nodes g) (repeat 0)) 1.205 + step 1.206 + (inc (count (:nodes g))) 1.207 + =)] 1.208 + (fold-into-sets counts))) 1.209 + 1.210 +(defn stratification-list 1.211 + "Similar to dependency-list (see doc), except two graphs are 1.212 + provided. The first is as dependency-list. The second (which may 1.213 + have cycles) provides a partial-dependency relation. If node a 1.214 + depends on node b (meaning an edge a->b exists) in the second 1.215 + graph, node a must be equal or later in the sequence." 1.216 + [g1 g2] 1.217 + (assert (= (-> g1 :nodes set) (-> g2 :nodes set))) 1.218 + (let [step (fn [d] 1.219 + (let [update (fn [n] 1.220 + (max (inc (apply max -1 1.221 + (map d (get-neighbors g1 n)))) 1.222 + (apply max -1 (map d (get-neighbors g2 n)))))] 1.223 + (into {} (map (fn [[k v]] [k (update k)]) d)))) 1.224 + counts (fixed-point (zipmap (:nodes g1) (repeat 0)) 1.225 + step 1.226 + (inc (count (:nodes g1))) 1.227 + =)] 1.228 + (fold-into-sets counts))) 1.229 + 1.230 + 1.231 +;; End of file