1129 lines
49 KiB
Racket
1129 lines
49 KiB
Racket
#lang typed/racket
|
|
|
|
(require "utils.rkt" "functions.rkt" "dynamics.rkt"
|
|
typed/graph typed/racket/random
|
|
syntax/parse/define)
|
|
(require/typed racket/syntax
|
|
[format-symbol (-> String Any * Symbol)])
|
|
|
|
(module+ test
|
|
(require typed/rackunit)
|
|
(define skip-expensive-tests? #t)
|
|
(unless skip-expensive-tests?
|
|
(displayln "Running the complete test suite...")))
|
|
|
|
(provide
|
|
State UpdateFunction Domain DomainMapping
|
|
01->boolean/state
|
|
|
|
(struct-out network) Network
|
|
make-same-domains make-boolean-domains make-boolean-network
|
|
make-01-domains make-01-network update
|
|
|
|
UpdateFunctionForm (struct-out network-form) NetworkForm
|
|
update-function-form->update-function/any
|
|
update-function-form->update-function/boolean
|
|
update-function-form->update-function/01
|
|
network-form->network/any network-form->network/boolean
|
|
network-form->network/01 make-boolean-network-form
|
|
forms->boolean-network
|
|
|
|
build-all-states build-all-boolean-states build-all-01-states
|
|
|
|
list-syntactic-interactions build-syntactic-interaction-graph
|
|
interaction? get-interaction-sign build-interaction-graph
|
|
build-interaction-graph/form build-signed-interaction-graph
|
|
build-signed-interaction-graph/form
|
|
|
|
Modality Mode dynamics% Dynamics% make-syn make-asyn
|
|
make-asyn-dynamics make-syn-dynamics build-full-state-graph
|
|
build-full-state-graph/annotated
|
|
pretty-print-state pretty-print-state/01 pretty-print-state-graph-with
|
|
pretty-print-state-graph ppsg pretty-print-state-graph/01 ppsg01
|
|
|
|
tabulate-state* tabulate-state*/boolean
|
|
tabulate-state*+headers tabulate-state*+headers/boolean
|
|
tabulate-state tabulate-state/boolean
|
|
tabulate-state+headers tabulate-state+headers/boolean
|
|
tabulate-network tabulate-network+headers
|
|
|
|
table+vars->network table->network table+headers->network
|
|
|
|
random-function/state random-boolean-function/state random-network
|
|
random-boolean-network random-boolean-network/n
|
|
)
|
|
|
|
(define-type (State a) (VariableMapping a))
|
|
(define-type (UpdateFunction a) (-> (State a) a))
|
|
(define-type (Domain a) (Listof a))
|
|
(define-type (DomainMapping a) (VariableMapping (Domain a)))
|
|
|
|
(: 01->boolean/state (-> (State (U Zero One)) (State Boolean)))
|
|
(define (01->boolean/state s)
|
|
(for/hash ([(x val) (in-hash s)]) : (State Boolean)
|
|
(if (eq? val 1) (values x #t) (values x #f))))
|
|
|
|
(module+ test
|
|
(test-case "01->boolean/state"
|
|
(check-equal? (01->boolean/state (hash 'a 0 'b 1))
|
|
(hash 'a #f 'b #t))))
|
|
|
|
(struct (a) network ([functions : (VariableMapping (UpdateFunction a))]
|
|
[domains : (DomainMapping a)])
|
|
#:transparent
|
|
#:type-name Network)
|
|
|
|
(: make-same-domains (All (a) (-> (Listof Variable) (Domain a)
|
|
(DomainMapping a))))
|
|
(define (make-same-domains vars domain)
|
|
(for/hash ([var vars]) : (DomainMapping a)
|
|
(values var domain)))
|
|
|
|
(module+ test
|
|
(test-case "make-same-domains"
|
|
(check-equal? (make-same-domains '(a b) '(1 2))
|
|
#hash((a . (1 2)) (b . (1 2))))))
|
|
|
|
(: make-boolean-domains (-> (Listof Variable) (DomainMapping Boolean)))
|
|
(define (make-boolean-domains vars)
|
|
(make-same-domains vars '(#f #t)))
|
|
|
|
(module+ test
|
|
(test-case "make-boolean-domains"
|
|
(check-equal? (make-boolean-domains '(a b))
|
|
#hash((a . (#f #t)) (b . (#f #t))))))
|
|
|
|
(: make-boolean-network (-> (VariableMapping (UpdateFunction Boolean))
|
|
(Network Boolean)))
|
|
(define (make-boolean-network funcs)
|
|
(network funcs (make-boolean-domains (hash-keys funcs))))
|
|
|
|
(module+ test
|
|
(test-case "make-boolean-network"
|
|
(define f1 (λ ([s : (State Boolean)])
|
|
(and (hash-ref s 'x1) (not (hash-ref s 'x2)))))
|
|
(define f2 (λ ([s : (State Boolean)])
|
|
(not (hash-ref s 'x2))))
|
|
(define bn (make-boolean-network (hash 'x1 f1 'x2 f2)))
|
|
(check-equal? (network-domains bn) (hash 'x1 '(#f #t) 'x2 '(#f #t)))))
|
|
|
|
(: make-01-domains (-> (Listof Variable) (DomainMapping (U Zero One))))
|
|
(define (make-01-domains vars)
|
|
(make-same-domains vars '(0 1)))
|
|
|
|
(module+ test
|
|
(test-case "make-01-domains"
|
|
(check-equal? (make-01-domains '(a b))
|
|
'#hash((a . (0 1)) (b . (0 1))))))
|
|
|
|
(: make-01-network (-> (VariableMapping (UpdateFunction (U Zero One)))
|
|
(Network (U Zero One))))
|
|
(define (make-01-network funcs)
|
|
(network funcs (make-01-domains (hash-keys funcs))))
|
|
|
|
(module+ test
|
|
(test-case "make-01-network"
|
|
(define f1 (λ ([s : (State (U Zero One))])
|
|
(assert-type (max (hash-ref s 'a) (hash-ref s 'b))
|
|
(U Zero One))))
|
|
(define f2 (λ ([s : (State (U Zero One))])
|
|
(assert-type (min (hash-ref s 'a) (hash-ref s 'b))
|
|
(U Zero One))))
|
|
(define n (make-01-network (hash 'a f1 'b f2)))
|
|
(check-equal? (network-domains n) (hash 'a '(0 1) 'b '(0 1)))))
|
|
|
|
(: update (All (a) (-> (Network a) (State a) (Listof Variable) (State a))))
|
|
(define (update network s xs)
|
|
(define funcs (network-functions network))
|
|
(for/fold ([new-s : (State a) s])
|
|
([x xs])
|
|
(define fx (hash-ref funcs x))
|
|
(hash-set new-s x (fx s))))
|
|
|
|
(module+ test
|
|
(test-case "update"
|
|
(define f1 (λ ([s : (State Boolean)])
|
|
(and (hash-ref s 'x1) (not (hash-ref s 'x2)))))
|
|
(define f2 (λ ([s : (State Boolean)])
|
|
(not (hash-ref s 'x2))))
|
|
(define bn (make-boolean-network (hash 'x1 f1 'x2 f2)))
|
|
(check-equal? (update bn (hash 'x1 #f 'x2 #f) '(x1))
|
|
#hash((x1 . #f) (x2 . #f)))
|
|
(check-equal? (update bn (hash 'x1 #f 'x2 #f) '(x1 x2))
|
|
#hash((x1 . #f) (x2 . #t)))))
|
|
|
|
(define-type UpdateFunctionForm Any)
|
|
|
|
(struct (a) network-form ([forms : (VariableMapping UpdateFunctionForm)]
|
|
[domains : (DomainMapping a)])
|
|
#:transparent
|
|
#:type-name NetworkForm)
|
|
|
|
(: update-function-form->update-function/any (-> UpdateFunctionForm (UpdateFunction Any)))
|
|
(define (update-function-form->update-function/any form)
|
|
(λ (s) (eval1-with s form)))
|
|
|
|
(module+ test
|
|
(test-case "update-function-form->update-function/any"
|
|
(define s (hash 'x #t 'y #f))
|
|
(define f (update-function-form->update-function/any '(and x y)))
|
|
(check-equal? (f s) #f)))
|
|
|
|
(: update-function-form->update-function/boolean (-> UpdateFunctionForm (UpdateFunction Boolean)))
|
|
(define (update-function-form->update-function/boolean form)
|
|
(λ (s) (assert-type (eval1-with s form) Boolean)))
|
|
|
|
(module+ test
|
|
(test-case "update-function-form->update-function/boolean"
|
|
(define s (hash 'x #t 'y #f))
|
|
(define f (update-function-form->update-function/boolean '(and x y)))
|
|
(check-equal? (f s) #f)))
|
|
|
|
(: update-function-form->update-function/01 (-> UpdateFunctionForm (UpdateFunction (U Zero One))))
|
|
(define (update-function-form->update-function/01 form)
|
|
(λ (s) (assert-type (eval1-with s form) (U Zero One))))
|
|
|
|
(module+ test
|
|
(test-case "update-function-form->update-function/01"
|
|
(define s (hash 'x 0 'y 1))
|
|
(define f (update-function-form->update-function/01 '(max x y)))
|
|
(check-equal? (f s) 1)))
|
|
|
|
(: network-form->network/any (-> (NetworkForm Any) (Network Any)))
|
|
(define (network-form->network/any nf)
|
|
(network
|
|
(for/hash ([(x form) (in-hash (network-form-forms nf))])
|
|
: (VariableMapping (UpdateFunction Any))
|
|
(values x (update-function-form->update-function/any form)))
|
|
(network-form-domains nf)))
|
|
|
|
(module+ test
|
|
(test-case "network-form->network/any"
|
|
(define bn (network-form->network/any
|
|
(network-form (hash 'a '(and a b)
|
|
'b '(not b))
|
|
(hash 'a '(#f #t)
|
|
'b '(#f #t)))))
|
|
(define s (hash 'a #t 'b #t))
|
|
(check-equal? ((hash-ref (network-functions bn) 'a) s) #t)))
|
|
|
|
(: network-form->network/boolean (-> (NetworkForm Boolean) (Network Boolean)))
|
|
(define (network-form->network/boolean nf)
|
|
(network
|
|
(for/hash ([(x form) (in-hash (network-form-forms nf))])
|
|
: (VariableMapping (UpdateFunction Boolean))
|
|
(values x (update-function-form->update-function/boolean form)))
|
|
(network-form-domains nf)))
|
|
|
|
(module+ test
|
|
(test-case "network-form->network/boolean"
|
|
(define bn (network-form->network/boolean
|
|
(network-form (hash 'a '(and a b)
|
|
'b '(not b))
|
|
(hash 'a '(#f #t)
|
|
'b '(#f #t)))))
|
|
(define s (hash 'a #t 'b #t))
|
|
(check-equal? ((hash-ref (network-functions bn) 'a) s) #t)))
|
|
|
|
(: network-form->network/01 (-> (NetworkForm (U Zero One)) (Network (U Zero One))))
|
|
(define (network-form->network/01 nf)
|
|
(network
|
|
(for/hash ([(x form) (in-hash (network-form-forms nf))])
|
|
: (VariableMapping (UpdateFunction (U Zero One)))
|
|
(values x (update-function-form->update-function/01 form)))
|
|
(network-form-domains nf)))
|
|
|
|
(module+ test
|
|
(test-case "network-form->network/01"
|
|
(define bn (network-form->network/01
|
|
(network-form (hash 'a '(min a b)
|
|
'b '(- 1 b))
|
|
(hash 'a '(0 1)
|
|
'b '(0 1)))))
|
|
(define s (hash 'a 1 'b 1))
|
|
(check-equal? ((hash-ref (network-functions bn) 'a) s) 1)))
|
|
|
|
(: make-boolean-network-form (-> (VariableMapping UpdateFunctionForm)
|
|
(NetworkForm Boolean)))
|
|
(define (make-boolean-network-form forms)
|
|
(network-form forms (make-boolean-domains (hash-keys forms))))
|
|
|
|
(module+ test
|
|
(test-case "make-boolean-network-form"
|
|
(check-equal? (make-boolean-network-form (hash 'a '(and a b)
|
|
'b '(not b)))
|
|
(network-form
|
|
'#hash((a . (and a b)) (b . (not b)))
|
|
'#hash((a . (#f #t)) (b . (#f #t)))))))
|
|
|
|
(: forms->boolean-network (-> (VariableMapping UpdateFunctionForm)
|
|
(Network Boolean)))
|
|
(define forms->boolean-network
|
|
(compose network-form->network/boolean make-boolean-network-form))
|
|
|
|
(module+ test
|
|
(test-case "forms->boolean-network"
|
|
(define n (forms->boolean-network (hash 'a '(and a b)
|
|
'b '(not b))))
|
|
(check-equal? (network-domains n) (hash 'a '(#f #t)
|
|
'b '(#f #t)))))
|
|
|
|
(: build-all-states (All (a) (-> (DomainMapping a) (Listof (State a)))))
|
|
(define (build-all-states vars-domains)
|
|
;; TODO: Use hash-keys and hash-values when Typed Racket will have
|
|
;; caught up with the new argument try-order?.
|
|
(define vdlist (hash-map vars-domains (inst cons Variable (Domain a)) #t))
|
|
(define vars (map (inst car Variable (Domain a)) vdlist))
|
|
(define doms (map (inst cdr Variable (Domain a)) vdlist))
|
|
(for/list ([s (apply cartesian-product doms)])
|
|
(make-immutable-hash (map (inst cons Variable a) vars s))))
|
|
|
|
(module+ test
|
|
(test-case "build-all-states"
|
|
(check-equal? (build-all-states #hash((a . (#t #f)) (b . (1 2 3))))
|
|
'(#hash((a . #t) (b . 1))
|
|
#hash((a . #t) (b . 2))
|
|
#hash((a . #t) (b . 3))
|
|
#hash((a . #f) (b . 1))
|
|
#hash((a . #f) (b . 2))
|
|
#hash((a . #f) (b . 3))))))
|
|
|
|
(: build-all-boolean-states (-> (Listof Variable) (Listof (State Boolean))))
|
|
(define (build-all-boolean-states vars)
|
|
(build-all-states (make-boolean-domains vars)))
|
|
|
|
(module+ test
|
|
(test-case "build-all-boolean-states"
|
|
(check-equal? (build-all-boolean-states '(a b))
|
|
'(#hash((a . #f) (b . #f))
|
|
#hash((a . #f) (b . #t))
|
|
#hash((a . #t) (b . #f))
|
|
#hash((a . #t) (b . #t))))))
|
|
|
|
(: build-all-01-states (-> (Listof Variable) (Listof (State (U Zero One)))))
|
|
(define (build-all-01-states vars)
|
|
(build-all-states (make-01-domains vars)))
|
|
|
|
(module+ test
|
|
(test-case "build-all-01-states"
|
|
(check-equal? (build-all-01-states '(a b))
|
|
'(#hash((a . 0) (b . 0))
|
|
#hash((a . 0) (b . 1))
|
|
#hash((a . 1) (b . 0))
|
|
#hash((a . 1) (b . 1))))))
|
|
|
|
(: list-syntactic-interactions
|
|
(All (a) (-> (NetworkForm a) Variable (Listof Variable))))
|
|
(define (list-syntactic-interactions nf x)
|
|
(set-intersect
|
|
(extract-symbols (hash-ref (network-form-forms nf) x))
|
|
(hash-keys (network-form-forms nf))))
|
|
|
|
(module+ test
|
|
(test-case "list-syntactic-interactions"
|
|
(define n (make-boolean-network-form #hash((a . (+ a b c))
|
|
(b . (- b c)))))
|
|
(check-true (set=? (list-syntactic-interactions n 'a) '(a b)))
|
|
(check-true (set=? (list-syntactic-interactions n 'b) '(b)))))
|
|
|
|
(: build-syntactic-interaction-graph (All (a) (-> (NetworkForm a) Graph)))
|
|
(define (build-syntactic-interaction-graph n)
|
|
(transpose
|
|
(unweighted-graph/adj
|
|
(for/list ([(var _) (in-hash (network-form-forms n))])
|
|
(cons var (list-syntactic-interactions n var))))))
|
|
|
|
(module+ test
|
|
(test-case "build-syntactic-interaction-graph"
|
|
(define n (make-boolean-network-form #hash((a . (+ a b c))
|
|
(b . (- b c)))))
|
|
(define ig (build-syntactic-interaction-graph n))
|
|
(check-true (has-vertex? ig 'a))
|
|
(check-true (has-vertex? ig 'b))
|
|
(check-false (has-vertex? ig 'c))
|
|
(check-true (has-edge? ig 'a 'a))
|
|
(check-true (has-edge? ig 'b 'a))
|
|
(check-true (has-edge? ig 'b 'b))
|
|
(check-false (has-edge? ig 'c 'b))
|
|
(check-false (has-edge? ig 'c 'a))))
|
|
|
|
(: interaction? (All (a) (-> (Network a) Variable Variable Boolean)))
|
|
(define (interaction? network x y)
|
|
(define doms (network-domains network))
|
|
(define states-not-x (build-all-states (hash-remove doms x)))
|
|
(define dom-x (hash-ref doms x))
|
|
(define y-func (hash-ref (network-functions network) y))
|
|
(: different-ys-exist? (-> (State a) Boolean))
|
|
(define (different-ys-exist? st)
|
|
(define x-states (for/list ([x-val (in-list dom-x)])
|
|
: (Listof (State a))
|
|
(hash-set st x x-val)))
|
|
;; TODO: Replace with for*/first when/if it is fixed.
|
|
(for*/first/typed : (Option Boolean)
|
|
([st1 : (State a) x-states]
|
|
[st2 : (State a) x-states]
|
|
#:unless (equal? (hash-ref st1 x) (hash-ref st2 x))
|
|
#:unless (equal? (y-func st1) (y-func st2)))
|
|
#t))
|
|
;; TODO: Replace with for/first when/if it is fixed.
|
|
(for/first/typed : (Option Boolean)
|
|
([st (in-list states-not-x)]
|
|
#:when (different-ys-exist? st))
|
|
#t))
|
|
|
|
(module+ test
|
|
(test-case "interaction?"
|
|
(define n1 (forms->boolean-network
|
|
(hash 'x '(not y)
|
|
'y 'x
|
|
'z '(and y z))))
|
|
(check-true (interaction? n1 'x 'y))
|
|
(check-true (interaction? n1 'y 'x))
|
|
(check-false (interaction? n1 'x 'z))
|
|
(define n-multi (hash 'x '(max (+ y 1) 2)
|
|
'y '(min (- y 1) 0)))
|
|
(define 123-doms (make-same-domains '(x y) '(0 1 2)))
|
|
(define n2 (network-form->network/any (network-form n-multi 123-doms)))
|
|
(check-false (interaction? n2 'x 'y))
|
|
(check-true (interaction? n2 'y 'x))))
|
|
|
|
(: get-interaction-sign (All (a) (-> (Network a) Variable Variable (Option Integer))))
|
|
(define (get-interaction-sign network x y)
|
|
(define doms (network-domains network))
|
|
(define dom-x (hash-ref doms x))
|
|
(define dom-y (hash-ref doms y))
|
|
(define y-func (hash-ref (network-functions network) y))
|
|
(define (collect-impacts-on-y [st : (State a)])
|
|
;; The way in which the values are ordered in the domains gives
|
|
;; a total order on these values. This means that considering
|
|
;; pairs of consecutive values of x is sufficient for testing the
|
|
;; sign of the interaction.
|
|
(define x-states (for/list : (Listof (State a))
|
|
([x-val (in-list dom-x)])
|
|
(hash-set st x x-val)))
|
|
(for/list : (Listof (U '< '> '=))
|
|
([st1 (in-list x-states)]
|
|
[st2 (in-list (cdr x-states))])
|
|
(define y1-idx (assert-type (index-of dom-y (y-func st1)) Index))
|
|
(define y2-idx (assert-type (index-of dom-y (y-func st2)) Index))
|
|
(cond
|
|
[(< y1-idx y2-idx) '<]
|
|
[(> y1-idx y2-idx) '>]
|
|
[else '=])))
|
|
(define states-not-x (build-all-states (hash-remove doms x)))
|
|
(define interactions
|
|
(remove-duplicates
|
|
(for/list : (Listof (U '< '> '= Zero))
|
|
([st (in-list states-not-x)])
|
|
(define impacts (remove-duplicates (collect-impacts-on-y st)))
|
|
(cond
|
|
[(and (member '< impacts) (not (member '> impacts))) '<]
|
|
[(and (member '> impacts) (not (member '< impacts))) '>]
|
|
[(equal? impacts '(=)) '=]
|
|
[else 0]))))
|
|
(cond
|
|
[(and (member '< interactions) (not (member '> interactions))) 1]
|
|
[(and (member '> interactions) (not (member '< interactions))) -1]
|
|
[(equal? interactions '(=)) #f]
|
|
[else 0]))
|
|
|
|
(module+ test
|
|
(test-case "get-interaction-sign"
|
|
(define n1 (forms->boolean-network
|
|
(hash 'x '(not y)
|
|
'y 'x
|
|
'z '(and y z)
|
|
't '(or (and (not x) y)
|
|
(and x (not y))))))
|
|
(check-equal? (get-interaction-sign n1 'x 'y) 1)
|
|
(check-equal? (get-interaction-sign n1 'y 'x) -1)
|
|
(check-false (get-interaction-sign n1 'x 'z))
|
|
(check-equal? (get-interaction-sign n1 'y 'z) 1)
|
|
(check-equal? (get-interaction-sign n1 'x 't) 0)
|
|
(define n-multi (hash 'x '(min (+ y 1) 2)
|
|
'y '(max (- y 1) 0)
|
|
'z '(- 2 y)
|
|
't '(abs (- y 1))))
|
|
(define 123-doms (make-same-domains '(x y z t) '(0 1 2)))
|
|
(define n2 (network-form->network/any (network-form n-multi 123-doms)))
|
|
(check-false (get-interaction-sign n2 'x 'y))
|
|
(check-equal? (get-interaction-sign n2 'y 'x) 1)
|
|
(check-equal? (get-interaction-sign n2 'y 'z) -1)
|
|
(check-equal? (get-interaction-sign n2 'y 't) 0)
|
|
(check-equal? (get-interaction-sign n2 'y 'y) 1)))
|
|
|
|
(: build-interaction-graph (All (a) (-> (Network a) Graph)))
|
|
(define (build-interaction-graph network)
|
|
(define vars (hash-keys (network-functions network)))
|
|
(unweighted-graph/directed
|
|
(for*/list : (Listof (List Any Any))
|
|
([x (in-list vars)]
|
|
[y (in-list vars)]
|
|
#:when (interaction? network x y))
|
|
(list x y))))
|
|
|
|
(: build-interaction-graph/form (All (a) (-> (NetworkForm a) Graph)))
|
|
(define (build-interaction-graph/form form)
|
|
(build-interaction-graph (network-form->network/any form)))
|
|
|
|
(module+ test
|
|
(test-case "build-interaction-graph"
|
|
(cond
|
|
[skip-expensive-tests?
|
|
(displayln "Skipping test case build-interaction-graph.")]
|
|
[else
|
|
(define n1 (make-boolean-network-form
|
|
(hash 'x '(not y)
|
|
'y 'x
|
|
'z '(and y z)
|
|
't '(or (and (not x) y)
|
|
(and x (not y))))))
|
|
(check-equal? (graphviz (build-interaction-graph/form n1))
|
|
"digraph G {\n\tnode0 [label=\"y\"];\n\tnode1 [label=\"t\"];\n\tnode2 [label=\"z\"];\n\tnode3 [label=\"x\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t\tnode0 -> node3 [];\n\t\tnode2 -> node2 [];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [];\n\t\tnode0 -> node2 [];\n\t\tnode3 -> node1 [];\n\t}\n}\n")
|
|
(define n-multi (hash 'x '(min (+ y 1) 2)
|
|
'y '(max (- y 1) 0)
|
|
'z '(- 2 y)
|
|
't '(abs (- y 1))))
|
|
(define 123-doms (make-same-domains '(x y z t) '(0 1 2)))
|
|
(define n2 (network-form n-multi 123-doms))
|
|
(check-equal? (graphviz (build-interaction-graph/form n2))
|
|
"digraph G {\n\tnode0 [label=\"y\"];\n\tnode1 [label=\"t\"];\n\tnode2 [label=\"z\"];\n\tnode3 [label=\"x\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t\tnode0 -> node0 [];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [];\n\t\tnode0 -> node2 [];\n\t\tnode0 -> node3 [];\n\t}\n}\n")])))
|
|
|
|
(: build-signed-interaction-graph (All (a) (-> (Network a) Graph)))
|
|
(define (build-signed-interaction-graph network)
|
|
(define vars (hash-keys (network-functions network)))
|
|
(weighted-graph/directed
|
|
(for*/list : (Listof (List Integer Any Any))
|
|
([x (in-list vars)]
|
|
[y (in-list vars)]
|
|
[sign (in-value (get-interaction-sign network x y))]
|
|
#:unless (eq? sign #f))
|
|
(list sign x y))))
|
|
|
|
(: build-signed-interaction-graph/form (All (a) (-> (NetworkForm a) Graph)))
|
|
(define (build-signed-interaction-graph/form nf)
|
|
(build-signed-interaction-graph (network-form->network/any nf)))
|
|
|
|
(module+ test
|
|
(test-case "build-signed-interaction-graph"
|
|
(cond
|
|
[skip-expensive-tests?
|
|
(displayln "Skipping test case build-signed-interaction-graph.")]
|
|
[else
|
|
(define n1 (make-boolean-network-form
|
|
(hash 'x '(not y)
|
|
'y 'x
|
|
'z '(and y z)
|
|
't '(or (and (not x) y)
|
|
(and x (not y))))))
|
|
(check-equal? (graphviz (build-signed-interaction-graph/form n1))
|
|
"digraph G {\n\tnode0 [label=\"y\"];\n\tnode1 [label=\"t\"];\n\tnode2 [label=\"z\"];\n\tnode3 [label=\"x\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t\tnode2 -> node2 [label=\"1\"];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [label=\"0\"];\n\t\tnode0 -> node2 [label=\"1\"];\n\t\tnode0 -> node3 [label=\"-1\"];\n\t\tnode3 -> node1 [label=\"0\"];\n\t\tnode3 -> node0 [label=\"1\"];\n\t}\n}\n")
|
|
(define n-multi (hash 'x '(min (+ y 1) 2)
|
|
'y '(max (- y 1) 0)
|
|
'z '(- 2 y)
|
|
't '(abs (- y 1))))
|
|
(define 123-doms (make-same-domains '(x y z t) '(0 1 2)))
|
|
(define n2 (network-form n-multi 123-doms))
|
|
(check-equal? (graphviz (build-signed-interaction-graph/form n2))
|
|
"digraph G {\n\tnode0 [label=\"y\"];\n\tnode1 [label=\"t\"];\n\tnode2 [label=\"z\"];\n\tnode3 [label=\"x\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t\tnode0 -> node0 [label=\"1\"];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [label=\"0\"];\n\t\tnode0 -> node2 [label=\"-1\"];\n\t\tnode0 -> node3 [label=\"1\"];\n\t}\n}\n")])))
|
|
|
|
(define-type Modality (Listof Variable))
|
|
(define-type Mode (Listof Modality))
|
|
|
|
(define dynamics%
|
|
;; TODO: Fix the parameter of State when Typed Racket supports
|
|
;; passing type parameters to the parent.
|
|
(class (inst dds% (State Any) Modality)
|
|
#:forall (a)
|
|
(super-new)
|
|
|
|
(init-field [network : (Network a) network]
|
|
[mode : Mode mode])
|
|
|
|
(: step/annotated (-> (State a) (Listof (Pairof Modality (State a)))))
|
|
(define/override (step/annotated s)
|
|
(for/list ([m mode])
|
|
(cons m (update network s m))))))
|
|
|
|
;; TODO: Find a better way to define the type of the class
|
|
;; dynamics%.
|
|
;;
|
|
;; TODO: Fix the parameter of State when Typed Racket supports
|
|
;; passing type parameters to the parent.
|
|
;;
|
|
;; NOTE: The type appearing when you type dynamics% in the REPL does
|
|
;; not directly type check, probably because of the structure types
|
|
;; which are fully expanded in the REPL.
|
|
(define-type (Dynamics% a)
|
|
(Instance (Class
|
|
(init (network (Network a) #:optional)
|
|
(mode Mode #:optional))
|
|
(field (network (Network a))
|
|
(mode Mode))
|
|
(step (-> (State Any) (Listof (State Any))))
|
|
(step/annotated (-> (State a) (Listof (Pairof Modality (State a)))))
|
|
(step* (-> (Listof (State Any)) (Listof (State Any))))
|
|
(build-state-graph (-> (Listof (State Any)) Graph))
|
|
(build-state-graph/annotated (-> (Listof (State Any)) Graph))
|
|
(build-state-graph* (-> (Listof (State Any)) (U Positive-Integer 'full) Graph))
|
|
(build-state-graph*/annotated (-> (Listof (State Any)) (U Positive-Integer 'full) Graph)))))
|
|
|
|
(module+ test
|
|
(let* ([n1 : (Network Boolean)
|
|
(forms->boolean-network (hash 'x '(not y)
|
|
'y 'x
|
|
'z '(and y z)))]
|
|
[syn : Mode '((x y z))]
|
|
[asyn : Mode '((x) (y) (z))]
|
|
[dyn-syn (new (inst dynamics% Boolean) [network n1] [mode syn])]
|
|
[dyn-asyn (new (inst dynamics% Boolean) [network n1] [mode asyn])]
|
|
[s1 (hash 'x #f 'y #f 'z #f)]
|
|
[s2 (hash 'x #t 'y #t 'z #t)])
|
|
(test-case "dynamics%"
|
|
(check-equal? (send dyn-syn step/annotated s1)
|
|
'(((x y z) . #hash((x . #t) (y . #f) (z . #f)))))
|
|
(check-equal? (send dyn-asyn step/annotated s1)
|
|
'(((x) . #hash((x . #t) (y . #f) (z . #f)))
|
|
((y) . #hash((x . #f) (y . #f) (z . #f)))
|
|
((z) . #hash((x . #f) (y . #f) (z . #f))))))
|
|
|
|
(test-case "dynamics%:step"
|
|
(check-equal? (send dyn-syn step s1)
|
|
'(#hash((x . #t) (y . #f) (z . #f))))
|
|
(check-equal? (send dyn-asyn step s1)
|
|
'(#hash((x . #t) (y . #f) (z . #f))
|
|
#hash((x . #f) (y . #f) (z . #f))
|
|
#hash((x . #f) (y . #f) (z . #f)))))
|
|
(test-case "dynamics%:step*"
|
|
(check-equal? (list->set (send dyn-syn step* (list s1 s2)))
|
|
(list->set (append (send dyn-syn step s1)
|
|
(send dyn-syn step s2))))
|
|
(check-equal? (list->set (send dyn-asyn step* (list s1 s2)))
|
|
(list->set (append (send dyn-asyn step s1)
|
|
(send dyn-asyn step s2)))))
|
|
(test-case "dynamics%:build-state-graph*/annotated"
|
|
(check-equal? (graphviz (send dyn-syn build-state-graph*/annotated (list s1) 2))
|
|
"digraph G {\n\tnode0 [label=\"'#hash((x . #t) (y . #f) (z . #f))\"];\n\tnode1 [label=\"'#hash((x . #t) (y . #t) (z . #f))\"];\n\tnode2 [label=\"'#hash((x . #f) (y . #f) (z . #f))\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [label=\"'(x y z)\"];\n\t\tnode2 -> node0 [label=\"'(x y z)\"];\n\t}\n}\n")
|
|
(check-equal? (graphviz (send dyn-syn build-state-graph*/annotated (list s1) 'full))
|
|
"digraph G {\n\tnode0 [label=\"'#hash((x . #f) (y . #t) (z . #f))\"];\n\tnode1 [label=\"'#hash((x . #t) (y . #f) (z . #f))\"];\n\tnode2 [label=\"'#hash((x . #t) (y . #t) (z . #f))\"];\n\tnode3 [label=\"'#hash((x . #f) (y . #f) (z . #f))\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node3 [label=\"'(x y z)\"];\n\t\tnode1 -> node2 [label=\"'(x y z)\"];\n\t\tnode2 -> node0 [label=\"'(x y z)\"];\n\t\tnode3 -> node1 [label=\"'(x y z)\"];\n\t}\n}\n"))
|
|
(test-case "dynamics%:build-state-graph*"
|
|
(check-equal? (graphviz (send dyn-syn build-state-graph* (list s1) 2))
|
|
"digraph G {\n\tnode0 [label=\"'#hash((x . #t) (y . #f) (z . #f))\"];\n\tnode1 [label=\"'#hash((x . #t) (y . #t) (z . #f))\"];\n\tnode2 [label=\"'#hash((x . #f) (y . #f) (z . #f))\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [];\n\t\tnode2 -> node0 [];\n\t}\n}\n")
|
|
(check-equal? (graphviz (send dyn-syn build-state-graph* (list s1) 'full))
|
|
"digraph G {\n\tnode0 [label=\"'#hash((x . #t) (y . #f) (z . #f))\"];\n\tnode1 [label=\"'#hash((x . #f) (y . #t) (z . #f))\"];\n\tnode2 [label=\"'#hash((x . #t) (y . #t) (z . #f))\"];\n\tnode3 [label=\"'#hash((x . #f) (y . #f) (z . #f))\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node2 [];\n\t\tnode1 -> node3 [];\n\t\tnode2 -> node1 [];\n\t\tnode3 -> node0 [];\n\t}\n}\n"))
|
|
(test-case "dynamics%:build-state-graph/annotated"
|
|
(check-equal? (graphviz (send dyn-syn build-state-graph/annotated (list s1)))
|
|
(graphviz (send dyn-syn build-state-graph*/annotated (list s1) 'full))))
|
|
(test-case "dynamics%:build-state-graph"
|
|
(check-equal? (graphviz (send dyn-syn build-state-graph (list s1)))
|
|
(graphviz (send dyn-syn build-state-graph* (list s1) 'full))))))
|
|
|
|
(: make-asyn (-> (Listof Variable) Mode))
|
|
(define (make-asyn vars) (map (inst list Variable) vars))
|
|
|
|
(module+ test
|
|
(test-case "make-asyn"
|
|
(check-equal? (make-asyn '(x y z)) '((x) (y) (z)))))
|
|
|
|
(: make-syn (-> (Listof Variable) Mode))
|
|
(define (make-syn vars) (list vars))
|
|
|
|
(module+ test
|
|
(test-case "make-syn"
|
|
(check-equal? (make-syn '(x y z)) '((x y z)))))
|
|
|
|
;;; Given a network, applies a function for building a mode to its
|
|
;;; variables and returns the corresponding network dynamics.
|
|
(: make-dynamics-from-mode
|
|
(All (a) (-> (Network a) (-> (Listof Variable) Mode) (Dynamics% a))))
|
|
(define (make-dynamics-from-mode n make-mode)
|
|
(new (inst dynamics% a)
|
|
[network n]
|
|
[mode (make-mode (hash-keys (network-functions n)))]))
|
|
|
|
(: make-asyn-dynamics (All (a) (-> (Network a) (Dynamics% a))))
|
|
(define (make-asyn-dynamics [n : (Network a)])
|
|
((inst make-dynamics-from-mode a) n make-asyn))
|
|
|
|
(module+ test
|
|
(test-case "make-asyn-dynamics"
|
|
(define n : (Network Boolean)
|
|
(forms->boolean-network (hash 'x '(not y)
|
|
'y 'x
|
|
'z '(and y z))))
|
|
(define asyn-dyn (make-asyn-dynamics n))
|
|
(check-equal? (get-field network asyn-dyn) n)
|
|
(check-true (set=? (get-field mode asyn-dyn) '((x) (y) (z))))))
|
|
|
|
(: make-syn-dynamics (All (a) (-> (Network a) (Dynamics% a))))
|
|
(define (make-syn-dynamics [n : (Network a)])
|
|
((inst make-dynamics-from-mode a) n make-syn))
|
|
|
|
(module+ test
|
|
(test-case "make-syn-dynamics"
|
|
(define n : (Network Boolean)
|
|
(forms->boolean-network (hash 'x '(not y)
|
|
'y 'x
|
|
'z '(and y z))))
|
|
(define syn-dyn (make-syn-dynamics n))
|
|
(check-equal? (get-field network syn-dyn) n)
|
|
(define m (get-field mode syn-dyn))
|
|
(check-equal? (length m) 1)
|
|
(check-true (set=? (car m) '(x y z)))))
|
|
|
|
(: pretty-print-state (All (a) (-> (State a) String)))
|
|
(define (pretty-print-state s)
|
|
(string-join (hash-map s (λ (key val) (format "~a:~a" key val)) #t)))
|
|
|
|
(module+ test
|
|
(test-case "pretty-print-state"
|
|
(check-equal? (pretty-print-state (hash 'a #f 'b 3 'c 4))
|
|
"a:#f b:3 c:4")))
|
|
|
|
(: pretty-print-state/01 (All (a) (-> (State a) String)))
|
|
(define (pretty-print-state/01 s)
|
|
(string-join (hash-map s (λ (key val) (format "~a:~a" key (any->01 val))) #t)))
|
|
|
|
(module+ test
|
|
(test-case "pretty-print-state/01"
|
|
(check-equal?
|
|
(pretty-print-state/01 (hash 'a #f 'b #t 'c #t))
|
|
"a:0 b:1 c:1")))
|
|
|
|
(: pretty-print-state-graph-with (-> Graph (-> Any Any) Graph))
|
|
(define (pretty-print-state-graph-with gr pprinter)
|
|
(update-graph
|
|
gr
|
|
#:v-func pprinter
|
|
#:e-func (relax-arg-type/any pretty-print-set-sets (Setof (Setof Any)))))
|
|
|
|
(: pretty-print-state-graph (-> Graph Graph))
|
|
(define (pretty-print-state-graph gr)
|
|
(define (pprinter/any [x : Any])
|
|
(pretty-print-state (assert-type x (State Any))))
|
|
(pretty-print-state-graph-with
|
|
gr
|
|
(relax-arg-type/any pretty-print-state (State Any))))
|
|
|
|
(define ppsg pretty-print-state-graph)
|
|
|
|
(: pretty-print-state-graph/01 (-> Graph Graph))
|
|
(define (pretty-print-state-graph/01 gr)
|
|
(define (pprinter/any [x : Any])
|
|
(pretty-print-state/01 (assert-type x (State Any))))
|
|
(pretty-print-state-graph-with gr pprinter/any))
|
|
|
|
(define ppsg01 pretty-print-state-graph/01)
|
|
|
|
(: build-full-state-graph (All (a) (-> (Dynamics% a) Graph)))
|
|
(define (build-full-state-graph dyn)
|
|
(send dyn
|
|
build-state-graph
|
|
(build-all-states (network-domains (get-field network dyn)))))
|
|
|
|
(: build-full-state-graph/annotated (All (a) (-> (Dynamics% a) Graph)))
|
|
(define (build-full-state-graph/annotated dyn)
|
|
(send dyn
|
|
build-state-graph/annotated
|
|
(build-all-states (network-domains (get-field network dyn)))))
|
|
|
|
(module+ test
|
|
(let* ([n1 : (Network Boolean)
|
|
(forms->boolean-network (hash 'x '(not y)
|
|
'y 'x
|
|
'z '(and y z)))]
|
|
[dyn-syn (make-syn-dynamics n1)]
|
|
[sg ((inst build-full-state-graph Boolean) dyn-syn)]
|
|
[sg/an ((inst build-full-state-graph/annotated Boolean) dyn-syn)])
|
|
(test-case "build-full-state-graph"
|
|
(check-equal? (graphviz sg)
|
|
"digraph G {\n\tnode0 [label=\"'#hash((x . #f) (y . #t) (z . #t))\"];\n\tnode1 [label=\"'#hash((x . #f) (y . #f) (z . #t))\"];\n\tnode2 [label=\"'#hash((x . #f) (y . #t) (z . #f))\"];\n\tnode3 [label=\"'#hash((x . #t) (y . #f) (z . #f))\"];\n\tnode4 [label=\"'#hash((x . #t) (y . #f) (z . #t))\"];\n\tnode5 [label=\"'#hash((x . #t) (y . #t) (z . #f))\"];\n\tnode6 [label=\"'#hash((x . #t) (y . #t) (z . #t))\"];\n\tnode7 [label=\"'#hash((x . #f) (y . #f) (z . #f))\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [];\n\t\tnode1 -> node3 [];\n\t\tnode2 -> node7 [];\n\t\tnode3 -> node5 [];\n\t\tnode4 -> node5 [];\n\t\tnode5 -> node2 [];\n\t\tnode6 -> node0 [];\n\t\tnode7 -> node3 [];\n\t}\n}\n"))
|
|
(test-case "build-full-state-graph/annotated"
|
|
(check-equal? (graphviz sg/an)
|
|
"digraph G {\n\tnode0 [label=\"'#hash((x . #f) (y . #t) (z . #t))\"];\n\tnode1 [label=\"'#hash((x . #f) (y . #f) (z . #t))\"];\n\tnode2 [label=\"'#hash((x . #t) (y . #f) (z . #f))\"];\n\tnode3 [label=\"'#hash((x . #f) (y . #t) (z . #f))\"];\n\tnode4 [label=\"'#hash((x . #t) (y . #f) (z . #t))\"];\n\tnode5 [label=\"'#hash((x . #t) (y . #t) (z . #f))\"];\n\tnode6 [label=\"'#hash((x . #t) (y . #t) (z . #t))\"];\n\tnode7 [label=\"'#hash((x . #f) (y . #f) (z . #f))\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [label=\"'(z y x)\"];\n\t\tnode1 -> node2 [label=\"'(z y x)\"];\n\t\tnode2 -> node5 [label=\"'(z y x)\"];\n\t\tnode3 -> node7 [label=\"'(z y x)\"];\n\t\tnode4 -> node5 [label=\"'(z y x)\"];\n\t\tnode5 -> node3 [label=\"'(z y x)\"];\n\t\tnode6 -> node0 [label=\"'(z y x)\"];\n\t\tnode7 -> node2 [label=\"'(z y x)\"];\n\t}\n}\n"))
|
|
|
|
(test-case "pretty-print-state-graph, pretty-print-state-graph/boolean"
|
|
(check-equal? (graphviz (ppsg sg))
|
|
"digraph G {\n\tnode0 [label=\"x:#f y:#t z:#f\"];\n\tnode1 [label=\"x:#f y:#t z:#t\"];\n\tnode2 [label=\"x:#t y:#t z:#t\"];\n\tnode3 [label=\"x:#t y:#f z:#t\"];\n\tnode4 [label=\"x:#t y:#t z:#f\"];\n\tnode5 [label=\"x:#t y:#f z:#f\"];\n\tnode6 [label=\"x:#f y:#f z:#f\"];\n\tnode7 [label=\"x:#f y:#f z:#t\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node6 [];\n\t\tnode1 -> node7 [];\n\t\tnode2 -> node1 [];\n\t\tnode3 -> node4 [];\n\t\tnode4 -> node0 [];\n\t\tnode5 -> node4 [];\n\t\tnode6 -> node5 [];\n\t\tnode7 -> node5 [];\n\t}\n}\n")
|
|
(check-equal? (graphviz (ppsg01 sg))
|
|
"digraph G {\n\tnode0 [label=\"x:1 y:0 z:0\"];\n\tnode1 [label=\"x:0 y:1 z:1\"];\n\tnode2 [label=\"x:1 y:1 z:1\"];\n\tnode3 [label=\"x:0 y:0 z:1\"];\n\tnode4 [label=\"x:1 y:0 z:1\"];\n\tnode5 [label=\"x:0 y:1 z:0\"];\n\tnode6 [label=\"x:1 y:1 z:0\"];\n\tnode7 [label=\"x:0 y:0 z:0\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node6 [];\n\t\tnode1 -> node3 [];\n\t\tnode2 -> node1 [];\n\t\tnode3 -> node0 [];\n\t\tnode4 -> node6 [];\n\t\tnode5 -> node7 [];\n\t\tnode6 -> node5 [];\n\t\tnode7 -> node0 [];\n\t}\n}\n"))))
|
|
|
|
(: tabulate-state* (All (a) (-> (Listof (-> (State a) a)) (DomainMapping a)
|
|
(Listof (Listof a)))))
|
|
(define (tabulate-state* funcs domains)
|
|
(for/list : (Listof (Listof a)) ([s (in-list (build-all-states domains))])
|
|
(append (hash-map s (λ ([x : Variable] [y : a]) y) #t)
|
|
(for/list : (Listof a) ([f (in-list funcs)]) (f s)))))
|
|
|
|
(module+ test
|
|
(test-case "tabulate-state*"
|
|
(define/: f1 (State Integer) (+ :a :b))
|
|
(define/: f2 (State Integer) (- :a :b))
|
|
(check-equal? (tabulate-state* (list f1 f2) (hash 'a '(1 2) 'b '(2 3)))
|
|
'((1 2 3 -1)
|
|
(1 3 4 -2)
|
|
(2 2 4 0)
|
|
(2 3 5 -1)))))
|
|
|
|
(: tabulate-state*+headers
|
|
(All (a) (-> (Listof (-> (State a) a)) (DomainMapping a)
|
|
(Pairof (Listof Symbol) (Listof (Listof a))))))
|
|
(define (tabulate-state*+headers funcs domains)
|
|
(define var-names : (Listof Symbol)
|
|
(hash-map domains (λ ([x : Symbol] _) x) #t))
|
|
(define func-names : (Listof Symbol)
|
|
(for/list ([_ funcs]
|
|
[i (in-naturals 1)])
|
|
(string->symbol (~a 'f i))))
|
|
(cons (append var-names func-names)
|
|
(tabulate-state* funcs domains)))
|
|
|
|
(module+ test
|
|
(test-case "tabulate-state*+headers"
|
|
(define/: f1 (State Integer) (+ :a :b))
|
|
(define/: f2 (State Integer) (- :a :b))
|
|
(check-equal?
|
|
(tabulate-state*+headers (list f1 f2) (hash 'a '(1 2) 'b '(2 3)))
|
|
'((a b f1 f2)
|
|
(1 2 3 -1)
|
|
(1 3 4 -2)
|
|
(2 2 4 0)
|
|
(2 3 5 -1)))))
|
|
|
|
(: tabulate-state*/boolean
|
|
(-> (Listof (-> (State Boolean) Boolean)) (Listof Variable)
|
|
(Listof (Listof Boolean))))
|
|
(define (tabulate-state*/boolean funcs args)
|
|
(tabulate-state* funcs (make-boolean-domains args)))
|
|
|
|
(module+ test
|
|
(test-case "tabulate-state*/boolean"
|
|
(define/: f1 (State Boolean) (and :a :b))
|
|
(define/: f2 (State Boolean) (or :a :b))
|
|
(check-equal? (tabulate-state*/boolean (list f1 f2) '(a b))
|
|
'((#f #f #f #f)
|
|
(#f #t #f #t)
|
|
(#t #f #f #t)
|
|
(#t #t #t #t)))))
|
|
|
|
(: tabulate-state*+headers/boolean
|
|
(-> (Listof (-> (State Boolean) Boolean)) (Listof Variable)
|
|
(Pairof (Listof Symbol) (Listof (Listof Boolean)))))
|
|
(define (tabulate-state*+headers/boolean funcs args)
|
|
(tabulate-state*+headers funcs (make-boolean-domains args)))
|
|
|
|
(module+ test
|
|
(test-case "tabulate-state*+headers/boolean"
|
|
(define/: f1 (State Boolean) (and :a :b))
|
|
(define/: f2 (State Boolean) (or :a :b))
|
|
(check-equal? (tabulate-state*+headers/boolean (list f1 f2) '(a b))
|
|
'((a b f1 f2)
|
|
(#f #f #f #f)
|
|
(#f #t #f #t)
|
|
(#t #f #f #t)
|
|
(#t #t #t #t)))))
|
|
|
|
(define-syntax-parse-rule (make-tabulate-no-star name star-name)
|
|
(define (name func domains)
|
|
(star-name `(,func) domains)))
|
|
|
|
(: tabulate-state (All (a) (-> (-> (State a) a) (DomainMapping a)
|
|
(Listof (Listof a)))))
|
|
(make-tabulate-no-star tabulate-state tabulate-state*)
|
|
|
|
(module+ test
|
|
(test-case "tabulate-state"
|
|
(check-equal? (tabulate-state (λ/: (State Integer) (+ :a :b))
|
|
(hash 'a '(1 2) 'b '(2 3)))
|
|
'((1 2 3)
|
|
(1 3 4)
|
|
(2 2 4)
|
|
(2 3 5)))))
|
|
|
|
(: tabulate-state+headers (All (a) (-> (-> (State a) a) (DomainMapping a)
|
|
(Pairof (Listof Symbol)
|
|
(Listof (Listof a))))))
|
|
(make-tabulate-no-star tabulate-state+headers tabulate-state*+headers)
|
|
|
|
(module+ test
|
|
(test-case "tabulate-state+headers"
|
|
(check-equal? (tabulate-state+headers
|
|
(λ/: (State Integer) (+ :a :b))
|
|
(hash 'a '(1 2) 'b '(2 3)))
|
|
'((a b f1)
|
|
(1 2 3)
|
|
(1 3 4)
|
|
(2 2 4)
|
|
(2 3 5)))))
|
|
|
|
(: tabulate-state/boolean
|
|
(-> (-> (State Boolean) Boolean)
|
|
(Listof Variable)
|
|
(Listof (Listof Boolean))))
|
|
(make-tabulate-no-star tabulate-state/boolean tabulate-state*/boolean)
|
|
|
|
(module+ test
|
|
(test-case "tabulate-state/boolean"
|
|
(check-equal? (tabulate-state/boolean (λ/: (State Boolean) (and :a :b)) '(a b))
|
|
'((#f #f #f)
|
|
(#f #t #f)
|
|
(#t #f #f)
|
|
(#t #t #t)))))
|
|
|
|
(: tabulate-state+headers/boolean
|
|
(-> (-> (State Boolean) Boolean)
|
|
(Listof Variable)
|
|
(Pairof (Listof Symbol) (Listof (Listof Boolean)))))
|
|
(make-tabulate-no-star tabulate-state+headers/boolean tabulate-state*+headers/boolean)
|
|
|
|
(module+ test
|
|
(test-case "tabulate-state+headers/boolean"
|
|
(check-equal? (tabulate-state+headers/boolean
|
|
(λ/: (State Boolean) (and :a :b)) '(a b))
|
|
'((a b f1)
|
|
(#f #f #f)
|
|
(#f #t #f)
|
|
(#t #f #f)
|
|
(#t #t #t)))))
|
|
|
|
(: tabulate-network (All (a) (-> (Network a) (Listof (Listof a)))))
|
|
(define (tabulate-network network)
|
|
(define funcs (hash-map (network-functions network)
|
|
(λ (_ [fx : (UpdateFunction a)]) fx)
|
|
#t))
|
|
(tabulate-state* funcs (network-domains network)))
|
|
|
|
(module+ test
|
|
(test-case "tabulate-network"
|
|
(define bn (forms->boolean-network (hash 'a '(not a) 'b 'b)))
|
|
(check-equal? (tabulate-network bn)
|
|
'((#f #f #t #f)
|
|
(#f #t #t #t)
|
|
(#t #f #f #f)
|
|
(#t #t #f #t)))))
|
|
|
|
(: tabulate-network+headers (All (a) (-> (Network a)
|
|
(Pairof (Listof Symbol)
|
|
(Listof (Listof a))))))
|
|
(define (tabulate-network+headers network)
|
|
(define-values (vars funcs)
|
|
(for/lists ([l1 : (Listof Variable)]
|
|
[l2 : (Listof (UpdateFunction a))])
|
|
([p (hash-map (network-functions network)
|
|
(inst cons Variable (UpdateFunction a))
|
|
#t)])
|
|
(values (car p) (cdr p))))
|
|
|
|
(define fnames : (Listof Variable)
|
|
(for/list ([v vars]) (format-symbol "f-~a" v)))
|
|
|
|
(match (tabulate-state*+headers funcs (network-domains network))
|
|
[(list headers tab ...)
|
|
(cons (append (take headers (length fnames)) fnames)
|
|
tab)]))
|
|
|
|
(module+ test
|
|
(test-case "tabulate-network+headers"
|
|
(define bn (forms->boolean-network (hash 'a '(not a) 'b 'b)))
|
|
(check-equal? (tabulate-network+headers bn)
|
|
'((a b f-a f-b)
|
|
(#f #f #t #f)
|
|
(#f #t #t #t)
|
|
(#t #f #f #f)
|
|
(#t #t #f #t)))))
|
|
|
|
(: table+vars->network (All (a) (-> (Listof Variable) (Listof (Listof a))
|
|
(Network a))))
|
|
(define (table+vars->network var-names table)
|
|
(define n : Integer (quotient (length (car table)) 2))
|
|
;; Split the table into the inputs and the outputs of the functions.
|
|
(define-values (ins outs) (multi-split-at table n))
|
|
;; Transpose outs to have functions define by lines instead of by
|
|
;; columns.
|
|
(define func-lines : (Listof (Listof a)) (lists-transpose outs))
|
|
;; Make states out of inputs.
|
|
(define st-ins : (Listof (State a))
|
|
(for/list ([in ins]) (make-immutable-hash
|
|
(map (inst cons Variable a) var-names in))))
|
|
;; Construct the functions.
|
|
(define funcs : (Listof (UpdateFunction a))
|
|
(for/list ([out func-lines])
|
|
(table->unary-function
|
|
(for/list : (Listof (List (State a) a))
|
|
([in st-ins] [o out])
|
|
(list in o)))))
|
|
;; Infer the domains.
|
|
(define domains : (DomainMapping a)
|
|
(make-immutable-hash
|
|
(map (inst cons Variable (Domain a))
|
|
var-names
|
|
(map (inst remove-duplicates a) (lists-transpose ins)))))
|
|
;; Construct the network.
|
|
(network (make-immutable-hash
|
|
(map (inst cons Variable (UpdateFunction a))
|
|
var-names funcs))
|
|
domains))
|
|
|
|
(module+ test
|
|
(test-case "table+vars->network"
|
|
(define n (table+vars->network '(x1 x2)
|
|
'((#f #f #f #f)
|
|
(#f #t #f #t)
|
|
(#t #f #t #f)
|
|
(#t #t #t #t))))
|
|
(define f1 (hash-ref (network-functions n) 'x1))
|
|
(define f2 (hash-ref (network-functions n) 'x2))
|
|
|
|
(check-false (f1 (hash 'x1 #f 'x2 #f)))
|
|
(check-false (f1 (hash 'x1 #f 'x2 #t)))
|
|
(check-true (f1 (hash 'x1 #t 'x2 #f)))
|
|
(check-true (f1 (hash 'x1 #t 'x2 #t)))
|
|
|
|
(check-false (f2 (hash 'x1 #f 'x2 #f)))
|
|
(check-true (f2 (hash 'x1 #f 'x2 #t)))
|
|
(check-false (f2 (hash 'x1 #t 'x2 #f)))
|
|
(check-true (f2 (hash 'x1 #t 'x2 #t)))
|
|
|
|
(check-equal? (network-domains n)
|
|
#hash((x1 . (#f #t)) (x2 . (#f #t))))))
|
|
|
|
(: table->network (All (a) (-> (Listof (Listof a)) (Network a))))
|
|
(define (table->network table)
|
|
(define n : Integer (quotient (length (car table)) 2))
|
|
(define var-names : (Listof Variable)
|
|
(for/list : (Listof Variable)
|
|
([i (in-range 1 (add1 n))])
|
|
(format-symbol "x~a" i)))
|
|
(table+vars->network var-names table))
|
|
|
|
(module+ test
|
|
(test-case "table->network"
|
|
(define n (table->network '((#f #f #f #f)
|
|
(#f #t #f #t)
|
|
(#t #f #t #f)
|
|
(#t #t #t #t))))
|
|
(define f1 (hash-ref (network-functions n) 'x1))
|
|
(define f2 (hash-ref (network-functions n) 'x2))
|
|
|
|
(check-false (f1 (hash 'x1 #f 'x2 #f)))
|
|
(check-false (f1 (hash 'x1 #f 'x2 #t)))
|
|
(check-true (f1 (hash 'x1 #t 'x2 #f)))
|
|
(check-true (f1 (hash 'x1 #t 'x2 #t)))
|
|
|
|
(check-false (f2 (hash 'x1 #f 'x2 #f)))
|
|
(check-true (f2 (hash 'x1 #f 'x2 #t)))
|
|
(check-false (f2 (hash 'x1 #t 'x2 #f)))
|
|
(check-true (f2 (hash 'x1 #t 'x2 #t)))
|
|
|
|
(check-equal? (network-domains n)
|
|
#hash((x1 . (#f #t)) (x2 . (#f #t))))))
|
|
|
|
(: table+headers->network (All (a) (-> (Pairof (Listof Symbol) (Listof (Listof a)))
|
|
(Network a))))
|
|
(define (table+headers->network table)
|
|
(define headers : (Listof Symbol) (car table))
|
|
(define var-names : (Listof Variable)
|
|
(drop-right headers (quotient (length headers) 2)))
|
|
(table+vars->network var-names (cdr table)))
|
|
|
|
(module+ test
|
|
(test-case "table+headers->network"
|
|
(define n (table+headers->network
|
|
'((x1 x2 f1 f2)
|
|
(#f #f #f #f)
|
|
(#f #t #f #t)
|
|
(#t #f #t #f)
|
|
(#t #t #t #t))))
|
|
(define f1 (hash-ref (network-functions n) 'x1))
|
|
(define f2 (hash-ref (network-functions n) 'x2))
|
|
|
|
(check-false (f1 (hash 'x1 #f 'x2 #f)))
|
|
(check-false (f1 (hash 'x1 #f 'x2 #t)))
|
|
(check-true (f1 (hash 'x1 #t 'x2 #f)))
|
|
(check-true (f1 (hash 'x1 #t 'x2 #t)))
|
|
|
|
(check-false (f2 (hash 'x1 #f 'x2 #f)))
|
|
(check-true (f2 (hash 'x1 #f 'x2 #t)))
|
|
(check-false (f2 (hash 'x1 #t 'x2 #f)))
|
|
(check-true (f2 (hash 'x1 #t 'x2 #t)))
|
|
|
|
(check-equal? (network-domains n)
|
|
#hash((x1 . (#f #t)) (x2 . (#f #t))))))
|
|
|
|
(: random-function/state (All (a) (-> (DomainMapping a) (Domain a)
|
|
(-> (State a) a))))
|
|
(define (random-function/state arg-domains func-domain)
|
|
(table->unary-function
|
|
(for/list : (Listof (List (State a) a))
|
|
([st (build-all-states arg-domains)])
|
|
(list st (random-ref func-domain)))))
|
|
|
|
(module+ test (random-seed 1))
|
|
|
|
(module+ test
|
|
(test-case "random-function/state"
|
|
(define doms (hash 'a '(1 2) 'b '(3 4)))
|
|
(define f (random-function/state doms '(e f)))
|
|
(check-equal? (tabulate-state+headers f doms)
|
|
'((a b f1) (1 3 e) (1 4 e) (2 3 f) (2 4 e)))))
|
|
|
|
(: random-boolean-function/state (-> (Listof Variable) (-> (State Boolean) Boolean)))
|
|
(define (random-boolean-function/state args)
|
|
(random-function/state (make-boolean-domains args) '(#f #t)))
|
|
|
|
(module+ test
|
|
(test-case "random-boolean-function/state"
|
|
(define f (random-boolean-function/state '(x1 x2)))
|
|
(check-equal? (tabulate-state+headers/boolean f '(x1 x2))
|
|
'((x1 x2 f1) (#f #f #f) (#f #t #f) (#t #f #t) (#t #t #t)))))
|
|
|
|
(: random-network (All (a) (-> (DomainMapping a) (Network a))))
|
|
(define (random-network domains)
|
|
(network (for/hash : (VariableMapping (UpdateFunction a))
|
|
([(x x-dom) (in-hash domains)])
|
|
(values x (random-function/state domains x-dom)))
|
|
domains))
|
|
|
|
(module+ test
|
|
(test-case "random-network"
|
|
(check-equal?
|
|
(tabulate-network+headers (random-network (hash 'a '(1 2) 'b '(#f #t))))
|
|
'((a b f-a f-b) (1 #f 1 #f) (1 #t 1 #f) (2 #f 2 #t) (2 #t 2 #f)))))
|
|
|
|
(: random-boolean-network (-> (Listof Variable) (Network Boolean)))
|
|
(define (random-boolean-network vars)
|
|
(random-network (make-boolean-domains vars)))
|
|
|
|
(module+ test
|
|
(test-case "random-boolean-network"
|
|
(check-equal?
|
|
(tabulate-network+headers (random-boolean-network '(x y z)))
|
|
'((x y z f-x f-y f-z)
|
|
(#f #f #f #t #t #t)
|
|
(#f #f #t #f #f #f)
|
|
(#f #t #f #t #t #t)
|
|
(#f #t #t #f #t #f)
|
|
(#t #f #f #t #t #t)
|
|
(#t #f #t #f #t #f)
|
|
(#t #t #f #f #f #t)
|
|
(#t #t #t #t #t #f)))))
|
|
|
|
(: random-boolean-network/n (-> Positive-Integer (Network Boolean)))
|
|
(define (random-boolean-network/n n)
|
|
(random-boolean-network (for/list : (Listof Variable)
|
|
([i (in-range n)])
|
|
(string->symbol (format "x~a" i)))))
|
|
|
|
(module+ test
|
|
(test-case "random-boolean-network/n"
|
|
(check-equal?
|
|
(tabulate-network+headers (random-boolean-network/n 3))
|
|
'((x0 x1 x2 f-x0 f-x1 f-x2)
|
|
(#f #f #f #f #t #f)
|
|
(#f #f #t #f #f #t)
|
|
(#f #t #f #t #f #f)
|
|
(#f #t #t #f #t #t)
|
|
(#t #f #f #t #t #t)
|
|
(#t #f #t #t #f #t)
|
|
(#t #t #f #t #t #f)
|
|
(#t #t #t #t #f #t)))))
|