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