racket/collects/mztake/demos/dijkstra/graph.ss
2006-04-04 23:22:49 +00:00

543 lines
20 KiB
Scheme

;; -*- compile-command: "mzscheme -M errortrace -u graph.ss" -*-
(module graph mzscheme
(require (lib "base-gm.ss" "frtime")
(lib "etc.ss")
(lib "list.ss"))
(provide make-graph
;; --- Constructors :
graph?
graph-directed?
graph-make-similar
graph-copy
graph-add-all!
;; --- Functions on nodes:
graph-nodes
graph-nodes-size
graph-make-node!
graph-node-add!
graph-node-mem?
graph-node-set!
graph-node-remove!
graph-node-collapse!
graph-node-has-label?
graph-node-label
graph-for-each-node
graph-fold-nodes
;; --- Functions on neighbors:
graph-succs
graph-preds
graph-adjs
graph-for-each-adjs
;; --- Functions on edges:
graph-edges
graph-edges-size
graph-edge-add!
graph-edge-mem?
graph-edge-set!
graph-edge-remove!
graph-edge-has-label?
graph-edge-label
graph-for-each-edge
graph-fold-edges
;; --- Simple graph algorithms:
graph-dfs-from-node
graph-dfs-all
graph-components
graph-strongly-connected-components
graph-topological-sort
;; --- Debugging:
graph-to-list
graph-to-string
graph-test
)
(define-struct t (flags nNodes nEdges nodes successors predessessors))
;; Flags can be: 'equal 'directed 'unique-node 'unique-edge 'nodes-must-exists 'safe
;; 'safe is a short for '(unique-node unique-edge nodes-must-exists)
(define (make-graph . flags)
(let ((flag-hash (make-hash)))
(set! flags (expands-safe-flag flags))
(for-each (lambda (flag) (hash-put! flag-hash flag true)) flags)
(if (member 'equal flags)
(make-t flag-hash 0 0 (make-hash 'equal) (make-hash 'equal) (make-hash 'equal))
(make-t flag-hash 0 0 (make-hash) (make-hash) (make-hash)))))
(define (graph? graph) (t? graph))
(define no-value (box empty))
;; Makes a hash with the same 'equal as the graph
(define (graph-make-hash graph)
(if (graph-has-flag? graph 'equal)
(make-hash 'equal)
(make-hash)))
(define (expands-safe-flag flags)
(let loop ((cur flags) (acc empty))
(cond [(empty? cur) acc]
[(eq? (first cur) 'safe) (loop (rest cur) (append '(unique-node unique-edge nodes-must-exists) flags))]
[true (loop (rest cur) (cons (first cur) acc))])))
;; Make a graph with mostly the same flags as another graph
(define (graph-make-similar graph plus-flags minus-flags)
(set! plus-flags (expands-safe-flag plus-flags))
(set! minus-flags (expands-safe-flag minus-flags))
(apply make-graph
(append plus-flags
(filter (lambda (i) (not (member i minus-flags)))
(hash-keys (t-flags graph))))))
(define (graph-copy graph)
(let* ((rtn-nodes (graph-make-hash graph))
(rtn-successors (graph-make-hash graph))
(rtn-predessessors (graph-make-hash graph))
(rtn (make-t (t-flags graph) (t-nNodes graph) (t-nEdges graph) rtn-nodes rtn-successors rtn-predessessors)))
(hash-add-all! rtn-nodes (t-nodes graph))
(hash-add-all! rtn-successors (t-successors graph))
(hash-add-all! rtn-predessessors (t-predessessors graph))
rtn))
(define (graph-add-all! dest-graph src-graph)
(graph-for-each-node
src-graph
(lambda (node)
(if (graph-node-has-label? src-graph node)
(graph-node-add! dest-graph node (graph-node-label src-graph node))
(graph-node-add! dest-graph node))))
(graph-for-each-edge
src-graph
(lambda (from to)
(if (graph-edge-has-label? src-graph from to)
(graph-edge-add! dest-graph from to (graph-edge-label src-graph from to))
(graph-edge-add! dest-graph from to)))))
(define (graph-has-flag? graph flag)
(hash-mem? (t-flags graph) flag))
(define (graph-directed? graph)
(hash-mem? (t-flags graph) 'directed))
;;; =====================================================================
;;; Nodes
(define (graph-nodes graph) (hash-keys (t-nodes graph)))
(define (graph-nodes-size graph) (t-nNodes graph))
(define graph-make-node!
(case-lambda
[(graph) (graph-make-node! graph no-value)]
[(graph val)
(let ((sym (string->symbol (string-append "node" (number->string (t-nNodes graph))))))
(graph-node-add! graph sym val)
sym)]))
;; Add a node to the graph. If the node already exists,
;; sets its label, unless the graph has the 'unique-node property,
;; in which case this will assert.
(define graph-node-add!
(case-lambda
[(graph node) (graph-node-add! graph node no-value)]
[(graph node val)
(if (hash-mem? (t-nodes graph) node)
(assert (not (graph-has-flag? graph 'unique-node)))
(begin
(set-t-nNodes! graph (+ 1 (t-nNodes graph)))
(hash-put! (t-successors graph) node (graph-make-hash graph))
(if (graph-directed? graph)
(hash-put! (t-predessessors graph) node (graph-make-hash graph)))))
(hash-put! (t-nodes graph) node val)]))
(define (graph-node-mem? graph node)
(hash-mem? (t-nodes graph) node))
(define (graph-node-set! graph node val)
(assert (hash-mem? (t-nodes graph) node))
(hash-put! (t-nodes graph) node val))
(define (graph-node-remove! graph node)
(assert (graph-node-mem? graph node))
(for-each (lambda (i) (graph-edge-remove! graph node i))
(hash-keys (hash-get (t-successors graph) node)))
(if (graph-directed? graph)
(for-each (lambda (i) (graph-edge-remove! graph i node))
(hash-keys (hash-get (t-predessessors graph) node))))
(hash-remove! (t-nodes graph) node)
(hash-remove! (t-successors graph) node)
(if (graph-directed? graph)
(hash-remove! (t-predessessors graph) node))
(set-t-nNodes! graph (- (t-nNodes graph) 1)))
(define graph-node-collapse!
(case-lambda
[(graph node with-self-loop) (graph-node-collapse! graph node with-self-loop (lambda (pred-label succ-label) no-value))]
[(graph node with-self-loop label-fn)
(let ((is-directed (graph-directed? graph)))
(for-each
(lambda (pred)
(for-each
(lambda (succ)
(unless (or (and (not is-directed) (eq? pred succ))
(graph-edge-mem? graph pred succ))
(let* ((label-pred (hash-get (hash-get (t-successors graph) pred) node))
(label-succ (hash-get (hash-get (t-successors graph) node) succ))
(new-label (label-fn (if (eq? label-pred no-value) false label-pred)
(if (eq? label-succ no-value) false label-succ))))
(when (or with-self-loop (not (eq? pred succ)))
(hash-put! (hash-get (t-successors graph) pred) succ new-label)
(if is-directed
(hash-put! (hash-get (t-predessessors graph) succ) pred new-label)
(hash-put! (hash-get (t-successors graph) succ) pred new-label))))))
(hash-keys (hash-get (t-successors graph) node))))
(hash-keys (hash-get
(if is-directed (t-predessessors graph) (t-successors graph))
node))))
(graph-node-remove! graph node)]))
(define (graph-node-has-label? graph node)
(not (eq? (hash-get (t-nodes graph) node) no-value)))
(define (graph-node-label graph node)
(let ((r (hash-get (t-nodes graph) node)))
(if (eq? r no-value) (error "graph-node-label: no value for node" node)
r)))
(define (graph-succs graph node)
(assert (graph-directed? graph))
(hash-keys (hash-get (t-successors graph) node)))
(define (graph-preds graph node)
(assert (graph-directed? graph))
(hash-keys (hash-get (t-predessessors graph) node)))
(define (graph-adjs graph node)
(if (graph-directed? graph)
(append (hash-keys (hash-get (t-successors graph) node))
(hash-keys (hash-get (t-predessessors graph) node)))
(hash-keys (hash-get (t-successors graph) node))))
(define (graph-for-each-adjs graph node fn)
(for-each (hash-keys (hash-get (t-successors graph) node))
(lambda (succ) (fn node succ)))
(when (graph-directed? graph)
(for-each (hash-keys (hash-get (t-predessessors graph) node))
(lambda (pred) (fn pred node)))))
(define (graph-for-each-node graph fn)
(for-each fn (hash-keys (t-nodes graph))))
(define (graph-fold-nodes graph init fn)
(let ((acc init))
(graph-for-each-node
graph
(lambda (node) (set! acc (fn node acc))))
acc))
;;; =====================================================================
;;; Edges
(define (graph-edges graph)
(let ((rtn empty))
(graph-for-each-edge graph (lambda (from to) (set! rtn (cons from to))))
rtn))
(define (graph-edges-size graph) (t-nEdges graph))
;; Add an edge to the graph. If the edge already exists,
;; sets its label, unless the graph has the 'unique-edge property,
;; in which case this will assert.
(define graph-edge-add!
(case-lambda
[(graph from to) (graph-edge-add! graph from to no-value)]
[(graph from to val)
(if (graph-edge-mem? graph from to)
(assert (not (graph-has-flag? graph 'unique-edge)))
(set-t-nEdges! graph (+ (t-nEdges graph) 1)))
(if (graph-has-flag? graph 'nodes-must-exists)
(assert (and (graph-node-mem? graph from) (graph-node-mem? graph to)))
(begin (if (not (graph-node-mem? graph from)) (graph-node-add! graph from))
(if (not (graph-node-mem? graph to)) (graph-node-add! graph to))))
(hash-put! (hash-get (t-successors graph) from) to val)
(if (graph-directed? graph)
(hash-put! (hash-get (t-predessessors graph) to) from val)
(hash-put! (hash-get (t-successors graph) to) from val))]))
(define (graph-edge-mem? graph from to)
(if (graph-has-flag? graph 'nodes-must-exists)
(assert (and (graph-node-mem? graph from)
(graph-node-mem? graph to))))
(and (hash-mem? (t-successors graph) from)
(hash-mem? (hash-get (t-successors graph) from) to)))
(define (graph-edge-set! graph from to val)
(assert (graph-edge-mem? graph from to))
(hash-put! (hash-get (t-successors graph) from) to val)
(if (graph-directed? graph)
(hash-put! (hash-get (t-predessessors graph) to) from val)
(hash-put! (hash-get (t-successors graph) to) from val)))
(define (graph-edge-remove! graph from to)
(assert (graph-edge-mem? graph from to))
(hash-remove! (hash-get (t-successors graph) from) to)
(if (graph-directed? graph)
(hash-remove! (hash-get (t-predessessors graph) to) from)
(hash-remove! (hash-get (t-successors graph) to) from)))
(define (graph-edge-has-label? graph from to)
(not (eq? (hash-get (hash-get (t-successors graph) from) to) no-value)))
(define (graph-edge-label graph from to)
(let ((r (hash-get (hash-get (t-successors graph) from) to)))
(if (eq? r no-value) (error "graph-edge-label: no value for edge" (cons from to)))
r))
(define (graph-for-each-edge graph fn)
(graph-for-each-node
graph
(lambda (from)
(for-each (lambda (to) (fn from to))
(hash-keys (hash-get (t-successors graph) from))))))
(define (graph-fold-edges graph init fn)
(let ((acc init))
(graph-for-each-edge
graph
(lambda (from to) (set! acc (fn from to acc))))
acc))
;;; =====================================================================
;;; Algos
(define (graph-dfs-from-node-with-log graph node dealt-with pre-fn post-fn backward)
(assert (or (not backward) (graph-directed? graph)))
(if (not (hash-mem? dealt-with node))
(begin (hash-put! dealt-with node true)
(pre-fn node)
(for-each (lambda (n) (graph-dfs-from-node-with-log graph n dealt-with pre-fn post-fn backward))
(if backward
(hash-keys (hash-get (t-predessessors graph) node))
(hash-keys (hash-get (t-successors graph) node))))
(post-fn node))))
(define graph-dfs-from-node
(case-lambda
[(graph node pre-fn) (graph-dfs-from-node graph node pre-fn (lambda (i) i))]
[(graph node pre-fn post-fn)
(graph-dfs-from-node-with-log graph node (graph-make-hash graph) pre-fn post-fn false)]))
(define graph-dfs-all
(case-lambda
[(graph pre-fn) (graph-dfs-all graph pre-fn (lambda (i) i))]
[(graph pre-fn post-fn)
(let ((dealt-with (graph-make-hash graph)))
(graph-for-each-node graph (lambda (n) (if (not (hash-mem? dealt-with n))
(graph-dfs-from-node-with-log graph n dealt-with pre-fn post-fn false)))))]))
(define (graph-components graph)
(let ((dealt-with (graph-make-hash graph)))
(graph-fold-nodes
graph
empty
(lambda (node acc)
(if (hash-mem? dealt-with node) acc
(let ((cur-component
(let loop ((cur node) (acc empty))
(if (hash-mem? dealt-with cur) acc
(begin (hash-put! dealt-with cur true)
(foldl (lambda (adj acc) (loop adj acc)) (cons cur acc)
(graph-adjs graph cur)))))))
(cons cur-component acc)))))))
(define (graph-strongly-connected-components graph)
(assert (graph-directed? graph))
(let ((finish-times empty)
(dealt-with (graph-make-hash graph)))
(graph-for-each-node
graph
(lambda (n) (graph-dfs-from-node-with-log
graph n dealt-with
(lambda (i) i)
(lambda (i) (set! finish-times (cons i finish-times)))
false)))
(set! dealt-with (graph-make-hash graph))
(let ((component-graph (graph-make-similar graph empty '(safe equal)))
(node2supernode (make-hash)))
(for-each
(lambda (n)
(if (not (hash-mem? dealt-with n))
(let ((super-node (graph-make-node! component-graph empty)))
(graph-dfs-from-node-with-log
graph n dealt-with
(lambda (i)
(graph-node-set! component-graph super-node (cons i (graph-node-label component-graph super-node)))
(hash-put! node2supernode i super-node))
(lambda (i) i)
true))))
finish-times)
(graph-for-each-edge graph
(lambda (from to)
(graph-edge-add! component-graph
(hash-get node2supernode from)
(hash-get node2supernode to))))
(cons component-graph node2supernode))))
(define (graph-topological-sort graph)
(assert (graph-directed? graph))
(let ((rtn empty))
(graph-dfs-all graph (lambda (i) i) (lambda (node) (set! rtn (cons node rtn))))
rtn))
;;; =====================================================================
;;; Utils
(define graph-to-list
(case-lambda
[(graph) (graph-to-list graph false)]
[(graph with-labels)
(hash-map (t-nodes graph)
(lambda (node node-val)
(let ((node-rep (if (and with-labels (graph-node-has-label? graph node))
(cons node (graph-node-label graph node))
node)))
(cons node-rep
(hash-fold (hash-get (t-successors graph) node) empty
(lambda (succ edge-val acc)
(if (and with-labels (graph-edge-has-label? graph node succ))
(cons (cons succ (graph-edge-label graph node succ)) acc)
(cons succ acc))))))))]))
(define (graph-to-string-prv graph with-labels to-string)
(let ([the-to-string (or to-string
(lambda (item) (format "~a" item)))])
(string-append (if (graph-directed? graph) "[di-graph: " "[undirected-graph:")
(the-to-string (map (lambda (n)
(cons (first n) (cons '--> (rest n))))
(graph-to-list graph with-labels)))
"]")))
(define (graph-to-string graph . to-string)
(graph-to-string-prv graph false (if (empty? to-string) false (first to-string))))
(define (graph-to-string-with-labels graph . to-string)
(graph-to-string-prv graph true (if (empty? to-string) true (first to-string))))
;;; =====================================================================
;;; Tests
(define (graph-test)
(define graph (make-graph 'safe 'directed))
(graph-node-add! graph 'a)
(graph-node-add! graph 'b 2)
(graph-node-add! graph 'c 3)
(graph-node-add! graph 'd)
(graph-edge-add! graph 'a 'c)
(graph-edge-add! graph 'a 'd "asd")
(graph-edge-add! graph 'b 'c "dfg")
(graph-edge-add! graph 'b 'd)
(graph-edge-add! graph 'd 'a)
(display (graph-node-mem? graph 'a))
(display (graph-edge-mem? graph 'a 'c))
(newline)
(display (graph-node-mem? graph 'v))
(display (graph-edge-mem? graph 'c 'a))
(display (graph-edge-mem? graph 'a 'b))
(newline)
(print-each (graph-to-list graph true))
(graph-for-each-edge graph (lambda (a b) (print-each "A " a b)))
(graph-dfs-from-node graph 'a (lambda (i) (display i)))
(newline)
(graph-dfs-from-node graph 'b (lambda (i) (display i)))
(newline)
(graph-dfs-from-node graph 'c (lambda (i) (display i)))
(newline)
(graph-dfs-from-node graph 'd (lambda (i) (display i)))
(newline)
(let ((star (make-graph 'directed)))
(graph-edge-add! star 1 'x)
(graph-edge-add! star 'x 1)
(graph-edge-add! star 2 'x)
(graph-edge-add! star 'x 3)
(graph-edge-add! star 'x 4)
(graph-edge-add! star 'x 5)
(graph-node-collapse! star 'x false)
(print-each "collapsed:" (graph-to-list star)))
(let ((strong-graph (make-graph 'directed)))
(graph-edge-add! strong-graph 'e 'a)
(graph-edge-add! strong-graph 'a 'b)
(graph-edge-add! strong-graph 'b 'e)
(graph-edge-add! strong-graph 'e 'f)
(graph-edge-add! strong-graph 'b 'f)
(graph-edge-add! strong-graph 'b 'c)
(graph-edge-add! strong-graph 'f 'g)
(graph-edge-add! strong-graph 'g 'f)
(graph-edge-add! strong-graph 'c 'g)
(graph-edge-add! strong-graph 'c 'd)
(graph-edge-add! strong-graph 'd 'c)
(graph-edge-add! strong-graph 'g 'h)
(graph-edge-add! strong-graph 'd 'h)
(graph-edge-add! strong-graph 'h 'h)
(graph-edge-add! strong-graph 'xa 'xb)
(graph-edge-add! strong-graph 'xb 'xc)
(graph-edge-add! strong-graph 'xc 'xa)
(print-each "strong-graph" strong-graph)
(print-each "component" (graph-components strong-graph))
(let ((components (graph-strongly-connected-components strong-graph)))
(print-each "strong-components" components)
(print-each "toposort" (graph-topological-sort (first components)))))
(let ((u-graph (make-graph)))
(graph-edge-add! u-graph 'a 'b)
(graph-edge-add! u-graph 'b 'c)
(graph-edge-add! u-graph 'c 'd)
(graph-edge-add! u-graph 'd 'a)
(graph-edge-add! u-graph 'd 'e)
(graph-edge-add! u-graph 'e 'c)
(graph-edge-add! u-graph 'xa 'xb)
(graph-edge-add! u-graph 'xa 'xc)
(graph-edge-add! u-graph 'xb 'xd)
(newline)
(print-each "u-graph" u-graph)
(graph-edge-remove! u-graph 'b 'a)
(graph-node-remove! u-graph 'd)
(print-each "u-graph" u-graph)
(print-each "component" (graph-components u-graph)))
)
;(graph-test)
)