;;
;;
;; Persistent directed graph based on adjacency intervals.
;;
;; Copyright 2010-2013 Ivan Raikov and the Okinawa Institute of Science and Technology.
;;
;;
;; This program is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; A full copy of the GPL license can be found at
;; .
(module interval-digraph
(
interval-digraph
;; digraph-union digraph-disjoint-union digraph-rename
;; make-random-gnp-digraph
)
(import scheme chicken)
(require-extension rb-tree typeclass)
(require-library cis )
(import (prefix cis cis:))
(import (only data-structures alist-ref compose ->string)
(only srfi-1 find any append-reverse delete-duplicates fold))
(define (interval-digraph:error x . rest)
(let ((port (open-output-string)))
(let loop ((objs (cons x rest)))
(if (null? objs)
(begin
(newline port)
(error 'digraph (get-output-string port)))
(begin (display (car objs) port)
(display " " port)
(loop (cdr objs)))))))
(define (alist-update k v alst #!optional (eq eq?))
(cons (cons k v)
(let recur ((alst alst) (ax '()))
(cond ((null? alst) (reverse ax))
((eq k (car (car alst))) (recur (cdr alst) ax))
(else (recur (cdr alst) (cons (car alst) ax)))))))
(define (node-set-lookup-compare x y)
(if (cis:subset? x y) 0
(let ((xmax (cis:get-max x))
(xmin (cis:get-min x))
(ymax (cis:get-max y))
(ymin (cis:get-min y)))
(cond ((fx= ymax xmax) (fx- ymin xmin))
(else (fx- ymax xmax))))
))
(define (node-set-insdel-compare x y)
(let ((xmax (cis:get-max x))
(xmin (cis:get-min x))
(ymax (cis:get-max y))
(ymin (cis:get-min y)))
(cond ((fx= ymax xmax) (fx- ymin xmin))
(else (fx- ymax xmax)))))
(define-record-type interval-digraph
(make-interval-digraph name label nodes context-tree)
interval-digraph?
(name graph-name)
(label graph-label)
(nodes graph-nodes)
(context-tree graph-context)
)
(define-record-type context
(make-context node label edges)
context?
(node context-node)
(label context-label)
(edges context-edges)
)
(define (context-add-edge c e)
(make-context (context-node c)
(context-label c)
(cons e (context-edges c))))
(define prop-map
(rb-tree-map node-set-lookup-compare
insdel-key-compare: node-set-insdel-compare))
(define node-map
(rb-tree-map fx-))
(import-instance ( prop-map prop.)
( node-map node.))
(define-class
empty
empty?
size
order
nodes
nodes-with-labels
node-label
edges
edges-with-labels
edge-label
add-node
add-node-interval
add-edge
add-edge-interval
get-succ
out-edges
has-edge
has-node
has-node-interval
foreach-node
foreach-node-with-label
foreach-edge
foreach-edge-with-label
)
(define (empty name label)
(make-interval-digraph name label cis:empty (node.empty) ))
(define (get-nodes g) (cis:elements (graph-nodes g)))
(define (get-nodes-with-labels g)
((node.map (graph-context g))
(lambda (v)
(cons (context-node v)
(context-label v)))
))
(define (node-label g i)
(let ((context (graph-context g)))
(let ((c ((node.get-value context) i)))
(context-label c))))
(define (get-edges g)
((node.foldi (graph-context g))
(lambda (i v ax)
(fold (lambda (e ax)
(cis:fold-left (lambda (j ax) (cons (list i j) ax)) ax (car e)))
ax (context-edges v))
)
'()
))
(define (get-edges-with-labels g)
((node.foldi (graph-context g))
(lambda (i v ax)
(fold
(lambda (e ax)
(let ((l (cdr e)))
(cis:fold-left (lambda (j ax) (cons (list i j l) ax)) ax (car e))))
ax (context-edges v))
)
'()
))
(define (edge-label g i j)
(let ((context (graph-context g)))
(let ((c ((node.get-value context) i)))
(let ((es (context-edges c)))
(let ((e (find (lambda (e) (cis:in? j (car e))) es)))
(and e (cdr e))
))
))
)
(define (order g) (cis:cardinal (graph-nodes g)))
(define (size g) ((node.fold (graph-context g))
(lambda (v ax)
(fold
(lambda (e ax) (+ (cis:cardinal (car e)) ax))
ax (context-edges v)))
0))
(define (add-node g i #!key (label #f) )
(make-interval-digraph
(graph-name g) (graph-label g)
(cis:add i (graph-nodes g))
(node.put (graph-context g)
i (make-context i label (list) ))
))
(define (add-node-interval g i #!key (label #f) )
(make-interval-digraph
(graph-name g) (graph-label g)
(cis:union i (graph-nodes g))
(cis:fold-left
(lambda (ii ax)
(node.put ax
ii (make-context ii label (list) )))
(graph-context g) i)
))
(define (add-edge g e #!key (label #f))
(cond ((and (pair? e) (pair? (cdr e)))
(let ((i (car e)) (j (cadr e))
(nodes (graph-nodes g))
(context (graph-context g)))
(and (cis:in? i nodes) (cis:in? j nodes)
(let ((oi ((node.get-value context) i)))
(let ((context1 (node.put context i (context-add-edge oi (cons (cis:singleton j) label)))))
(make-interval-digraph
(graph-name g) (graph-label g)
nodes context1)
))
))
)
(else (interval-digraph:error 'add-edge ": invalid edge " e))
))
(define (add-edge-interval g es #!key (label #f))
(cond ((and (pair? es) (pair? (cdr es)))
(let ((i (car es)) (j (cadr es))
(nodes (graph-nodes g))
(context (graph-context g)))
(and (cis:in? i nodes) (cis:subset? j nodes)
(let ((oi ((node.get-value context) i)))
(let ((context-tree1 (node.put context i (context-add-edge oi (cons j label)))))
(make-interval-digraph
(graph-name g) (graph-label g)
nodes context-tree1)
))
))
)
(else (interval-digraph:error 'add-edge ": invalid edge " es))
))
(define (get-succ g i)
(and (cis:in? i (graph-nodes g)
((node.get-value (graph-context g)) i))))
(define (out-edges g i)
(and (cis:in? i (graph-nodes g) )
(let ((c ((node.get-value (graph-context g)) i)))
(fold (lambda (e ax) (cis:fold-left (lambda (j ax) (list i j)) ax (car e)))
'()
(context-edges c)))))
(define (has-edge g i j)
(and (cis:in? i (graph-nodes g))
(let ((c ((node.get-value (graph-context g)) i)))
(any (lambda (e ax) (cis:in? j (car e)))
(context-edges c)))))
(define (has-node g i) (cis:in? i (graph-nodes g) ))
(define (has-node-interval g i) (cis:subset? i (graph-nodes g) ))
(define (foreach-node g f) (cis:foreach (lambda (i) (f i)) (graph-nodes g)))
(define (foreach-node-with-label g f)
((node.for-each-ascending (graph-context g))
(lambda (v)
(f (context-node v) (context-label v)))
))
(define (foreach-edge g f #!key (default-label 'undefined))
((node.for-each-ascending (graph-context g))
(lambda (v)
(let ((i (context-node (cdr v))))
(for-each
(lambda (e)
(cis:foreach (lambda (j) (f i j)) (car e)))
(context-edges (cdr v))))
)
))
(define (foreach-edge-with-label g f)
((node.for-each-ascending (graph-context g))
(lambda (v)
(let ((i (context-node (cdr v))))
(for-each
(lambda (e)
(let ((l (cdr e)))
(cis:foreach (lambda (j) (f i j l)) (car e))))
(context-edges (cdr v))))
)
))
(define (interval-digraph)
(make-
;; empty
empty
;; empty?
(lambda (g) (cis:empty? (graph-nodes g)))
;; size
size
;; order
order
;; nodes
get-nodes
;; nodes-with-labels
get-nodes-with-labels
;; node-label
node-label
;; edges
get-edges
;; edges-with-labels
get-edges-with-labels
;; edge-label
edge-label
;; add-node
add-node
;; add-node-interval
add-node-interval
;; add-edge
add-edge
;; add-edge-interval
add-edge-interval
get-succ
out-edges
has-edge
has-node
has-node-interval
foreach-node
foreach-node-with-label
foreach-edge
foreach-edge-with-label
))
#|
(define (merge a b compare merge-fn)
(let recur ((a a) (b b) (l '()))
(cond ((and (null? a) (null? b)) (reverse l))
((null? a) (append-reverse l b))
((null? b) (append-reverse l a))
(else
(let ((c (compare (car a) (car b))))
(cond ((negative? c) (recur (cdr a) b (cons (car a) l)))
((zero? c) (recur (cdr a) (cdr b) (cons (merge-fn (car a) (car b)) l)))
((positive? c) (recur a (cdr b) (cons (car b) l))))))
)))
(define (digraph-union a b merge-label)
(define (merge-nodes a b)
(merge a b
(lambda (x y) (fx- (car x) (car y)))
(lambda (x y) x)))
(define (merge-nodes-with-labels a b)
(merge a b
(lambda (x y) (fx- (car x) (car y)))
(lambda (x y) (list (car x) (merge-label (cadr x) (cadr y))))))
(define (merge-edges a b)
(merge a b
(lambda (x y) (let ((c (fx- (car x) (car y))))
(if (zero? c) (fx- (cadr x) (cadr y)) c)))
(lambda (x y) x)))
(define (merge-edges-with-labels a b)
(merge a b
(lambda (x y) (let ((c (fx- (car x) (car y))))
(if (zero? c) (fx- (cadr x) (cadr y)) c)))
(lambda (x y) (list (car x) (cadr x) (merge-label (caddr x) (caddr y))))))
(let recur ((a a) (b b))
(let* (;; accessors
(name (string-append "union " (a 'name) (b 'name)))
(label (merge-label (a 'label) (b 'label)))
(nodes (lambda () (cis:elements (cis:union (a 'node-intervals) (b 'node-intervals)))))
(nodes-with-labels (lambda ()
(merge-nodes-with-labels
(a 'nodes-with-labels)
(b 'nodes-with-labels))))
(node-intervals (lambda () (cis:union (a 'node-intervals) (b 'node-intervals))))
(edges (lambda () (merge-edges (a 'edges) (b 'edges))))
(edges-with-labels (lambda () (merge-edges-with-labels
(a 'edges-with-labels)
(b 'edges-with-labels))))
(order (lambda () (cis:cardinal (cis:union (a 'node-intervals) (b 'node-intervals)))))
(size (lambda () (length (edges))))
(capacity order)
(out-edges (lambda (i) (merge-edges ((a 'out-edges) i) ((b 'out-edges) i))))
(succ (lambda (i) (cis:elements (cis:union ((a 'succ-interval) i) ((b 'succ-interval) i)))))
(succ-interval (lambda (i) (cis:union ((a 'succ-interval) i) ((b 'succ-interval) i))))
(has-edge (lambda (i j) (or ((a 'has-edge) i j) ((b 'has-edge) i j))))
(has-node (lambda (i) (or ((a 'has-node) i) ((b 'has-node) i))))
(has-node-interval (lambda (i) (or ((a 'has-node-interval) i) ((b 'has-node-interval) i))))
(node-property-list-keys (lambda () (delete-duplicates (append ((a 'node-property-list-keys)) ((b 'node-property-list-keys))))))
(node-property (lambda (p i) (or ((a 'node-property) p i) ((b 'node-property) p i))))
(node-interval-property (lambda (p i) (or ((a 'node-interval-property) p i) ((b 'node-interval-property) p i))))
(node-label (lambda (i) (or ((a 'node-label) i) ((b 'node-label) i))))
(edge-property-list-keys (lambda () (delete-duplicates (append ((a 'edge-property-list-keys)) ((b 'edge-property-list-keys))))))
(edge-property-list-map (lambda () (delete-duplicates (append ((a 'edge-property-list-map)) ((b 'edge-property-list-map))))))
(edge-property (lambda (p i j) (or ((a 'edge-property) p i j) ((b 'edge-property) i j))))
(edge-interval-property (lambda (p i j) (or ((a 'edge-interval-property) p i j) ((b 'edge-interval-property) i j))))
(foreach-node (lambda (f) (for-each f (nodes))))
(foreach-node-with-label (lambda (f) (for-each f (nodes-with-labels))))
(foreach-edge (lambda (f) (for-each f (edges))))
;; transformers
(add-node
(lambda (n #!key (label #f))
(recur ((a 'add-node) n label: label) ((b 'add-node) n label: label))))
(add-node-interval
(lambda (i #!key (label #f))
(recur ((a 'add-node-interval) i label: label) ((b 'add-node-interval) i label: label))))
(add-edge-interval
(lambda (e)
(recur ((a 'add-edge-interval) e) ((b 'add-edge-interval) e))))
(edge-interval-property-set
(lambda (p i j v) (let* ((a1 ((a 'edge-interval-property-set) p i j v))
(b1 (and (not a1) ((b 'edge-interval-property-set) p i j v))))
(cond (a1 (recur a1 b))
(b1 (recur a b1))
(else (recur a b))))))
(node-interval-property-set
(lambda (p i v) (let* ((a1 ((a 'node-interval-property-set) p i v))
(b1 (and (not a1) ((b 'node-interval-property-set) p i v))))
(cond (a1 (recur a1 b))
(b1 (recur a b1))
(else (recur a b))))))
(node-label-set
(lambda (i v) (let* ((a1 ((a 'node-label-set) i v))
(b1 (and (not a1) ((b 'node-label-set) i v))))
(cond (a1 (recur a1 b))
(b1 (recur a b1))
(else (recur a b))))))
(node-property-set
(lambda (p i v) (let* ((a1 ((a 'node-property-set) p i v))
(b1 (and (not a1) ((b 'node-property-set) p i v))))
(cond (a1 (recur a1 b))
(b1 (recur a b1))
(else (recur a b))))))
(edge-property-set
(lambda (p i j v) (let* ((a1 ((a 'edge-property-set) p i j v))
(b1 (and (not a1) ((b 'edge-property-set) p i j v))))
(cond (a1 (recur a1 b))
(b1 (recur a b1))
(else (recur a b))))))
)
(lambda (selector)
(case selector
;; accessors
((name) name)
((label) label)
((nodes) nodes)
((nodes-with-labels) nodes-with-labels)
((node-intervals) node-intervals)
((edges) edges)
((edges-with-labels) edges-with-labels)
((order) order)
((size) size)
((capacity) capacity)
((out-edges) out-edges)
((succ) succ)
((succ-interval) succ-interval)
((has-edge) has-edge)
((has-node) has-node)
((has-node-interval) has-node-interval)
((node-property-list-keys) node-property-list-keys)
((node-property) node-property)
((node-interval-property) node-interval-property)
((node-label) node-label)
((edge-property) edge-property)
((edge-property-list-keys) edge-property-list-keys)
((edge-property-list-map) edge-property-list-map)
((foreach-node) foreach-node)
((foreach-node-with-label) foreach-node-with-label)
((foreach-edge) foreach-edge)
;; transformers
((add-node) add-node)
((add-node-interval) add-node-interval)
((add-edge-interval) add-edge-interval)
((node-label-set) node-label-set)
((node-property-set) node-property-set)
((node-interval-property-set) node-interval-property-set)
((edge-property-set) edge-property-set)
((edge-interval-property-set) edge-interval-property-set)
(else
(interval-digraph:error 'selector ": unknown message " selector " sent to a graph"))))
)))
;;
;; Adds a number k to all node ids of the graph
;;
(define (digraph-rename k a)
(define (rename-nodes ns) (map (lambda (x) (list (fx+ k x))) ns))
(define (rename-nodes-with-labels ns) (map (lambda (x) (list (fx+ k (car x)) (cadr x))) ns))
(define (rename-edges es) (map (lambda (e) (list (fx+ k (car e)) (fx+ k (cadr e)))) es))
(define (rename-edges-with-labels es) (map (lambda (e) (list (fx+ k (car e)) (fx+ k (cadr e)) (caddr e))) es))
(let recur ((a a))
(let* (;; accessors
(name (a 'name))
(label (a 'label))
(nodes (lambda () (cis:elements (cis:shift k (a 'node-intervals) ))))
(nodes-with-labels (lambda () (rename-nodes-with-labels (a 'nodes-with-labels))))
(node-intervals (lambda () (cis:shift k (a 'node-intervals) )))
(edges (lambda () (rename-edges (a 'edges) )))
(edges-with-labels (lambda () (rename-edges-with-labels (a 'edges-with-labels) )))
(order (lambda () (a 'order)))
(size (a 'size))
(capacity order)
(out-edges (lambda (i) (rename-edges ((a 'out-edges) (fx- i k)))))
(succ (lambda (i) (cis:elements (cis:shift k ((a 'succ-interval) (fx- i k) )))))
(succ-interval (lambda (i) (cis:shift k ((a 'succ-interval) (fx- i k) ))))
(has-edge (lambda (i j) ((a 'has-edge) (fx- i k) (fx- j k))))
(has-node (lambda (i) ((a 'has-node) (fx- i k))))
(has-node-interval (lambda (i) ((a 'has-node-interval) (cis:shift (fxneg k) i))))
(node-property-list-keys (a 'node-property-list-keys))
(node-property (lambda (p i) ((a 'node-property) p (fx- i k) )))
(node-interval-property (lambda (p i) ((a 'node-interval-property) p (cis:shift (fxneg k) i) )))
(node-label (lambda (i) ((a 'node-label) (fx- i k))))
(edge-property-list-keys (a 'edge-property-list-keys))
(edge-property-list-map (a 'edge-property-list-map))
(edge-property (lambda (p i j) ((a 'edge-property) p (fx- i k) (fx- j k) )))
(edge-interval-property (lambda (p i j) ((a 'edge-interval-property) p (cis:shift (fxneg k) i) (cis:shift (fxneg k) j) )))
(foreach-node (lambda (f) (for-each (lambda (i) (f (fx+ i k))) (nodes))))
(foreach-node-with-label (lambda (f) (for-each (lambda (x) (f (fx+ (car x) k) (cadr x))) (nodes-with-labels))))
(foreach-edge (lambda (f) (for-each (lambda (e) (f (list (fx+ (car e) k) (fx+ (cadr e) k)))) (edges))))
;; transformers
(add-node (lambda (n #!key (label #f)) (recur ((a 'add-node) (fx- n k) label: label) )))
(add-node-interval (lambda (i #!key (label #f)) (recur ((a 'add-node-interval) (cis:shift (fxneg k) i) label: label) )))
(add-edge (lambda (e #!key (label #f)) (recur ((a 'add-edge) (list (fx- (car e) k) (fx- (cadr e) k)) label: label) )))
(add-edge-interval (lambda (e) (recur ((a 'add-edge-interval) (list (fx- (car e) k) (fx- (cadr e) k)) ))))
(edge-interval-property-set
(lambda (p i j v) (recur ((a 'edge-interval-property-set) p
(cis:shift (fxneg k) i) (cis:shift (fxneg k) j) v))))
(node-interval-property-set
(lambda (p i v) (recur ((a 'node-interval-property-set) p
(cis:shift (fxneg k) i) v))))
(node-label-set
(lambda (i v) (recur ((a 'node-label-set) (fx- i k) v))))
(node-property-set
(lambda (p i v) (recur ((a 'node-property-set) p (fx- i k) v))))
(edge-property-set
(lambda (p i j v) (recur ((a 'edge-property-set) p (fx- i k) (fx- j k) v))))
)
(lambda (selector)
(case selector
;; accessors
((name) name)
((label) label)
((nodes) nodes)
((nodes-with-labels) nodes-with-labels)
((node-intervals) node-intervals)
((edges) edges)
((edges-with-labels) edges-with-labels)
((order) order)
((size) size)
((capacity) capacity)
((out-edges) out-edges)
((succ) succ)
((succ-interval) succ-interval)
((has-edge) has-edge)
((has-node) has-node)
((has-node-interval) has-node-interval)
((node-property-list-keys) node-property-list-keys)
((node-property) node-property)
((node-interval-property) node-interval-property)
((node-label) node-label)
((edge-property-list-keys) edge-property-list-keys)
((edge-property-list-map) edge-property-list-map)
((edge-property) edge-property)
((foreach-node) foreach-node)
((foreach-node-with-label) foreach-node-with-label)
((foreach-edge) foreach-edge)
;; transformers
((add-node) add-node)
((add-node-interval) add-node-interval)
((add-edge) add-edge)
((add-edge-interval) add-edge-interval)
((node-label-set) node-label-set)
((node-property-set) node-property-set)
((node-interval-property-set) node-interval-property-set)
((edge-property-set) edge-property-set)
((edge-interval-property-set) edge-interval-property-set)
(else
(interval-digraph:error 'selector ": unknown message " selector " sent to a graph"))))
)))
(define (digraph-disjoint-union a b)
(digraph-union a (digraph-rename ((a 'capacity)) b)
(string->symbol (string-append (->string (a 'label)) "+" (->string (b 'label))))))
;;
;; Naive implementation: randomly choosing edges from NxN possibilities with probability P
;;
(define (make-random-gnp-digraph name label N P R S loops)
(if (< N 10) (error 'make-random-gnp-digraph "N is too small" N))
(if (not (and (< 0 P) (<= P 1))) (error 'make-random-gnp-digraph "P must be in the interval (0, 1]"))
(let* ((E (* N N))
(nodes (cis:interval 1 N))
(a (make-digraph name label))
(a ((a 'add-node-interval) nodes)))
(let recur ((a a) (s S) (e 0))
(if (> e E) a
(let* ((i (inexact->exact (R N P s)))
(j (inexact->exact (R N P s))))
(if (or (zero? i) (zero? j) (and (= i j) (not loops)) ((a 'has-edge) i j))
(recur a s (+ 1 e))
(recur ((a 'add-edge) (list i j)) s (+ 1 e))
)))
)))
|#
)