Compare commits
316 Commits
typed-rack
...
master
Author | SHA1 | Date |
---|---|---|
Sergiu Ivanov | 56301357ca | |
Sergiu Ivanov | 3ba0cca66d | |
Sergiu Ivanov | 0e3cc3c9fd | |
Sergiu Ivanov | e01f99457d | |
Sergiu Ivanov | 09b62feb46 | |
Sergiu Ivanov | 51c033b29c | |
Sergiu Ivanov | 5a2307ed58 | |
Sergiu Ivanov | 1e9d906b34 | |
Sergiu Ivanov | a2e5f9d091 | |
Sergiu Ivanov | 11c736b04b | |
Sergiu Ivanov | a18620e694 | |
Sergiu Ivanov | 0e364eb52d | |
Sergiu Ivanov | 5bc062af54 | |
Sergiu Ivanov | 99d50c8505 | |
Sergiu Ivanov | 6a3bd9e7a6 | |
Sergiu Ivanov | c667f75c0e | |
Sergiu Ivanov | b2bc06646e | |
Sergiu Ivanov | 78e88840cc | |
Sergiu Ivanov | f398d10d15 | |
Sergiu Ivanov | a469eae764 | |
Sergiu Ivanov | c1723066a7 | |
Sergiu Ivanov | 6a9adf5e07 | |
Sergiu Ivanov | aae2e1f964 | |
Sergiu Ivanov | e884a2ee07 | |
Sergiu Ivanov | a51bba870e | |
Sergiu Ivanov | b29f98105f | |
Sergiu Ivanov | c944841dc6 | |
Sergiu Ivanov | 064169f0b6 | |
Sergiu Ivanov | d779c52cc8 | |
Sergiu Ivanov | cf9a68ae6b | |
Sergiu Ivanov | 9efca22f7b | |
Sergiu Ivanov | 6518ae2fdd | |
Sergiu Ivanov | 6cf1505a61 | |
Sergiu Ivanov | 3821a11d38 | |
Sergiu Ivanov | 4e90afc638 | |
Sergiu Ivanov | ceb8e64a35 | |
Sergiu Ivanov | ddc6b3d10c | |
Sergiu Ivanov | 17b6ac7fb6 | |
Sergiu Ivanov | 0fa6ead5ec | |
Sergiu Ivanov | dd7117c733 | |
Sergiu Ivanov | e9ecbd8a7c | |
Sergiu Ivanov | ac7f928737 | |
Sergiu Ivanov | aca3fb7868 | |
Sergiu Ivanov | 704221185b | |
Sergiu Ivanov | 927877b02f | |
Sergiu Ivanov | d9641e7b5b | |
Sergiu Ivanov | cdb4602701 | |
Sergiu Ivanov | b7b4956fdc | |
Sergiu Ivanov | 91b96463da | |
Sergiu Ivanov | f0a20646ef | |
Sergiu Ivanov | f05024e61e | |
Sergiu Ivanov | 78462d5083 | |
Sergiu Ivanov | c523c68037 | |
Sergiu Ivanov | 8241bc4da5 | |
Sergiu Ivanov | 0c91e6f6b2 | |
Sergiu Ivanov | afbc5426ce | |
Sergiu Ivanov | cd8cada92e | |
Sergiu Ivanov | 76c6bb5745 | |
Sergiu Ivanov | 18c9828a5a | |
Sergiu Ivanov | 738ad858ae | |
Sergiu Ivanov | ab56b64d38 | |
Sergiu Ivanov | 001a12d166 | |
Sergiu Ivanov | b9eb692091 | |
Sergiu Ivanov | 74347b5151 | |
Sergiu Ivanov | 08d41dd4ca | |
Sergiu Ivanov | b1b78917ce | |
Sergiu Ivanov | 495ea18bb5 | |
Sergiu Ivanov | b611115f8c | |
Sergiu Ivanov | 43d782f149 | |
Sergiu Ivanov | 3455b8aae1 | |
Sergiu Ivanov | 764b4612f1 | |
Sergiu Ivanov | 72454c395c | |
Sergiu Ivanov | 7c5333555c | |
Sergiu Ivanov | e8ebab58ca | |
Sergiu Ivanov | b8b9fee9ce | |
Sergiu Ivanov | ed67927803 | |
Sergiu Ivanov | 490127593c | |
Sergiu Ivanov | fa88c15454 | |
Sergiu Ivanov | d2e4ab854c | |
Sergiu Ivanov | 0396e558ce | |
Sergiu Ivanov | aa278215ed | |
Sergiu Ivanov | 507f7a28f7 | |
Sergiu Ivanov | e6764b2dd6 | |
Sergiu Ivanov | 861665e205 | |
Sergiu Ivanov | 5e155d0a61 | |
Sergiu Ivanov | e2e0ee6903 | |
Sergiu Ivanov | 59b90d4c12 | |
Sergiu Ivanov | f5349a3659 | |
Sergiu Ivanov | 78c638a886 | |
Sergiu Ivanov | abf8d4cf92 | |
Sergiu Ivanov | d3907556ba | |
Sergiu Ivanov | a6321c932a | |
Sergiu Ivanov | 9175a98a2a | |
Sergiu Ivanov | 1490792a19 | |
Sergiu Ivanov | 6783b97add | |
Sergiu Ivanov | 09c14907ca | |
Sergiu Ivanov | b9b224fc6a | |
Sergiu Ivanov | 0e5334f5e1 | |
Sergiu Ivanov | dd23de304f | |
Sergiu Ivanov | 16626c70ec | |
Sergiu Ivanov | 53e4981845 | |
Sergiu Ivanov | da368d2574 | |
Sergiu Ivanov | d7d9274bc9 | |
Sergiu Ivanov | 6dce7583ab | |
Sergiu Ivanov | fabecbe0f9 | |
Sergiu Ivanov | 3d58660e9c | |
Sergiu Ivanov | b5fef760c5 | |
Sergiu Ivanov | f8a03659d1 | |
Sergiu Ivanov | d00056affb | |
Sergiu Ivanov | 990abee3db | |
Sergiu Ivanov | 47602a1785 | |
Sergiu Ivanov | fb56dc6589 | |
Sergiu Ivanov | 4c3415f0ac | |
Sergiu Ivanov | 2577d06dbc | |
Sergiu Ivanov | d5a351d6c1 | |
Sergiu Ivanov | d5382eacda | |
Sergiu Ivanov | b1cc242c7b | |
Sergiu Ivanov | 6d2034f9e5 | |
Sergiu Ivanov | 945626487c | |
Sergiu Ivanov | 4d05b9d9ee | |
Sergiu Ivanov | 46612dd3df | |
Sergiu Ivanov | d2ab44c79b | |
Sergiu Ivanov | cc121fc9e3 | |
Sergiu Ivanov | 60fd8b2a24 | |
Sergiu Ivanov | 567a721c8f | |
Sergiu Ivanov | 857b33ad71 | |
Sergiu Ivanov | d7f4d2d732 | |
Sergiu Ivanov | ecc57a34fc | |
Sergiu Ivanov | 45410176b7 | |
Sergiu Ivanov | 983380b063 | |
Sergiu Ivanov | bba44c2887 | |
Sergiu Ivanov | 8c89bf810a | |
Sergiu Ivanov | 811df5fe1e | |
Sergiu Ivanov | 279ee68b91 | |
Sergiu Ivanov | b0c084af37 | |
Sergiu Ivanov | 77f2fcb58f | |
Sergiu Ivanov | 3691c42e67 | |
Sergiu Ivanov | 413f1798c4 | |
Sergiu Ivanov | 25411043c1 | |
Sergiu Ivanov | f2f0564f72 | |
Sergiu Ivanov | 2424e155fd | |
Sergiu Ivanov | 3e1dca8d63 | |
Sergiu Ivanov | 59b3d5f6fe | |
Sergiu Ivanov | 1bfc44491e | |
Sergiu Ivanov | d64b04a8f0 | |
Sergiu Ivanov | eaabcd9a05 | |
Sergiu Ivanov | 8722d63d3e | |
Sergiu Ivanov | 0018c91fb6 | |
Sergiu Ivanov | 893b375b91 | |
Sergiu Ivanov | 0336526a84 | |
Sergiu Ivanov | 871a923842 | |
Sergiu Ivanov | 7a3cbaa1af | |
Sergiu Ivanov | 007baa5e41 | |
Sergiu Ivanov | 43e29f928b | |
Sergiu Ivanov | b795be0a39 | |
Sergiu Ivanov | 181b427cd8 | |
Sergiu Ivanov | 2c2d8fbbdb | |
Sergiu Ivanov | 320ae55456 | |
Sergiu Ivanov | b97bbfd972 | |
Sergiu Ivanov | 84134340e5 | |
Sergiu Ivanov | 901720d2f5 | |
Sergiu Ivanov | fc633c2e4c | |
Sergiu Ivanov | 458ba10ab5 | |
Sergiu Ivanov | aea472acb2 | |
Sergiu Ivanov | ba30e3dc5e | |
Sergiu Ivanov | 9a2f1ff527 | |
Sergiu Ivanov | 4ea31d8f39 | |
Sergiu Ivanov | 70be49b957 | |
Sergiu Ivanov | 5da523d297 | |
Sergiu Ivanov | d02742ba6d | |
Sergiu Ivanov | bed1ed24f3 | |
Sergiu Ivanov | e867a86d4d | |
Sergiu Ivanov | be729f6ca8 | |
Sergiu Ivanov | 609de226a9 | |
Sergiu Ivanov | 883e845d9d | |
Sergiu Ivanov | 8067f9e7f0 | |
Sergiu Ivanov | 4927c0ec8c | |
Sergiu Ivanov | 86d52eed3b | |
Sergiu Ivanov | 5efe086d06 | |
Sergiu Ivanov | 85566d7479 | |
Sergiu Ivanov | cdcee66b7c | |
Sergiu Ivanov | ba8b9b4d98 | |
Sergiu Ivanov | 9182ea9ecb | |
Sergiu Ivanov | 0e2b91fdd1 | |
Sergiu Ivanov | 974bf193ed | |
Sergiu Ivanov | ef979d6dce | |
Sergiu Ivanov | 945dfe1490 | |
Sergiu Ivanov | 6d37f180ba | |
Sergiu Ivanov | 297d455207 | |
Sergiu Ivanov | 9db0fcbdb4 | |
Sergiu Ivanov | da6f5acf4e | |
Sergiu Ivanov | e1a97235c0 | |
Sergiu Ivanov | 8ded018c05 | |
Sergiu Ivanov | 70caf3bb7e | |
Sergiu Ivanov | 64daec5065 | |
Sergiu Ivanov | 2e8373d037 | |
Sergiu Ivanov | 0e9a974965 | |
Sergiu Ivanov | 0196ab5800 | |
Sergiu Ivanov | 47f7f70241 | |
Sergiu Ivanov | cf49a6f087 | |
Sergiu Ivanov | d958c5822d | |
Sergiu Ivanov | 79a688a3e5 | |
Sergiu Ivanov | 1f01917f8a | |
Sergiu Ivanov | d9734a5a35 | |
Sergiu Ivanov | cbede999df | |
Sergiu Ivanov | 1863b0829c | |
Sergiu Ivanov | 027022524b | |
Sergiu Ivanov | dc8a098234 | |
Sergiu Ivanov | f3a8c65e9d | |
Sergiu Ivanov | e44d9c7748 | |
Sergiu Ivanov | cc137a5459 | |
Sergiu Ivanov | 212440add1 | |
Sergiu Ivanov | ee487af157 | |
Sergiu Ivanov | 9ad3a69c27 | |
Sergiu Ivanov | 242ea9d31d | |
Sergiu Ivanov | a7a25f92fe | |
Sergiu Ivanov | 52bf1b2f58 | |
Sergiu Ivanov | a201e537e1 | |
Sergiu Ivanov | a4a6604ecd | |
Sergiu Ivanov | fa63875022 | |
Sergiu Ivanov | 4272cd87a8 | |
Sergiu Ivanov | 1324be292e | |
Sergiu Ivanov | 3b51a4ba51 | |
Sergiu Ivanov | 1503434306 | |
Sergiu Ivanov | 6102a2b8f3 | |
Sergiu Ivanov | 81496a2ee7 | |
Sergiu Ivanov | 1b22ba4a7e | |
Sergiu Ivanov | c19d18122c | |
Sergiu Ivanov | 096d536908 | |
Sergiu Ivanov | f2a9336d71 | |
Sergiu Ivanov | f29bb3956d | |
Sergiu Ivanov | b1613ac1f7 | |
Sergiu Ivanov | 650801a6d2 | |
Sergiu Ivanov | 1e4f6d3fbc | |
Sergiu Ivanov | 5aa816507d | |
Sergiu Ivanov | b07cda477f | |
Sergiu Ivanov | 8fb5bab803 | |
Sergiu Ivanov | 8b838c0b22 | |
Sergiu Ivanov | 1f281d3851 | |
Sergiu Ivanov | e7616684f5 | |
Sergiu Ivanov | fcf21c51aa | |
Sergiu Ivanov | 31d6275229 | |
Sergiu Ivanov | 37dddb190f | |
Sergiu Ivanov | 929bf09299 | |
Sergiu Ivanov | 66f1157200 | |
Sergiu Ivanov | ccb70a5921 | |
Sergiu Ivanov | cd94326ea3 | |
Sergiu Ivanov | 3e16dbe3c8 | |
Sergiu Ivanov | fa940eb0d3 | |
Sergiu Ivanov | 97d4c18305 | |
Sergiu Ivanov | f62d53ed8f | |
Sergiu Ivanov | bd6c62e3eb | |
Sergiu Ivanov | b026f74f39 | |
Sergiu Ivanov | 687aea5337 | |
Sergiu Ivanov | 357fcad89f | |
Sergiu Ivanov | 77cc8e295a | |
Sergiu Ivanov | 83b293e3e4 | |
Sergiu Ivanov | 347e17158d | |
Sergiu Ivanov | f5c762b6d8 | |
Sergiu Ivanov | 10f0e0ab0c | |
Sergiu Ivanov | 8cd75b0fa3 | |
Sergiu Ivanov | 3918730e1a | |
Sergiu Ivanov | fa015870d0 | |
Sergiu Ivanov | f6946d94ad | |
Sergiu Ivanov | 6679db9aa4 | |
Sergiu Ivanov | 87c80bb6ef | |
Sergiu Ivanov | b36d8adaa4 | |
Sergiu Ivanov | ec50395cb0 | |
Sergiu Ivanov | 2af8d9276b | |
Sergiu Ivanov | 20cc0a27d0 | |
Sergiu Ivanov | 912da811f2 | |
Sergiu Ivanov | 9defe51ee6 | |
Sergiu Ivanov | e01ba07724 | |
Sergiu Ivanov | 50fb3dab59 | |
Sergiu Ivanov | cf3f20097b | |
Sergiu Ivanov | 002b1a8006 | |
Sergiu Ivanov | 67ae415064 | |
Sergiu Ivanov | 0179423be9 | |
Sergiu Ivanov | 45a60cd122 | |
Sergiu Ivanov | e1cf64a822 | |
Sergiu Ivanov | 25b4216faf | |
Sergiu Ivanov | 8d5d41233e | |
Sergiu Ivanov | e4f7c956d4 | |
Sergiu Ivanov | 2997319f1f | |
Sergiu Ivanov | 211f39e91f | |
Sergiu Ivanov | 4703bfcce8 | |
Sergiu Ivanov | 403401f085 | |
Sergiu Ivanov | ec28541c46 | |
Sergiu Ivanov | caf9114ec5 | |
Sergiu Ivanov | b2f3306731 | |
Sergiu Ivanov | ebd960de53 | |
Sergiu Ivanov | 746c586973 | |
Sergiu Ivanov | 8b2bab4d9e | |
Sergiu Ivanov | 73afefdeb2 | |
Sergiu Ivanov | 44de84de10 | |
Sergiu Ivanov | c1967816b4 | |
Sergiu Ivanov | 69a7234665 | |
Sergiu Ivanov | ec84924608 | |
Sergiu Ivanov | b6417d2d07 | |
Sergiu Ivanov | 9d569bd3ba | |
Sergiu Ivanov | 783000318b | |
Sergiu Ivanov | f30d9b9aa1 | |
Sergiu Ivanov | 8ffd252fc2 | |
Sergiu Ivanov | 85d60c6a9b | |
Sergiu Ivanov | 752d173279 | |
Sergiu Ivanov | 75560658a0 | |
Sergiu Ivanov | 556a4ae8bf | |
Sergiu Ivanov | d64ecfb207 | |
Sergiu Ivanov | 28fdc23324 | |
Sergiu Ivanov | 906d339508 | |
Sergiu Ivanov | 505487d3b6 | |
Sergiu Ivanov | 29a9c57ebd | |
Sergiu Ivanov | 4490cadf58 | |
Sergiu Ivanov | 33ddd747c3 | |
Sergiu Ivanov | e3e50c99ea | |
Sergiu Ivanov | 00db8b651b |
55
README.org
55
README.org
|
@ -18,7 +18,9 @@ Currently, the toolkit includes the following files:
|
|||
|
||||
- [[file:generic.rkt][generic.rkt]]: The generic interface for a discrete dynamical
|
||||
system, with functions for constructing state graphs.
|
||||
- [[file:utils.rkt][utils.rkt]]: Misc utility functions.
|
||||
- [[file:utils.rkt][utils.rkt]]: Misc utilities.
|
||||
- [[file:functions.rkt][functions.rkt]]: Definitions for working with functions: tabulating,
|
||||
constructing from tables, generating random functions, etc.
|
||||
- [[file:networks.rkt][networks.rkt]]: Implements network-based models, which generalise
|
||||
Boolean networks, threshold Boolean automata networks, multivalued
|
||||
networks, etc.
|
||||
|
@ -34,43 +36,20 @@ interaction with Org-mode.
|
|||
that I work on the subsequent items decreases with their position
|
||||
in the list.
|
||||
|
||||
*** TODO Convert =dds= to Typed Racket
|
||||
Rewriting =dds= with Typed Racket will be a major refactoring,
|
||||
consisting in the following 3 phases which will happen more or
|
||||
less in parallel:
|
||||
*** TODO Fix =ob-racket= to work with Typed Racket
|
||||
Currently, the =ob-racket= backend for running Racket code blocks in
|
||||
Org-mode assumes the code is written in =#lang racket=, which breaks
|
||||
the output for tables for example. This makes the typed version of
|
||||
=dds= essentially useless with tables.
|
||||
|
||||
- define types, add type signatures, add types for functions
|
||||
I import from =graph=;
|
||||
- transfer the comments from the source files to Scribble
|
||||
documentation;
|
||||
- redefine the generic interface for dynamics, which is currently
|
||||
in =generics=.
|
||||
|
||||
I plan to implement the new dynamics interface as a classes, since
|
||||
Typed Racket can now type classes. I didn't really like generics:
|
||||
they are quite cumbersome, and I think I can do more with classes.
|
||||
|
||||
People on Racket Users suggested using structures with fields
|
||||
containing functions, but it does not seem to get me to my goal of
|
||||
having type-level methods/functions.
|
||||
|
||||
The order in which I will convert the modules:
|
||||
|
||||
1. =utils=
|
||||
2. =functions=
|
||||
3. =dynamics= (currently =generics=)
|
||||
4. =networks=
|
||||
5. =rs=
|
||||
|
||||
This is how I will convert a module =A=:
|
||||
|
||||
1. Create a copy =A-untyped.rkt=.
|
||||
2. Empty =A.rkt=.
|
||||
3. Transfer the definitions from =A-untyped.rkt= to =A.rkt= one by
|
||||
one, adding the necessary types, signatures, and typed imports
|
||||
as I go.
|
||||
4. When done, remove =A-untyped.rkt=.
|
||||
*** TODO Fix [[file:example/example.org][example.org]]
|
||||
I changed quite a number of things while converting =dds= to Typed
|
||||
Racket, which should break a lot of things in [[file:example/example.org][example.org]].
|
||||
|
||||
*** TODO Think about splitting the library into lib and doc
|
||||
*** TODO Implement string rewriting
|
||||
*** TODO Implement [[https://en.wikipedia.org/wiki/P_system][P systems]]
|
||||
*** TODO Consider splitting =dds/utils= into a separate package
|
||||
*** TODO Implement a shorter syntax for defining Boolean functions
|
||||
Right now, this is how one defines the Boolean function:
|
||||
|
||||
|
@ -87,14 +66,14 @@ interaction with Org-mode.
|
|||
(define-pbf my-pbf (or (and (not :Go) :new-x) (and :Go :old-x)))
|
||||
#+END_SRC
|
||||
|
||||
*** TODO Submit =update-graph= to =stchang=
|
||||
*** TODO Implement =monotone?=
|
||||
=monotone?= would verify whether a given Boolean function is
|
||||
monotone according to the definition in the book /Boolean
|
||||
Functions: Theory, Algorithms, and Applications/ by Crama
|
||||
and Hammer.
|
||||
|
||||
*** TODO Submit =update-graph= to =stchang=
|
||||
*** TODO Split =networks= into general networks and threshold Boolean networks
|
||||
*** TODO Consider optimizing the algorithms in =networks= and =dynamics=
|
||||
*** TODO Implement the BN \to RS conversion
|
||||
*** TODO Implement the minimisation of TBF/SBF
|
||||
*** TODO Contribute to Racket
|
||||
|
|
9
dds.org
9
dds.org
|
@ -17,7 +17,14 @@ raco pkg install
|
|||
raco setup -l dds
|
||||
#+END_SRC
|
||||
|
||||
I think that the =dds= package must already be installed.
|
||||
The =dds= package must already be installed.
|
||||
|
||||
Execute the following code block to open the documentation in the
|
||||
last visited browser window.
|
||||
|
||||
#+BEGIN_SRC elisp :results silent
|
||||
(shell-command "firefox doc/dds/index.html")
|
||||
#+END_SRC
|
||||
|
||||
* Racket Package catalog bug <2020-11-26 Thu>
|
||||
=raco pkg= had a bug which caused it to fail with the following
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
#lang typed/racket
|
||||
|
||||
(require "utils.rkt" typed/graph)
|
||||
|
||||
(provide dds%)
|
||||
|
||||
(define dds%
|
||||
(class object%
|
||||
#:forall (State Modality)
|
||||
(super-new)
|
||||
|
||||
(: step (-> State (Listof State)))
|
||||
(define/public (step st)
|
||||
(map (inst cdr Modality State) (step/annotated st)))
|
||||
|
||||
(: step/annotated (-> State (Listof (Pairof Modality State))))
|
||||
(define/abstract/error (step/annotated st))
|
||||
|
||||
(: step* (-> (Listof State) (Listof State)))
|
||||
(define/public (step* sts)
|
||||
(remove-duplicates
|
||||
(apply append
|
||||
(for/list : (Listof (Listof State)) ([s sts])
|
||||
(step s)))))
|
||||
|
||||
(: build-state-graph (-> (Listof State) Graph))
|
||||
(define/public (build-state-graph sts)
|
||||
(build-state-graph* sts 'full))
|
||||
|
||||
(: build-state-graph/annotated (-> (Listof State) Graph))
|
||||
(define/public (build-state-graph/annotated sts)
|
||||
(build-state-graph*/annotated sts 'full))
|
||||
|
||||
(: build-state-graph* (-> (Listof State) (U Positive-Integer 'full) Graph))
|
||||
(define/public (build-state-graph* sts nsteps)
|
||||
(unweighted-graph/directed
|
||||
(assert-type (get-edges (build-state-graph*/annotated sts nsteps))
|
||||
(Listof (List Any Any)))))
|
||||
|
||||
(: build-state-graph*/annotated (-> (Listof State) (U Positive-Integer 'full) Graph))
|
||||
(define/public (build-state-graph*/annotated sts nsteps)
|
||||
(define (all-steps-done? k)
|
||||
(if (equal? nsteps 'full)
|
||||
#f ; keep going forever
|
||||
(>= (assert-type k Integer) nsteps)))
|
||||
(weighted-graph/directed
|
||||
(let build-graph : (Listof (List Modality State State))
|
||||
([visited-states : (Setof State) (set)]
|
||||
[states : (Setof State) (list->set sts)]
|
||||
[edges : (Listof (List Modality State State)) '()]
|
||||
[k 1])
|
||||
(define new-edges
|
||||
(for*/list : (Listof (List Modality State State))
|
||||
([s (in-set states)] ; the state we are looking at
|
||||
[out (in-list (step/annotated s))]) ; the arrows going out of s
|
||||
(list (car out) s (cdr out))))
|
||||
(define edges/full (append edges new-edges))
|
||||
(define new-states
|
||||
(list->set (map (inst caddr Modality State State) new-edges)))
|
||||
(define new-states-pruned
|
||||
(set-subtract new-states visited-states))
|
||||
(if (or (set-empty? new-states-pruned) (all-steps-done? k))
|
||||
edges/full
|
||||
(build-graph (set-union visited-states new-states-pruned)
|
||||
new-states-pruned
|
||||
edges/full
|
||||
(add1 k))))))
|
||||
))
|
737
functions.rkt
737
functions.rkt
|
@ -1,4 +1,4 @@
|
|||
#lang racket
|
||||
#lang typed/racket
|
||||
|
||||
;;; dds/functions
|
||||
|
||||
|
@ -9,154 +9,281 @@
|
|||
|
||||
(require "utils.rkt")
|
||||
|
||||
(require "utils.rkt"
|
||||
syntax/parse/define typed/racket/stream
|
||||
(only-in typed/racket/unsafe unsafe-provide)
|
||||
(for-syntax syntax/parse))
|
||||
|
||||
(require/typed racket/stream
|
||||
[stream-map (All (a b) (-> (-> a b) (Sequenceof a) (Sequenceof b)))])
|
||||
|
||||
(provide
|
||||
;; Structures
|
||||
(contract-out
|
||||
[struct tbf ((weights (vectorof number?)) (threshold number?))])
|
||||
;; Functions
|
||||
(contract-out
|
||||
[tabulate (-> procedure? (listof generic-set?) (listof list?))]
|
||||
[tabulate* (-> (listof procedure?) (listof generic-set?) (listof list?))]
|
||||
[tabulate/boolean (-> procedure-fixed-arity? (listof (listof boolean?)))]
|
||||
[tabulate*/boolean (-> (non-empty-listof procedure-fixed-arity?) (listof (listof boolean?)))]
|
||||
[tabulate/01 (-> procedure? (listof (listof (or/c 0 1))))]
|
||||
[tabulate*/01 (-> (non-empty-listof procedure?) (listof (listof (or/c 0 1))))]
|
||||
[table->function (-> (listof (*list/c any/c any/c)) procedure?)]
|
||||
[table->function/list (-> (listof (*list/c any/c any/c)) procedure?)]
|
||||
[enumerate-boolean-tables (-> number? (stream/c (listof (*list/c boolean? boolean?))))]
|
||||
[enumerate-boolean-functions (-> number? (stream/c procedure?))]
|
||||
[enumerate-boolean-functions/list (-> number? (stream/c procedure?))]
|
||||
[random-boolean-table (-> number? (listof (*list/c boolean? boolean?)))]
|
||||
[random-boolean-function (-> number? procedure?)]
|
||||
[random-boolean-function/list (-> number? procedure?)]
|
||||
[tbf-w (-> tbf? (vectorof number?))]
|
||||
[tbf-θ (-> tbf? number?)]
|
||||
[vector-boolean->01 (-> (vectorof boolean?) (vectorof (or/c 0 1)))]
|
||||
[apply-tbf (-> tbf? (vectorof (or/c 0 1)) (or/c 0 1))]
|
||||
[apply-tbf/boolean (-> tbf? (vectorof boolean?) boolean?)]
|
||||
[list->tbf (-> (cons/c number? (cons/c number? (listof number?))) tbf?)]
|
||||
[lists->tbfs (-> (listof (listof number?)) (listof tbf?))]
|
||||
[read-org-tbfs (->* (string?) (#:headers boolean?) (listof tbf?))]
|
||||
[tbf-tabulate* (-> (listof tbf?) (listof (listof (or/c 0 1))))]
|
||||
[tbf-tabulate (-> tbf? (listof (listof (or/c 0 1))))]
|
||||
[tbf-tabulate*/boolean (-> (listof tbf?) (listof (listof boolean?)))]
|
||||
[sbf (-> (vectorof number?) tbf?)]
|
||||
[list->sbf (-> (listof number?) sbf?)]
|
||||
[read-org-sbfs (->* (string?) (#:headers boolean?) (listof sbf?))])
|
||||
;; Predicates
|
||||
(contract-out
|
||||
[sbf? (-> any/c boolean?)]))
|
||||
pseudovariadic-lambda pvλ pseudovariadic-define pvdefine
|
||||
tabulate* tabulate*/strict tabulate*/pv tabulate tabulate/strict tabulate/pv
|
||||
tabulate*/pv/boolean tabulate/pv/boolean tabulate*/pv/01 tabulate/pv/01
|
||||
tabulate*/list tabulate/list
|
||||
tabulate*/list/boolean tabulate/list/boolean tabulate*/list/01 tabulate/list/01
|
||||
table->function/list table->unary-function table->function table->function/pv
|
||||
enumerate-boolean-tables enumerate-boolean-functions
|
||||
enumerate-boolean-functions/pv enumerate-boolean-functions/list
|
||||
random-boolean-table random-boolean-function random-boolean-function/list
|
||||
|
||||
(struct-out tbf) TBF tbf-w tbf-θ boolean->01/vector apply-tbf apply-tbf/boolean
|
||||
list->tbf lists->tbfs read-org-tbfs tbf-tabulate* tbf-tabulate
|
||||
tbf-tabulate*/boolean sbf? sbf list->sbf read-org-sbfs)
|
||||
|
||||
(module+ test
|
||||
(require rackunit))
|
||||
(require typed/rackunit))
|
||||
|
||||
(begin-for-syntax
|
||||
(require racket (for-syntax syntax/parse))
|
||||
|
||||
(define (make-pseudovariadic-core args bodies tag-stx)
|
||||
(define nargs-stx (datum->syntax args (length (syntax->list args))))
|
||||
#`(λ xs
|
||||
(match xs
|
||||
[(list #,@args) #,@bodies]
|
||||
[_ (error #,tag-stx "invalid arity, expected ~a argument(s)" #,nargs-stx)])))
|
||||
|
||||
(define (make-pseudovariadic-lambda stx)
|
||||
(syntax-parse stx
|
||||
[(_ (args:id ...) bodies:expr ...)
|
||||
(make-pseudovariadic-core #'(args ...)
|
||||
#'(bodies ...)
|
||||
(datum->syntax stx ''pseudovariadic-lambda))]))
|
||||
|
||||
(define (make-pseudovariadic-define stx)
|
||||
(syntax-parse stx
|
||||
[(_ (name:id args:id ...) bodies:expr ...)
|
||||
#`(define name
|
||||
#,(make-pseudovariadic-core
|
||||
#'(args ...)
|
||||
#'(bodies ...)
|
||||
(datum->syntax #'name `(quote ,(syntax->datum #'name)))))])))
|
||||
|
||||
(define-syntax (pseudovariadic-lambda stx) (make-pseudovariadic-lambda stx))
|
||||
(define-syntax (pvλ stx) (make-pseudovariadic-lambda stx))
|
||||
|
||||
(module+ test
|
||||
(test-case "pseudovariadic-lambda")
|
||||
(check-false ((pseudovariadic-lambda (x y) (and x y)) #t #f))
|
||||
(check-false ((pvλ (x y) (and x y)) #t #f))
|
||||
(check-exn exn:fail? (λ () ((pseudovariadic-lambda (x y) (and x y)) #t #f #f)))
|
||||
(check-exn exn:fail? (λ () ((pvλ (x y) (and x y)) #t #f #f))))
|
||||
|
||||
(define-syntax (pseudovariadic-define stx) (make-pseudovariadic-define stx))
|
||||
(define-syntax (pvdefine stx) (make-pseudovariadic-define stx))
|
||||
|
||||
(module+ test
|
||||
(test-case "pseudovariadic-define")
|
||||
(: f (-> Boolean * Boolean))
|
||||
(pseudovariadic-define (f x y) (and x y))
|
||||
(check-false (f #t #f))
|
||||
(check-exn exn:fail? (λ () (f #t #f #f)))
|
||||
|
||||
(: g (-> Boolean * Boolean))
|
||||
(pvdefine (g x y) (and x y))
|
||||
(check-false (g #t #f))
|
||||
(check-exn exn:fail? (λ () (g #t #f #f))))
|
||||
|
||||
(define-syntax-parse-rule (make-tabulate* name:id row-op:id apply-op:id)
|
||||
(define (name funcs doms)
|
||||
(for/list ([xs (in-list (apply cartesian-product doms))])
|
||||
(row-op xs (for/list ([f funcs]) : (Listof b)
|
||||
(apply-op f xs))))))
|
||||
|
||||
(: tabulate* (All (b a ... ) (-> (Listof (-> a ... b)) (List (Listof a) ... a)
|
||||
(Listof (Listof (U Any b))))))
|
||||
(make-tabulate* tabulate* append apply)
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate*"
|
||||
(check-equal? (tabulate*
|
||||
(list (λ (x y) (and x y))
|
||||
(λ (x y) (or x y)))
|
||||
'((#f #t) (#f #t)))
|
||||
'((#f #f #f #f) (#f #t #f #t) (#t #f #f #t) (#t #t #t #t)))
|
||||
(check-equal? (tabulate* empty '((#f #t) (#f #t)))
|
||||
'((#f #f) (#f #t) (#t #f) (#t #t)))))
|
||||
|
||||
(: tabulate*/strict (All (b a ...) (-> (Listof (-> a ... b)) (List (Listof a) ... a)
|
||||
(Listof (List (List a ...) (Listof b))))))
|
||||
(make-tabulate* tabulate*/strict list apply)
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate*/strict"
|
||||
(check-equal? (tabulate*/strict
|
||||
(list (λ (x y) (and x y))
|
||||
(λ (x y) (or x y)))
|
||||
'((#f #t) (#f #t)))
|
||||
'(((#f #f) (#f #f)) ((#f #t) (#f #t)) ((#t #f) (#f #t)) ((#t #t) (#t #t))))))
|
||||
|
||||
|
||||
;;; ==========
|
||||
;;; Tabulating
|
||||
;;; ==========
|
||||
(: tabulate*/pv (All (a b) (-> (Listof (-> a * b)) (Listof (Listof a))
|
||||
(Listof (Listof (U a b))))))
|
||||
(make-tabulate* tabulate*/pv append apply)
|
||||
|
||||
;;; Given a function and a list of domains for each of its arguments,
|
||||
;;; in order, produces a list of lists giving the values of arguments
|
||||
;;; and the value of the functions for these inputs.
|
||||
(module+ test
|
||||
(test-case "tabulate*/pv"
|
||||
(check-equal? (tabulate*/pv (list (pvλ (x y) (and x y))
|
||||
(pvλ (x y) (or x y)))
|
||||
'((#f #t) (#f #t)))
|
||||
'((#f #f #f #f) (#f #t #f #t) (#t #f #f #t) (#t #t #t #t)))))
|
||||
|
||||
(define-syntax-parse-rule (simple-apply func:expr arg:expr)
|
||||
(func arg))
|
||||
|
||||
(: tabulate*/list (All (a b) (-> (Listof (-> (Listof a) b)) (Listof (Listof a))
|
||||
(Listof (Listof (U a b))))))
|
||||
(make-tabulate* tabulate*/list append simple-apply)
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate*/list"
|
||||
(check-equal? (tabulate*/list (list (λ ([xs : (Listof Boolean)])
|
||||
(and (car xs) (cadr xs)))
|
||||
(λ ([xs : (Listof Boolean)])
|
||||
(or (car xs) (cadr xs))))
|
||||
'((#f #t) (#f #t)))
|
||||
'((#f #f #f #f) (#f #t #f #t) (#t #f #f #t) (#t #t #t #t)))))
|
||||
|
||||
(: tabulate (All (b a ...) (-> (-> a ... b) (List (Listof a) ... a)
|
||||
(Listof (Listof (U Any b))))))
|
||||
(define (tabulate func doms)
|
||||
(tabulate* `(,func) doms))
|
||||
(tabulate* (list func) doms))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate"
|
||||
(check-equal? (tabulate (λ (x y) (and x y)) '((#f #t) (#f #t)))
|
||||
'((#f #f #f) (#f #t #f) (#t #f #f) (#t #t #t)))))
|
||||
|
||||
;;; Like tabulate, but takes a list of functions taking
|
||||
;;; the same arguments over the same domains.
|
||||
(define (tabulate* funcs doms)
|
||||
(for/list ([xs (apply cartesian-product doms)])
|
||||
(append xs (for/list ([f funcs]) (apply f xs)))))
|
||||
(: tabulate/strict (All (b a ...) (-> (-> a ... b) (List (Listof a) ... a)
|
||||
(Listof (List (List a ...) (Listof b))))))
|
||||
(define (tabulate/strict func doms)
|
||||
(tabulate*/strict (list func) doms))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate*"
|
||||
(check-equal? (tabulate* (list (λ (x y) (and x y))
|
||||
(λ (x y) (or x y)))
|
||||
'((#f #t) (#f #t)))
|
||||
'((#f #f #f #f) (#f #t #f #t) (#t #f #f #t) (#t #t #t #t)))
|
||||
(check-equal? (tabulate* empty '((#f #t) (#f #t)))
|
||||
'((#f #f) (#f #t) (#t #f) (#t #t)))))
|
||||
(test-case "tabulate/strict"
|
||||
(check-equal? (tabulate/strict (λ (x y) (and x y)) '((#f #t) (#f #t)))
|
||||
'(((#f #f) (#f)) ((#f #t) (#f)) ((#t #f) (#f)) ((#t #t) (#t))))))
|
||||
|
||||
;;; Like tabulate, but assumes the domains of all variables of the
|
||||
;;; function are Boolean. func must have a fixed arity. It is an
|
||||
;;; error to supply a function of variable arity.
|
||||
(define (tabulate/boolean func)
|
||||
(tabulate func (make-list (procedure-arity func) '(#f #t))))
|
||||
(: tabulate/pv (All (a b) (-> (-> a * b) (Listof (Listof a))
|
||||
(Listof (Listof (U a b))))))
|
||||
(define (tabulate/pv func doms)
|
||||
(tabulate*/pv (list func) doms))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate/boolean"
|
||||
(check-equal? (tabulate/boolean (lambda (x y) (and x y)))
|
||||
(test-case "tabulate/pv"
|
||||
(check-equal? (tabulate/pv (pvλ (x y) (and x y)) '((#f #t) (#f #t)))
|
||||
'((#f #f #f) (#f #t #f) (#t #f #f) (#t #t #t)))))
|
||||
|
||||
;;; Like tabulate/boolean, but takes a list of functions of the same
|
||||
;;; arity.
|
||||
(define (tabulate*/boolean funcs)
|
||||
(define doms (make-list (procedure-arity (car funcs)) '(#f #t)))
|
||||
(tabulate* funcs doms))
|
||||
(: tabulate/list (All (a b) (-> (-> (Listof a) b) (Listof (Listof a))
|
||||
(Listof (Listof (U a b))))))
|
||||
(define (tabulate/list func doms)
|
||||
(tabulate*/list (list func) doms))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate*/boolean"
|
||||
(check-equal? (tabulate*/boolean `(,(λ (x y) (and x y))
|
||||
,(λ (x y) (or x y))))
|
||||
(test-case "tabulate/list"
|
||||
(check-equal? (tabulate/list (λ ([xs : (Listof Boolean)])
|
||||
(and (car xs) (cadr xs)))
|
||||
'((#f #t) (#f #t)))
|
||||
'((#f #f #f) (#f #t #f) (#t #f #f) (#t #t #t)))))
|
||||
|
||||
(: tabulate/pv/boolean (-> Positive-Integer (-> Boolean * Boolean) (Listof (Listof Boolean))))
|
||||
(define (tabulate/pv/boolean arity func)
|
||||
(tabulate/pv func (make-list arity '(#f #t))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate/pv/boolean"
|
||||
(check-equal? (tabulate/pv/boolean 2 (pvλ (x y) (and x y)))
|
||||
'((#f #f #f) (#f #t #f) (#t #f #f) (#t #t #t)))))
|
||||
|
||||
(: tabulate*/pv/boolean (-> Positive-Integer (Listof (-> Boolean * Boolean))
|
||||
(Listof (Listof Boolean))))
|
||||
(define (tabulate*/pv/boolean arity funcs)
|
||||
(tabulate*/pv funcs (make-list arity '(#f #t))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate*/pv/boolean"
|
||||
(check-equal? (tabulate*/pv/boolean 2 (list (pvλ (x y) (and x y))
|
||||
(pvλ (x y) (or x y))))
|
||||
'((#f #f #f #f) (#f #t #f #t) (#t #f #f #t) (#t #t #t #t)))))
|
||||
|
||||
;;; Like tabulate, but assumes the domains of all variables of the
|
||||
;;; function are {0, 1}. func must have a fixed arity. It is an
|
||||
;;; error to supply a function of variable arity.
|
||||
(define (tabulate/01 func)
|
||||
(tabulate func (make-list (procedure-arity func) '(0 1))))
|
||||
(: tabulate/pv/01 (-> Positive-Integer (-> (U Zero One) * (U Zero One))
|
||||
(Listof (Listof (U Zero One)))))
|
||||
(define (tabulate/pv/01 arity func)
|
||||
(tabulate/pv func (make-list arity '(0 1))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate/01"
|
||||
(check-equal? (tabulate/01 (λ (x y) (modulo (+ x y) 2)))
|
||||
(test-case "tabulate/pv/01"
|
||||
(check-equal? (tabulate/pv/01 2 (pvλ (x y)
|
||||
(assert-type (modulo (+ x y) 2) (U Zero One))))
|
||||
'((0 0 0) (0 1 1) (1 0 1) (1 1 0)))))
|
||||
|
||||
;;; Like tabulate/01, but takes a list of functions of the same arity.
|
||||
(define (tabulate*/01 funcs)
|
||||
(tabulate* funcs (make-list (procedure-arity (car funcs)) '(0 1))))
|
||||
(: tabulate*/pv/01 (-> Positive-Integer (Listof (-> (U Zero One) * (U Zero One)))
|
||||
(Listof (Listof (U Zero One)))))
|
||||
(define (tabulate*/pv/01 arity funcs)
|
||||
(tabulate*/pv funcs (make-list arity '(0 1))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate*/01"
|
||||
(check-equal? (tabulate*/01 `(,(λ (x y) (min x y)) ,(λ (x y) (max x y))))
|
||||
(test-case "tabulate*/pv/01"
|
||||
(check-equal? (tabulate*/pv/01 2 `(,(pvλ (x y) (assert-type (min x y) (U Zero One)))
|
||||
,(pvλ (x y) (assert-type (max x y) (U Zero One)))))
|
||||
'((0 0 0 0) (0 1 0 1) (1 0 0 1) (1 1 1 1)))))
|
||||
|
||||
;;; ======================
|
||||
;;; Constructing functions
|
||||
;;; ======================
|
||||
|
||||
;;; Given a table like the one produced by the tabulate functions,
|
||||
;;; creates a function which has this behaviour.
|
||||
;;;
|
||||
;;; More exactly, the input is a list of lists of values. All but the
|
||||
;;; last elements of every list give the values of the parameters of
|
||||
;;; the function, while the the last element of every list gives the
|
||||
;;; value of the function. Thus, every list should have at least two
|
||||
;;; elements.
|
||||
;;;
|
||||
;;; The produced function is implemented via lookups in hash tables,
|
||||
;;; meaning that it may be sometimes more expensive to compute than by
|
||||
;;; using an direct symbolic implementation.
|
||||
(define (table->function table)
|
||||
(let ([func (table->function/list table)])
|
||||
(λ args (func args))))
|
||||
(: tabulate/list/boolean (-> Positive-Integer (-> (Listof Boolean) Boolean)
|
||||
(Listof (Listof Boolean))))
|
||||
(define (tabulate/list/boolean arity func)
|
||||
(tabulate/list func (make-list arity '(#f #t))))
|
||||
|
||||
(module+ test
|
||||
(test-case "table->function"
|
||||
(define negation (table->function '((#t #f) (#f #t))))
|
||||
(check-true (negation #f))
|
||||
(check-false (negation #t))))
|
||||
(test-case "tabulate/list/boolean"
|
||||
(check-equal? (tabulate/list/boolean 2 (λ (xs) (and (car xs) (cadr xs))))
|
||||
'((#f #f #f) (#f #t #f) (#t #f #f) (#t #t #t)))))
|
||||
|
||||
;;; Like table->function, but the produced function accepts a single
|
||||
;;; list of arguments instead of individual arguments.
|
||||
(: tabulate*/list/boolean (-> Positive-Integer (Listof (-> (Listof Boolean) Boolean))
|
||||
(Listof (Listof Boolean))))
|
||||
(define (tabulate*/list/boolean arity funcs)
|
||||
(tabulate*/list funcs (make-list arity '(#f #t))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate*/list/boolean"
|
||||
(check-equal?
|
||||
(tabulate*/list/boolean 2 (list (λ (xs) (and (car xs) (cadr xs)))
|
||||
(λ (xs) (or (car xs) (cadr xs)))))
|
||||
'((#f #f #f #f) (#f #t #f #t) (#t #f #f #t) (#t #t #t #t)))))
|
||||
|
||||
(: tabulate/list/01 (-> Positive-Integer (-> (Listof (U Zero One)) (U Zero One))
|
||||
(Listof (Listof (U Zero One)))))
|
||||
(define (tabulate/list/01 arity func)
|
||||
(tabulate/list func (make-list arity '(0 1))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate/list/01"
|
||||
(check-equal?
|
||||
(tabulate/list/01 2 (λ (xs)
|
||||
(assert-type (modulo (+ (car xs) (cadr xs)) 2) (U Zero One))))
|
||||
'((0 0 0) (0 1 1) (1 0 1) (1 1 0)))))
|
||||
|
||||
(: tabulate*/list/01 (-> Positive-Integer (Listof (-> (Listof (U Zero One)) (U Zero One)))
|
||||
(Listof (Listof (U Zero One)))))
|
||||
(define (tabulate*/list/01 arity funcs)
|
||||
(tabulate*/list funcs (make-list arity '(0 1))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate*/list/01"
|
||||
(check-equal? (tabulate*/list/01
|
||||
2
|
||||
`(,(λ (xs) (assert-type (min (car xs) (cadr xs)) (U Zero One)))
|
||||
,(λ (xs) (assert-type (max (car xs) (cadr xs)) (U Zero One)))))
|
||||
'((0 0 0 0) (0 1 0 1) (1 0 0 1) (1 1 1 1)))))
|
||||
|
||||
(: table->function/list (All (a) (-> (Listof (Listof a))
|
||||
(-> (Listof a) a))))
|
||||
(define (table->function/list table)
|
||||
((curry hash-ref)
|
||||
(for/hash ([line table])
|
||||
(let-values ([(x fx) (split-at-right line 1)])
|
||||
(values x (car fx))))))
|
||||
(table->unary-function
|
||||
(for/list ([line (in-list table)]) : (Listof (List (Listof a) a))
|
||||
(define-values (ins out) (split-at-right line 1))
|
||||
(list ins (car out)))))
|
||||
|
||||
(module+ test
|
||||
(test-case "table->function/list"
|
||||
|
@ -164,136 +291,201 @@
|
|||
(check-true (negation/list '(#f)))
|
||||
(check-false (negation/list '(#t)))))
|
||||
|
||||
;;; Returns the stream of the truth tables of all Boolean functions of
|
||||
;;; a given arity.
|
||||
;;;
|
||||
;;; There are 2^(2^n) Boolean functions of arity n.
|
||||
(define (enumerate-boolean-tables n)
|
||||
(let ([inputs (boolean-power/stream n)]
|
||||
[outputs (boolean-power/stream (expt 2 n))])
|
||||
(for/stream ([out (in-stream outputs)])
|
||||
(for/list ([in (in-stream inputs)] [o out])
|
||||
(append in (list o))))))
|
||||
(: table->unary-function (All (a b) (-> (Listof (List a b)) (-> a b))))
|
||||
(define (table->unary-function table)
|
||||
(define ht-tab
|
||||
(for/hash ([line (in-list table)]) : (HashTable a b)
|
||||
(values (car line) (cadr line))))
|
||||
(λ (x) (hash-ref ht-tab x)))
|
||||
|
||||
;;; Returns the stream of all Boolean functions of a given arity.
|
||||
;;;
|
||||
;;; There are 2^(2^n) Boolean functions of arity n.
|
||||
(define (enumerate-boolean-functions n)
|
||||
(stream-map table->function (enumerate-boolean-tables n)))
|
||||
(module+ test
|
||||
(test-case "table->unary-function"
|
||||
(define unary-negation (table->unary-function '((#t #f) (#f #t))))
|
||||
(check-false (unary-negation #t))
|
||||
(check-true (unary-negation #f))))
|
||||
|
||||
(: table->function (All (a) (-> (Listof (Listof a)) (-> a * a))))
|
||||
(define (table->function table)
|
||||
(define func (table->function/list table))
|
||||
(λ args (func args)))
|
||||
|
||||
(module+ test
|
||||
(test-case "table->function"
|
||||
(define negation (table->function '((#t #f) (#f #t))))
|
||||
(check-true (negation #f))
|
||||
(check-false (negation #t))))
|
||||
|
||||
(: table->function/pv (All (a) (-> (Listof (Listof a)) (-> a * a))))
|
||||
(define (table->function/pv table)
|
||||
(define func (table->function/list table))
|
||||
(define arity (- (length (car table)) 1))
|
||||
(λ xs
|
||||
(if (= arity (length xs))
|
||||
(func xs)
|
||||
(error 'pseudovariadic-lambda
|
||||
"invalid arity, expected ~a argument(s)"
|
||||
arity))))
|
||||
|
||||
(module+ test
|
||||
(test-case "table->function/pv"
|
||||
(define negation (table->function/pv '((#t #f) (#f #t))))
|
||||
(check-true (negation #f))
|
||||
(check-false (negation #t))
|
||||
(check-exn exn:fail? (λ () (negation #f #t)))))
|
||||
|
||||
(: enumerate-boolean-tables (-> Positive-Integer (Sequenceof (Listof (Listof Boolean)))))
|
||||
(define (enumerate-boolean-tables n)
|
||||
(define inputs (boolean-power n))
|
||||
(define outputs (boolean-power/stream (assert-type (expt 2 n) Integer)))
|
||||
|
||||
(: append-outputs (-> (Listof (Listof Boolean)) (Listof Boolean)
|
||||
(Listof (Listof Boolean))))
|
||||
(define (append-outputs ins outs)
|
||||
(for/list ([row ins] [o outs]) (append row (list o))))
|
||||
|
||||
(: yield (-> (Sequenceof (Listof Boolean)) (Sequenceof (Listof (Listof Boolean)))))
|
||||
(define (yield rest-outputs)
|
||||
(if (stream-empty? rest-outputs)
|
||||
(stream)
|
||||
(stream-cons (append-outputs inputs (stream-first rest-outputs))
|
||||
(yield (stream-rest rest-outputs)))))
|
||||
|
||||
(yield outputs))
|
||||
|
||||
(module+ test
|
||||
(test-case "enumerate-boolean-tables"
|
||||
(define f1 (stream-first (enumerate-boolean-functions 1)))
|
||||
(check-false (f1 #f))
|
||||
(check-false (f1 #t))))
|
||||
(check-equal? (stream->list (enumerate-boolean-tables 2))
|
||||
'(((#f #f #f) (#f #t #f) (#t #f #f) (#t #t #f))
|
||||
((#f #f #f) (#f #t #f) (#t #f #f) (#t #t #t))
|
||||
((#f #f #f) (#f #t #f) (#t #f #t) (#t #t #f))
|
||||
((#f #f #f) (#f #t #f) (#t #f #t) (#t #t #t))
|
||||
((#f #f #f) (#f #t #t) (#t #f #f) (#t #t #f))
|
||||
((#f #f #f) (#f #t #t) (#t #f #f) (#t #t #t))
|
||||
((#f #f #f) (#f #t #t) (#t #f #t) (#t #t #f))
|
||||
((#f #f #f) (#f #t #t) (#t #f #t) (#t #t #t))
|
||||
((#f #f #t) (#f #t #f) (#t #f #f) (#t #t #f))
|
||||
((#f #f #t) (#f #t #f) (#t #f #f) (#t #t #t))
|
||||
((#f #f #t) (#f #t #f) (#t #f #t) (#t #t #f))
|
||||
((#f #f #t) (#f #t #f) (#t #f #t) (#t #t #t))
|
||||
((#f #f #t) (#f #t #t) (#t #f #f) (#t #t #f))
|
||||
((#f #f #t) (#f #t #t) (#t #f #f) (#t #t #t))
|
||||
((#f #f #t) (#f #t #t) (#t #f #t) (#t #t #f))
|
||||
((#f #f #t) (#f #t #t) (#t #f #t) (#t #t #t))))))
|
||||
|
||||
;;; Returns the stream of all Boolean functions of a given arity. As
|
||||
;;; different from the functions returned by
|
||||
;;; enumerate-boolean-functions, the functions take lists of arguments
|
||||
;;; instead of n arguments.
|
||||
;;;
|
||||
;;; There are 2^(2^n) Boolean functions of arity n.
|
||||
(: enumerate-boolean-functions (-> Positive-Integer (Sequenceof (-> Boolean * Boolean))))
|
||||
(define (enumerate-boolean-functions n)
|
||||
(stream-map (inst table->function Boolean) (enumerate-boolean-tables n)))
|
||||
|
||||
(module+ test
|
||||
(test-case "enumerate-boolean-functions"
|
||||
(define bool-f1 (stream-first (enumerate-boolean-functions 1)))
|
||||
(check-false (bool-f1 #f))
|
||||
(check-false (bool-f1 #t))))
|
||||
|
||||
(: enumerate-boolean-functions/pv (-> Positive-Integer (Sequenceof (-> Boolean * Boolean))))
|
||||
(define (enumerate-boolean-functions/pv n)
|
||||
(stream-map (inst table->function/pv Boolean) (enumerate-boolean-tables n)))
|
||||
|
||||
(module+ test
|
||||
(test-case "enumerate-boolean-functions/pv"
|
||||
(define bool-f1/pv (stream-first (enumerate-boolean-functions/pv 1)))
|
||||
(check-false (bool-f1/pv #f))
|
||||
(check-false (bool-f1/pv #t))
|
||||
(check-exn exn:fail? (λ () (bool-f1/pv #f #f)))))
|
||||
|
||||
(: enumerate-boolean-functions/list
|
||||
(-> Positive-Integer (Sequenceof (-> (Listof Boolean) Boolean))))
|
||||
(define (enumerate-boolean-functions/list n)
|
||||
(stream-map table->function/list (enumerate-boolean-tables n)))
|
||||
(stream-map (inst table->function/list Boolean) (enumerate-boolean-tables n)))
|
||||
|
||||
(module+ test
|
||||
(test-case "enumerate-boolean-functions/list"
|
||||
(define f1/list (stream-first (enumerate-boolean-functions/list 1)))
|
||||
(check-false (f1/list '(#f)))
|
||||
(check-false (f1/list '(#t)))))
|
||||
(define bool-f1/list (stream-first (enumerate-boolean-functions/list 1)))
|
||||
(check-false (bool-f1/list '(#f)))
|
||||
(check-false (bool-f1/list '(#t)))))
|
||||
|
||||
|
||||
;;; ================
|
||||
;;; Random functions
|
||||
;;; ================
|
||||
|
||||
;;; Generates a random truth table for a Boolean function of arity n.
|
||||
(: random-boolean-table (-> Positive-Integer (Listof (Listof Boolean))))
|
||||
(define (random-boolean-table n)
|
||||
(define/match (num->bool x) [(0) #f] [(1) #t])
|
||||
(define inputs (boolean-power n))
|
||||
(define outputs (stream-take (in-random 2) (expt 2 n)))
|
||||
(for/list ([i inputs] [o outputs])
|
||||
(append i (list (num->bool o)))))
|
||||
(define ins (boolean-power n))
|
||||
(define outs (stream-take (in-random 2) (assert-type (expt 2 n) Nonnegative-Integer)))
|
||||
(for/list ([i ins] [o outs])
|
||||
(append i (list (if (= o 1) #t #f)))))
|
||||
|
||||
(module+ test
|
||||
(test-case "random-boolean-table"
|
||||
(random-seed 0)
|
||||
(check-equal? (random-boolean-table 2) '((#f #f #t) (#f #t #t) (#t #f #f) (#t #t #f)))))
|
||||
(random-seed 1)
|
||||
(check-equal? (random-boolean-table 2)
|
||||
'((#f #f #t)
|
||||
(#f #t #t)
|
||||
(#t #f #f)
|
||||
(#t #t #t)))))
|
||||
|
||||
;;; Generates a random Boolean function of arity n.
|
||||
(define random-boolean-function (compose table->function random-boolean-table))
|
||||
(: random-boolean-function (-> Positive-Integer (-> Boolean * Boolean)))
|
||||
(define (random-boolean-function n)
|
||||
(table->function (random-boolean-table n)))
|
||||
|
||||
(module+ test
|
||||
(test-case "random-boolean-function"
|
||||
(define f (random-boolean-function 2))
|
||||
(check-true (f #f #f)) (check-false (f #f #t))
|
||||
(check-true (f #t #f)) (check-false (f #t #t))))
|
||||
(random-seed 1)
|
||||
(define random-bool-f (random-boolean-function 2))
|
||||
(check-true (random-bool-f #f #f))
|
||||
(check-true (random-bool-f #f #t))
|
||||
(check-false (random-bool-f #t #f))
|
||||
(check-true (random-bool-f #t #t))))
|
||||
|
||||
;;; Like random-boolean-function, but the constructed function takes a
|
||||
;;; list of arguments.
|
||||
(define random-boolean-function/list (compose table->function/list random-boolean-table))
|
||||
(: random-boolean-function/list (-> Positive-Integer (-> (Listof Boolean) Boolean)))
|
||||
(define (random-boolean-function/list n)
|
||||
(table->function/list (random-boolean-table n)))
|
||||
|
||||
(module+ test
|
||||
(test-case "random-boolean-function/list"
|
||||
(define f (random-boolean-function/list 2))
|
||||
(check-false (f '(#f #f))) (check-true (f '(#f #t)))
|
||||
(check-true (f '(#t #f))) (check-false (f '(#t #t)))))
|
||||
(random-seed 1)
|
||||
(define random-bool-f/list (random-boolean-function/list 2))
|
||||
(check-true (random-bool-f/list '(#f #f)))
|
||||
(check-true (random-bool-f/list '(#f #t)))
|
||||
(check-false (random-bool-f/list '(#t #f)))
|
||||
(check-true (random-bool-f/list '(#t #t)))))
|
||||
|
||||
|
||||
;;; ===========================
|
||||
;;; Threshold Boolean functions
|
||||
;;; ===========================
|
||||
|
||||
;;; A threshold Boolean function (TBF) is a pair (w, θ), where w is a
|
||||
;;; vector of weights and θ is the threshold.
|
||||
(struct tbf (weights threshold) #:transparent)
|
||||
|
||||
;;; Unicode shortcuts for accessing the elements of a TBF.
|
||||
(struct tbf ([weights : (Vectorof Real)] [threshold : Real])
|
||||
#:transparent #:type-name TBF)
|
||||
(define tbf-w tbf-weights)
|
||||
(define tbf-θ tbf-threshold)
|
||||
|
||||
;;; Converts a Boolean vector to a 0-1 vector.
|
||||
(define (vector-boolean->01 bool-v)
|
||||
(vector-map any->01 bool-v))
|
||||
(: boolean->01/vector (-> (Vectorof Boolean) (Vectorof (U Zero One))))
|
||||
(define (boolean->01/vector bool-v)
|
||||
(vector-map (λ (x) (any->01 x)) bool-v))
|
||||
|
||||
(module+ test
|
||||
(test-case "boolean->0-1"
|
||||
(check-equal? (vector-boolean->01 #(#t #f #f)) #(1 0 0))))
|
||||
(test-case "boolean->01/vector"
|
||||
(check-equal? (boolean->01/vector #(#t #f #f)) #(1 0 0))))
|
||||
|
||||
;;; Applies the TBF to its inputs.
|
||||
;;;
|
||||
;;; Applying a TBF consists in multiplying the weights by the
|
||||
;;; corresponding inputs and comparing the sum of the products to the
|
||||
;;; threshold.
|
||||
(: apply-tbf (-> TBF (Vectorof (U Zero One)) (U Zero One)))
|
||||
(define (apply-tbf tbf inputs)
|
||||
(any->01
|
||||
(>
|
||||
;; The scalar product between the inputs and the weights
|
||||
;; The scalar product between the inputs and the weights.
|
||||
(for/sum ([x (in-vector inputs)]
|
||||
[w (in-vector (tbf-w tbf))])
|
||||
(* x w))
|
||||
[w (in-vector (tbf-w tbf))]) : Real
|
||||
(* x w))
|
||||
(tbf-θ tbf))))
|
||||
|
||||
(module+ test
|
||||
(test-case "apply-tbf"
|
||||
(define f1 (tbf #(2 -2) 1))
|
||||
(check-equal? (tabulate/01 (λ (x y) (apply-tbf f1 (vector x y))))
|
||||
(check-equal? (tabulate/pv/01 2 (pvλ (x y) (apply-tbf f1 (vector x y))))
|
||||
'((0 0 0) (0 1 0) (1 0 1) (1 1 0)))))
|
||||
|
||||
;;; Like apply-tbf, but takes Boolean values as inputs and outputs a
|
||||
;;; boolean value.
|
||||
(: apply-tbf/boolean (-> TBF (Vectorof Boolean) Boolean))
|
||||
(define (apply-tbf/boolean tbf inputs)
|
||||
(01->boolean (apply-tbf tbf (vector-map any->01 inputs))))
|
||||
(01->boolean (apply-tbf tbf (boolean->01/vector inputs))))
|
||||
|
||||
(module+ test
|
||||
(test-case "apply-tbf/boolean"
|
||||
(define f1 (tbf #(2 -2) 1))
|
||||
(check-equal? (tabulate/boolean (λ (x y) (apply-tbf/boolean f1 (vector x y))))
|
||||
(check-equal? (tabulate/pv/boolean 2 (pvλ (x y) (apply-tbf/boolean f1 (vector x y))))
|
||||
'((#f #f #f) (#f #t #f) (#t #f #t) (#t #t #f)))))
|
||||
|
||||
;;; Converts a list of numbers to a TBF. The last element of the list
|
||||
;;; is taken to be the threshold, while the other elements are taken
|
||||
;;; to be the weights.
|
||||
(: list->tbf (-> (Listof Real) TBF))
|
||||
(define (list->tbf lst)
|
||||
(define-values (w θ) (split-at-right lst 1))
|
||||
(tbf (list->vector w) (car θ)))
|
||||
|
@ -302,112 +494,167 @@
|
|||
(test-case "list->tbf"
|
||||
(check-equal? (list->tbf '(1 2 3)) (tbf #(1 2) 3))))
|
||||
|
||||
;;; Reads a list of TBF from an Org-mode table read by
|
||||
;;; read-org-sexp.
|
||||
(define lists->tbfs ((curry map) list->tbf))
|
||||
(: lists->tbfs (-> (Listof (Listof Real)) (Listof TBF)))
|
||||
(define (lists->tbfs lsts)
|
||||
(map list->tbf lsts))
|
||||
|
||||
(module+ test
|
||||
(test-case "read-tbfs"
|
||||
(check-equal? (lists->tbfs '((1 2 3) (2 3 4)))
|
||||
(list (tbf '#(1 2) 3) (tbf '#(2 3) 4)))))
|
||||
|
||||
;;; Reads a list of TBF from an Org-mode string containing a sexp,
|
||||
;;; containing a list of lists of numbers. If headers is #t, drops
|
||||
;;; the first list, supposing that it contains the headers of the
|
||||
;;; table.
|
||||
;;;
|
||||
;;; The input is typically what read-org-sexp reads.
|
||||
(: read-org-tbfs (->* (String) (#:headers Boolean) (Listof TBF)))
|
||||
(define (read-org-tbfs str #:headers [headers #f])
|
||||
(define sexp (read-org-sexp str))
|
||||
(define sexp (assert-type (read-org-sexp str) (Listof Any)))
|
||||
(define sexp-clean (cond [headers (cdr sexp)] [else sexp]))
|
||||
(lists->tbfs sexp-clean))
|
||||
(lists->tbfs (assert-type sexp-clean (Listof (Listof Real)))))
|
||||
|
||||
(module+ test
|
||||
(test-case "read-org-tbfs"
|
||||
(check-equal? (read-org-tbfs "((1 2 1) (1 0 1))")
|
||||
(list (tbf '#(1 2) 1) (tbf '#(1 0) 1)))))
|
||||
|
||||
;;; Tabulates a list of TBFs.
|
||||
;;;
|
||||
;;; The result is a list of lists describing the truth table of the
|
||||
;;; given TBFs. The first elements of each line give the values of
|
||||
;;; the inputs, while the last elements give the values of each the
|
||||
;;; functions corresponding to the input.
|
||||
;;;
|
||||
;;; All the TBFs in tbfs must have the same number of inputs as the
|
||||
;;; first TBF in the list. This function does not check this
|
||||
;;; condition.
|
||||
(: tbf-tabulate* (-> (Listof TBF) (Listof (Listof (U Zero One)))))
|
||||
(define (tbf-tabulate* tbfs)
|
||||
(define funcs (for/list ([tbf tbfs])
|
||||
(λ in (apply-tbf tbf (list->vector in)))))
|
||||
: (Listof (-> (Listof (U Zero One)) (U Zero One)))
|
||||
(λ ([in : (Listof (U Zero One))])
|
||||
(apply-tbf tbf (list->vector in)))))
|
||||
(define nvars (vector-length (tbf-w (car tbfs))))
|
||||
(tabulate* funcs (make-list nvars '(0 1))))
|
||||
(tabulate*/list funcs (make-list nvars '(0 1))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tbf-tabulate*"
|
||||
(check-equal? (tbf-tabulate* (list (tbf #(2 2) 1) (tbf #(1 1) 1)))
|
||||
'((0 0 0 0) (0 1 1 0) (1 0 1 0) (1 1 1 1)))))
|
||||
|
||||
;;; Tabulates a TBF.
|
||||
(define tbf-tabulate (compose tbf-tabulate* list))
|
||||
(: tbf-tabulate (-> TBF (Listof (Listof (U Zero One)))))
|
||||
(define (tbf-tabulate t)
|
||||
(tbf-tabulate* (list t)))
|
||||
|
||||
(module+ test
|
||||
(test-case "tbf-tabulate"
|
||||
(check-equal? (tbf-tabulate (tbf #(1 2) 1))
|
||||
'((0 0 0) (0 1 1) (1 0 0) (1 1 1)))))
|
||||
|
||||
;;; Tabulates a list of TBFs like tbf-boolean*, but uses Boolean
|
||||
;;; values #f and #t instead of 0 and 1.
|
||||
;;;
|
||||
;;; All the TBFs in tbfs must have the same number of inputs as the
|
||||
;;; first TBF in the list. This function does not check this
|
||||
;;; condition.
|
||||
(: tbf-tabulate*/boolean (-> (Listof TBF) (Listof (Listof Boolean))))
|
||||
(define (tbf-tabulate*/boolean tbfs)
|
||||
(define funcs (for/list ([tbf tbfs])
|
||||
(λ in (apply-tbf/boolean tbf (list->vector in)))))
|
||||
: (Listof (-> (Listof Boolean) Boolean))
|
||||
(λ ([in : (Listof Boolean)])
|
||||
(apply-tbf/boolean tbf (list->vector in)))))
|
||||
(define nvars (vector-length (tbf-w (car tbfs))))
|
||||
(tabulate* funcs (make-list nvars '(#f #t))))
|
||||
(tabulate*/list funcs (make-list nvars '(#f #t))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tbf-tabulate*/boolean"
|
||||
(check-equal? (tbf-tabulate*/boolean `(,(tbf #(1 2) 1)))
|
||||
(check-equal? (tbf-tabulate*/boolean (list (tbf #(1 2) 1)))
|
||||
'((#f #f #f) (#f #t #t) (#t #f #f) (#t #t #t)))))
|
||||
|
||||
;;; A sign Boolean function (SBF) is a TBF whose threshold is 0.
|
||||
(define sbf? (and/c tbf? (λ (x) (= 0 (tbf-θ x)))))
|
||||
(: sbf? (-> TBF Boolean))
|
||||
(define (sbf? t)
|
||||
(= 0 (tbf-θ t)))
|
||||
|
||||
(module+ test
|
||||
(test-case "sbf?"
|
||||
(check-false (sbf? (tbf #(1 2) 3)))
|
||||
(check-true (sbf? (tbf #(1 2) 0)))))
|
||||
|
||||
;;; Creates a TBF which is an SBF from a vector of weights.
|
||||
(define (sbf w) (tbf w 0))
|
||||
(: sbf (-> (Vectorof Real) TBF))
|
||||
(define (sbf w)
|
||||
(tbf w 0))
|
||||
|
||||
(module+ test
|
||||
(test-case "sbf"
|
||||
(check-equal? (sbf #(1 -1)) (tbf '#(1 -1) 0))))
|
||||
|
||||
;;; Converts a list of numbers to an SBF. The elements of the list
|
||||
;;; are taken to be the weights of the SBF.
|
||||
(define list->sbf (compose sbf list->vector))
|
||||
(: list->sbf (-> (Listof Real) TBF))
|
||||
(define (list->sbf lst) (sbf (list->vector lst)))
|
||||
|
||||
(module+ test
|
||||
(test-case "list->sbf"
|
||||
(check-equal? (list->sbf '(1 -1)) (tbf '#(1 -1) 0))))
|
||||
|
||||
;;; Reads a list of SBF from an Org-mode string containing a sexp,
|
||||
;;; containing a list of lists of numbers. If headers is #t, drops
|
||||
;;; the first list, supposing that it contains the headers of the
|
||||
;;; table.
|
||||
;;;
|
||||
;;; The input is typically what read-org-sexp reads.
|
||||
(: read-org-sbfs (->* (String) (#:headers Boolean) (Listof TBF)))
|
||||
(define (read-org-sbfs str #:headers [headers #f])
|
||||
(define sexp (read-org-sexp str))
|
||||
(define sexp (assert-type (read-org-sexp str) (Listof Any)))
|
||||
(define sexp-clean (cond [headers (cdr sexp)] [else sexp]))
|
||||
(map list->sbf sexp-clean))
|
||||
(map list->sbf (assert-type sexp-clean (Listof (Listof Real)))))
|
||||
|
||||
(module+ test
|
||||
(test-case "read-org-sbfs"
|
||||
(check-equal? (read-org-sbfs "((1 1) (1 -1))")
|
||||
(list (tbf '#(1 1) 0) (tbf '#(1 -1) 0)))))
|
||||
|
||||
(module untyped racket
|
||||
(module+ test
|
||||
(require rackunit))
|
||||
|
||||
(provide
|
||||
(contract-out [tabulate* (-> (listof procedure?) (listof (listof any/c))
|
||||
(listof (listof any/c)))]
|
||||
[tabulate (-> procedure? (listof (listof any/c))
|
||||
(listof (listof any/c)))]
|
||||
[tabulate/boolean (-> procedure? (listof (listof boolean?)))]
|
||||
[tabulate*/boolean (-> (non-empty-listof procedure?)
|
||||
(listof (listof boolean?)))]
|
||||
[tabulate/01 (-> procedure? (listof (listof (or/c 0 1))))]
|
||||
[tabulate*/01 (-> (non-empty-listof procedure?) (listof (listof (or/c 0 1))))]))
|
||||
|
||||
(define (tabulate* funcs doms)
|
||||
(for/list ([xs (in-list (apply cartesian-product doms))])
|
||||
(append xs (for/list ([f funcs])
|
||||
(apply f xs)))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate*"
|
||||
(check-equal? (tabulate*
|
||||
(list (λ (x y) (and x y))
|
||||
(λ (x y) (or x y)))
|
||||
'((#f #t) (#f #t)))
|
||||
'((#f #f #f #f) (#f #t #f #t) (#t #f #f #t) (#t #t #t #t)))
|
||||
(check-equal? (tabulate* empty '((#f #t) (#f #t)))
|
||||
'((#f #f) (#f #t) (#t #f) (#t #t)))))
|
||||
|
||||
(define (tabulate func doms)
|
||||
(tabulate* (list func) doms))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate"
|
||||
(check-equal? (tabulate (λ (x y) (and x y)) '((#f #t) (#f #t)))
|
||||
'((#f #f #f) (#f #t #f) (#t #f #f) (#t #t #t)))))
|
||||
|
||||
(define (tabulate/boolean func)
|
||||
(tabulate func (make-list (procedure-arity func) '(#f #t))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate/boolean"
|
||||
(check-equal? (tabulate/boolean (lambda (x y) (and x y)))
|
||||
'((#f #f #f) (#f #t #f) (#t #f #f) (#t #t #t)))))
|
||||
|
||||
(define (tabulate*/boolean funcs)
|
||||
(define doms (make-list (procedure-arity (car funcs)) '(#f #t)))
|
||||
(tabulate* funcs doms))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate*/boolean"
|
||||
(check-equal? (tabulate*/boolean `(,(λ (x y) (and x y))
|
||||
,(λ (x y) (or x y))))
|
||||
'((#f #f #f #f) (#f #t #f #t) (#t #f #f #t) (#t #t #t #t)))))
|
||||
|
||||
(define (tabulate/01 func)
|
||||
(tabulate func (make-list (procedure-arity func) '(0 1))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate/01"
|
||||
(check-equal? (tabulate/01 (λ (x y) (modulo (+ x y) 2)))
|
||||
'((0 0 0) (0 1 1) (1 0 1) (1 1 0)))))
|
||||
|
||||
(define (tabulate*/01 funcs)
|
||||
(tabulate* funcs (make-list (procedure-arity (car funcs)) '(0 1))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate*/01"
|
||||
(check-equal? (tabulate*/01 `(,(λ (x y) (min x y)) ,(λ (x y) (max x y))))
|
||||
'((0 0 0 0) (0 1 0 1) (1 0 0 1) (1 1 1 1)))))
|
||||
)
|
||||
|
|
15
info.rkt
15
info.rkt
|
@ -2,5 +2,18 @@
|
|||
(define collection "dds")
|
||||
(define deps '("base"
|
||||
"graph-lib"
|
||||
"rackunit-lib"))
|
||||
"rackunit-lib"
|
||||
"rackunit-typed"
|
||||
"typed-graph"
|
||||
"typed-racket-lib"
|
||||
"typed-compose"
|
||||
"typed-racket-stream"
|
||||
"typed-racket-more"))
|
||||
(define build-deps '("racket-doc"
|
||||
"typed-racket-doc"
|
||||
"sandbox-lib"
|
||||
"scribble-lib"
|
||||
"typed-graph"
|
||||
"graph-doc"
|
||||
))
|
||||
(define scribblings '(("scribblings/dds.scrbl" (multi-page))))
|
||||
|
|
2226
networks.rkt
2226
networks.rkt
File diff suppressed because it is too large
Load Diff
525
rs.rkt
525
rs.rkt
|
@ -1,336 +1,355 @@
|
|||
#lang racket
|
||||
#lang typed/racket
|
||||
|
||||
;;; dds/rs
|
||||
|
||||
;;; Definitions for working with reaction systems.
|
||||
|
||||
(require graph "utils.rkt" "generic.rkt")
|
||||
(require typed/graph "utils.rkt" "dynamics.rkt")
|
||||
|
||||
(provide
|
||||
;; Structures
|
||||
(struct-out reaction)
|
||||
(struct-out state)
|
||||
(struct-out dynamics)
|
||||
;; Functions
|
||||
(contract-out [enabled? (-> reaction? (set/c symbol?) boolean?)]
|
||||
[list-enabled (-> reaction-system/c (set/c species?) (listof symbol?))]
|
||||
[union-products (-> reaction-system/c (listof symbol?) (set/c species?))]
|
||||
[apply-rs (-> reaction-system/c (set/c species?) (set/c species?))]
|
||||
[ht-str-triples->rs (-> (hash/c symbol? (list/c string? string? string?)) reaction-system/c)]
|
||||
[read-org-rs (-> string? reaction-system/c)]
|
||||
[read-context-sequence (-> string? (listof (set/c species?)))]
|
||||
[rs->ht-str-triples (-> reaction-system/c (hash/c symbol? (list/c string? string? string?)))]
|
||||
[dds-step-one (-> dynamics? state? (set/c state?))]
|
||||
[dds-step-one-annotated (-> dynamics? state? (set/c (cons/c (set/c symbol?) state?)))]
|
||||
[dds-step (-> dynamics? (set/c state? #:kind 'dont-care) (set/c state?))]
|
||||
[dds-build-state-graph (-> dynamics? (set/c state? #:kind 'dont-care) graph?)]
|
||||
[dds-build-n-step-state-graph (-> dynamics? (set/c state? #:kind 'dont-care) number? graph?)]
|
||||
[dds-build-state-graph-annotated (-> dynamics? (set/c state? #:kind 'dont-care) graph?)]
|
||||
[dds-build-n-step-state-graph-annotated (-> dynamics? (set/c state? #:kind 'dont-care) number? graph?)]
|
||||
[build-interactive-process-graph (-> reaction-system/c (listof (set/c species?)) graph?)]
|
||||
[build-reduced-state-graph (-> reaction-system/c (listof (set/c species?)) graph?)]
|
||||
[pretty-print-reduced-state-graph (-> graph? graph?)]
|
||||
[build-interactive-process (-> reaction-system/c (listof (set/c species?)) (listof (list/c (set/c species?) (set/c species?))))]
|
||||
[pretty-print-state-graph (-> graph? graph?)])
|
||||
;; Predicates
|
||||
(contract-out [species? (-> any/c boolean?)])
|
||||
;; Contracts
|
||||
(contract-out [reaction-system/c contract?]))
|
||||
Species (struct-out reaction) Reaction ReactionName ReactionSystem
|
||||
make-reaction enabled? list-enabled union-products apply-rs
|
||||
|
||||
str-triple->reaction ht-str-triples->rs read-org-rs read-context-sequence
|
||||
reaction->str-triple rs->ht-str-triples
|
||||
|
||||
(struct-out state) State dynamics% Dynamics% build-interactive-process-graph
|
||||
build-interactive-process-graph/simple-states
|
||||
pretty-print-state-graph/simple-states build-interactive-process
|
||||
build-interactive-process/org pretty-print-state pretty-print-state-graph
|
||||
)
|
||||
|
||||
(module+ test
|
||||
(require rackunit))
|
||||
(require typed/rackunit))
|
||||
|
||||
(define-type Species Symbol)
|
||||
|
||||
;;; =================
|
||||
;;; Basic definitions
|
||||
;;; =================
|
||||
(struct reaction ([reactants : (Setof Species)]
|
||||
[inhibitors : (Setof Species)]
|
||||
[products : (Setof Species)])
|
||||
#:transparent
|
||||
#:type-name Reaction)
|
||||
|
||||
;;; A species is a symbol.
|
||||
(define species? symbol?)
|
||||
(define-type ReactionName Symbol)
|
||||
|
||||
;;; A reaction is a triple of sets, giving the reactants, the
|
||||
;;; inhibitors, and the products, respectively.
|
||||
(struct reaction (reactants inhibitors products) #:transparent)
|
||||
(: make-reaction (-> (Listof Species) (Listof Species) (Listof Species) Reaction))
|
||||
(define (make-reaction r i p) (reaction (list->set r)
|
||||
(list->set i)
|
||||
(list->set p)))
|
||||
(module+ test
|
||||
(test-case "make-reaction"
|
||||
(check-equal? (make-reaction '(a b) '(c d) '(e f))
|
||||
(reaction (set 'b 'a) (set 'c 'd) (set 'f 'e)))))
|
||||
|
||||
;;; A reaction is enabled on a set if all of its reactants are in the
|
||||
;;; set and none of its inhibitors are.
|
||||
(: enabled? (-> Reaction (Setof Species) Boolean))
|
||||
(define/match (enabled? r s)
|
||||
[((reaction r i p) s)
|
||||
[((reaction r i _) s)
|
||||
(and (subset? r s) (set-empty? (set-intersect i s)))])
|
||||
|
||||
;;; A reaction system is a dictionary mapping reaction names to
|
||||
;;; reactions.
|
||||
(define reaction-system/c (hash/c symbol? reaction?))
|
||||
(module+ test
|
||||
(test-case "enabled?"
|
||||
(check-true (enabled? (make-reaction '(a b) '(c d) '())
|
||||
(set 'a 'b 'e)))
|
||||
(check-false (enabled? (make-reaction '(a b) '(c d) '())
|
||||
(set 'a 'b 'c)))
|
||||
(check-false (enabled? (make-reaction '(a b) '(c d) '())
|
||||
(set 'b 'e)))))
|
||||
|
||||
;;; Returns the list of reaction names enabled on a given set.
|
||||
(define-type ReactionSystem (HashTable ReactionName Reaction))
|
||||
|
||||
(: list-enabled (-> ReactionSystem (Setof Species) (Listof ReactionName)))
|
||||
(define (list-enabled rs s)
|
||||
(for/list ([(name reaction) (in-hash rs)]
|
||||
#:when (enabled? reaction s))
|
||||
name))
|
||||
|
||||
;;; Returns the union of the product sets of the given reactions in a
|
||||
;;; reaction system. If no reactions are supplied, returns the empty
|
||||
;;; set.
|
||||
;;;
|
||||
;;; This function can be seen as producing the result of the
|
||||
;;; application of the given reactions to a set. Clearly, it does not
|
||||
;;; check whether the reactions are actually enabled.
|
||||
(define (union-products rs as)
|
||||
(if (empty? as)
|
||||
(set)
|
||||
(apply set-union
|
||||
(for/list ([a as])
|
||||
(reaction-products (hash-ref rs a))))))
|
||||
(module+ test
|
||||
(test-case "list-enabled"
|
||||
(define rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(z))))
|
||||
(check-equal? (list-enabled rs (set 'x 'y)) '(b))
|
||||
(check-equal? (list-enabled rs (set 'x)) '(a))))
|
||||
|
||||
;;; Applies a reaction system to a set.
|
||||
(: union-products (-> ReactionSystem (Listof ReactionName) (Setof Species)))
|
||||
(define (union-products rs as)
|
||||
(cond
|
||||
[(empty? as) (set)]
|
||||
[else (define products (for/list : (Listof (Setof Species))
|
||||
([a as])
|
||||
(reaction-products (hash-ref rs a))))
|
||||
(apply set-union (assert-type products (NonemptyListof (Setof Species))))]))
|
||||
|
||||
(module+ test
|
||||
(test-case "union-products"
|
||||
(define rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(t))))
|
||||
(check-equal? (union-products rs '(a b))
|
||||
(set 't 'z))
|
||||
(check-equal? (union-products rs '(a))
|
||||
(set 'z))
|
||||
(check-equal? (union-products rs '())
|
||||
(set))))
|
||||
|
||||
(: apply-rs (-> ReactionSystem (Setof Species) (Setof Species)))
|
||||
(define (apply-rs rs s)
|
||||
(let ([as (list-enabled rs s)])
|
||||
(union-products rs as)))
|
||||
|
||||
(module+ test
|
||||
(test-case "Basic definitions"
|
||||
(define r1 (reaction (set 'x) (set 'y) (set 'z)))
|
||||
(define r2 (reaction (set 'x) (set) (set 'y)))
|
||||
(define rs (make-immutable-hash (list (cons 'a r1) (cons 'b r2))))
|
||||
(define s1 (set 'x 'z))
|
||||
(define s2 (set 'x 'y))
|
||||
(check-true (enabled? r1 s1))
|
||||
(check-false (enabled? r1 s2))
|
||||
(check-equal? (list-enabled rs s1) '(a b))
|
||||
(check-equal? (list-enabled rs s2) '(b))
|
||||
(check-equal? (union-products rs '(a b)) (set 'y 'z))
|
||||
(check-equal? (apply-rs rs s1) (set 'y 'z))
|
||||
(check-equal? (apply-rs rs s2) (set 'y))))
|
||||
(test-case "apply-rs"
|
||||
(define rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(t))))
|
||||
(check-equal? (apply-rs rs (set 'x 'y))
|
||||
(set 't))
|
||||
(check-equal? (apply-rs rs (set 'x))
|
||||
(set 'z))))
|
||||
|
||||
|
||||
;;; ====================
|
||||
;;; Org-mode interaction
|
||||
;;; ====================
|
||||
|
||||
;;; This section contains some useful primitives for Org-mode
|
||||
;;; interoperability.
|
||||
|
||||
;;; Converts a triple of strings to a reaction.
|
||||
(: str-triple->reaction (-> (List String String String) Reaction))
|
||||
(define/match (str-triple->reaction lst)
|
||||
[((list str-reactants str-inhibitors str-products))
|
||||
(reaction (list->set (read-symbol-list str-reactants))
|
||||
(list->set (read-symbol-list str-inhibitors))
|
||||
(list->set (read-symbol-list str-products)))])
|
||||
|
||||
;;; Converts a hash table mapping reaction names to triples of strings
|
||||
;;; to a reaction system.
|
||||
(module+ test
|
||||
(test-case "str-triple->reaction"
|
||||
(check-equal? (str-triple->reaction '("a b" "c d" "e f"))
|
||||
(reaction (set 'b 'a) (set 'c 'd) (set 'f 'e)))))
|
||||
|
||||
(: ht-str-triples->rs (-> (HashTable ReactionName (List String String String))
|
||||
ReactionSystem))
|
||||
(define (ht-str-triples->rs ht)
|
||||
(for/hash ([(a triple) (in-hash ht)])
|
||||
(for/hash : (HashTable ReactionName Reaction)
|
||||
([(a triple) (in-hash ht)])
|
||||
(values a (str-triple->reaction triple))))
|
||||
|
||||
(module+ test
|
||||
(test-case "ht-str-triples->rs"
|
||||
(check-equal?
|
||||
(ht-str-triples->rs #hash((a . ("x t" "y" "z"))))
|
||||
(make-immutable-hash (list (cons 'a (reaction (set 'x 't) (set 'y) (set 'z))))))))
|
||||
(check-equal? (ht-str-triples->rs (hash 'a (list "x y" "" "k i")
|
||||
'b (list "" "x y" "t j")))
|
||||
(hash 'a (reaction (set 'y 'x) (set) (set 'k 'i))
|
||||
'b (reaction (set) (set 'y 'x) (set 't 'j))))))
|
||||
|
||||
;;; Reads a reaction system from an Org-mode style string.
|
||||
(define read-org-rs (compose ht-str-triples->rs read-org-variable-mapping))
|
||||
(: read-org-rs (-> String ReactionSystem))
|
||||
(define (read-org-rs str)
|
||||
(ht-str-triples->rs
|
||||
(assert-type (read-org-variable-mapping str)
|
||||
(Immutable-HashTable ReactionName (List String String String)))))
|
||||
|
||||
(module+ test
|
||||
(test-case "read-org-rs"
|
||||
(check-equal? (read-org-rs "((\"a\" \"x t\" \"y\" \"z\") (\"b\" \"x\" \"q\" \"z\"))")
|
||||
(hash
|
||||
'a
|
||||
(reaction (set 'x 't) (set 'y) (set 'z))
|
||||
'b
|
||||
(reaction (set 'x) (set 'q) (set 'z))))))
|
||||
(check-equal?
|
||||
(read-org-rs "((\"a\" \"x t\" \"y\" \"z\") (\"b\" \"x\" \"q\" \"z\"))")
|
||||
(hash 'a (reaction (set 't 'x) (set 'y) (set 'z))
|
||||
'b (reaction (set 'x) (set 'q) (set 'z))))))
|
||||
|
||||
;;; Reads a context sequence from an Org sexp corresponding to a list.
|
||||
(: read-context-sequence (-> String (Listof (Setof Species))))
|
||||
(define (read-context-sequence str)
|
||||
(map (compose list->set read-symbol-list) (flatten (string->any str))))
|
||||
(for/list ([sexp (in-list (flatten (string->any str)))])
|
||||
(list->set (read-symbol-list (assert-type sexp String)))))
|
||||
|
||||
(module+ test
|
||||
(test-case "read-context-sequence"
|
||||
(check-equal? (read-context-sequence "((\"x y\") (\"z\") (\"\") (\"t\"))")
|
||||
(list (set 'x 'y) (set 'z) (set) (set 't)))))
|
||||
|
||||
;;; Converts a reaction to a triple of strings.
|
||||
(: reaction->str-triple (-> Reaction (Listof String)))
|
||||
(define/match (reaction->str-triple r)
|
||||
[((reaction r i p))
|
||||
(map (compose drop-first-last any->string set->list)
|
||||
(list r i p))])
|
||||
(for/list ([c (in-list (list r i p))])
|
||||
(drop-first-last (any->string (set->list c))))])
|
||||
|
||||
;;; Converts a reaction system to a hash table mapping reaction names
|
||||
;;; to triples of strings.
|
||||
(module+ test
|
||||
(test-case "reaction->str-triple"
|
||||
(check-equal? (reaction->str-triple (make-reaction '(x y) '(z t) '(k i)))
|
||||
'("x y" "z t" "i k"))))
|
||||
|
||||
(: rs->ht-str-triples (-> ReactionSystem (HashTable ReactionName (Listof String))))
|
||||
(define (rs->ht-str-triples rs)
|
||||
(for/hash ([(a r) (in-hash rs)])
|
||||
(for/hash : (HashTable ReactionName (Listof String))
|
||||
([(a r) (in-hash rs)])
|
||||
(values a (reaction->str-triple r))))
|
||||
|
||||
(module+ test
|
||||
(test-case "rs->ht-str-triples"
|
||||
(check-equal?
|
||||
(rs->ht-str-triples (make-immutable-hash (list (cons 'a (reaction (set 'x 't) (set 'y) (set 'z))))))
|
||||
#hash((a . ("t x" "y" "z"))))))
|
||||
(define rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(t))))
|
||||
(check-equal? (rs->ht-str-triples rs)
|
||||
(hash 'a (list "x" "y" "z")
|
||||
'b (list "x y" "" "t")))))
|
||||
|
||||
(struct state ([result : (Setof Species)]
|
||||
[rest-contexts : (Listof (Setof Species))])
|
||||
#:transparent
|
||||
#:type-name State)
|
||||
|
||||
;;; ============================
|
||||
;;; Dynamics of reaction systems
|
||||
;;; ============================
|
||||
(define dynamics%
|
||||
(class (inst dds% State (Listof ReactionName))
|
||||
(super-new)
|
||||
(init-field [rs : ReactionSystem])
|
||||
(: step/annotated (-> State (Listof (Pairof (Listof ReactionName) State))))
|
||||
(define/override (step/annotated s)
|
||||
(match s
|
||||
[(state res (cons ctx rest-ctx))
|
||||
(define full-s (set-union ctx res))
|
||||
(define en (list-enabled rs full-s))
|
||||
(list (cons en (state (union-products rs en) rest-ctx)))]
|
||||
[(state _'()) '()]))))
|
||||
|
||||
;;; An interactive process of a reaction system is a sequence of
|
||||
;;; states driven by a sequence of contexts in the following way.
|
||||
;;; The reaction system starts with the initial context. Then, at
|
||||
;;; every step, the result of applying the reaction system is merged
|
||||
;;; with the next element of the context sequence, and the reaction
|
||||
;;; system is then applied to the result of the union. If the
|
||||
;;; sequence of contexts is empty, the reaction system cannot evolve.
|
||||
(define-type Dynamics%
|
||||
(Instance (Class
|
||||
(init (rs ReactionSystem))
|
||||
(field (rs ReactionSystem))
|
||||
(build-state-graph (-> (Listof State) Graph))
|
||||
(build-state-graph*
|
||||
(-> (Listof State) (U 'full Exact-Positive-Integer) Graph))
|
||||
(build-state-graph*/annotated
|
||||
(-> (Listof State) (U 'full Exact-Positive-Integer) Graph))
|
||||
(build-state-graph/annotated (-> (Listof State) Graph))
|
||||
(step (-> State (Listof State)))
|
||||
(step* (-> (Listof State) (Listof State)))
|
||||
(step/annotated (-> State (Listof (Pairof (Listof Variable) State)))))))
|
||||
|
||||
;;; A state of a reaction system is a set of species representing the
|
||||
;;; result of the application of the reactions from the previous
|
||||
;;; steps, plus the rest of the context sequence. When the context
|
||||
;;; sequence is empty, nothing is added to the current state.
|
||||
(struct state (result rest-contexts) #:transparent)
|
||||
(module+ test
|
||||
(test-case "dynamics%:step/annotated"
|
||||
(define rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x))))
|
||||
(define dyn (new dynamics% [rs rs]))
|
||||
(define s0 (state (set 'x 'y)
|
||||
(list (set) (set) (set 'x))))
|
||||
(define-values (_ 3-steps)
|
||||
(for/fold ([last-s : State s0]
|
||||
[trace : (Listof (Pairof (Listof ReactionName) State)) '()])
|
||||
([_ (in-range 1 4)])
|
||||
(define trans (send dyn step/annotated last-s))
|
||||
(values (cdar trans) (append trace trans))))
|
||||
(check-equal? 3-steps
|
||||
(list
|
||||
(cons '(b) (state (set 'x) (list (set) (set 'x))))
|
||||
(cons '(a) (state (set 'z) (list (set 'x))))
|
||||
(cons '(a) (state (set 'z) '()))))))
|
||||
|
||||
;;; The dynamics of the reaction system only stores the reaction
|
||||
;;; system itself.
|
||||
(struct dynamics (rs) #:transparent
|
||||
#:methods gen:dds
|
||||
[;; Since reaction systems are deterministic, a singleton set is
|
||||
;; produced, unless the context sequence is empty, in which case an
|
||||
;; empty set of states is generated. This transition is annotated
|
||||
;; by the list of rules which were enabled in the current step.
|
||||
(define (dds-step-one-annotated dyn st)
|
||||
(define rs (dynamics-rs dyn))
|
||||
(define (apply-rs-annotate s rest-ctx)
|
||||
(define en (list-enabled rs s))
|
||||
(set (cons (list->set en)
|
||||
(state (union-products rs en) rest-ctx))))
|
||||
(match st
|
||||
[(state res (cons ctx rest-ctx))
|
||||
(apply-rs-annotate (set-union res ctx) rest-ctx)]
|
||||
[(state res '()) (set)]))])
|
||||
|
||||
;;; Builds the state graph of a reaction system driven by a given
|
||||
;;; context sequence.
|
||||
(: build-interactive-process-graph (-> ReactionSystem (Listof (Setof Species)) Graph))
|
||||
(define (build-interactive-process-graph rs contexts)
|
||||
(dds-build-state-graph-annotated (dynamics rs)
|
||||
(set (state (set) contexts))))
|
||||
(send (new dynamics% [rs rs])
|
||||
build-state-graph/annotated
|
||||
(list (state (set) contexts))))
|
||||
|
||||
;;; Builds the reduced state graph of a reaction system driven by
|
||||
;;; a given context sequence. Unlike build-interactive-process-graph,
|
||||
;;; the nodes of this state graph do not contain the context sequence.
|
||||
(define (build-reduced-state-graph rs contexts)
|
||||
(module+ test
|
||||
(test-case "build-interactive-process-graph"
|
||||
(define rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x))))
|
||||
(define ctx : (Listof (Setof Species)) (list (set) (set) (set 'x)))
|
||||
(check-equal? (graphviz (build-interactive-process-graph rs ctx))
|
||||
"digraph G {\n\tnode0 [label=\"(state (set) '(#<set: x>))\"];\n\tnode1 [label=\"(state (set 'z) '())\"];\n\tnode2 [label=\"(state (set) '(#<set:> #<set: x>))\"];\n\tnode3 [label=\"(state (set) '(#<set:> #<set:> #<set: x>))\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [label=\"'(a)\"];\n\t\tnode2 -> node0 [label=\"'()\"];\n\t\tnode3 -> node2 [label=\"'()\"];\n\t}\n}\n")))
|
||||
|
||||
(: build-interactive-process-graph/simple-states (-> ReactionSystem (Listof (Setof Species)) Graph))
|
||||
(define (build-interactive-process-graph/simple-states rs contexts)
|
||||
(define sgr (build-interactive-process-graph rs contexts))
|
||||
(weighted-graph/directed
|
||||
(for/list ([e (in-edges sgr)])
|
||||
(define u (car e)) (define v (cadr e))
|
||||
(define u (assert-type (car e) State))
|
||||
(define v (assert-type (cadr e) State))
|
||||
(list (edge-weight sgr u v) (state-result u) (state-result v)))))
|
||||
|
||||
(module+ test
|
||||
(test-case "build-reduced-state-graph"
|
||||
(define rs (hash 'a (reaction (set 'x) (set 'y) (set 'z))
|
||||
'b (reaction (set 'x) (set) (set 'y))))
|
||||
(define ctx (list (set 'x) (set 'y) (set 'z) (set) (set 'z)))
|
||||
(check-equal? (graphviz (build-reduced-state-graph rs ctx))
|
||||
"digraph G {\n\tnode0 [label=\"(set)\\n\"];\n\tnode1 [label=\"(set 'y 'z)\\n\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t\tnode0 -> node0 [label=\"#<set: #<set:>>\"];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [label=\"#<set: #<set: a b>>\"];\n\t\tnode1 -> node0 [label=\"#<set: #<set:>>\"];\n\t}\n}\n")))
|
||||
(test-case "build-interactive-process-graph/simple-states"
|
||||
(define rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x))))
|
||||
(define ctx : (Listof (Setof Species)) (list (set) (set) (set 'x)))
|
||||
(check-equal? (graphviz (build-interactive-process-graph/simple-states rs ctx))
|
||||
"digraph G {\n\tnode0 [label=\"(set)\"];\n\tnode1 [label=\"(set 'z)\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t\tnode0 -> node0 [label=\"'()\"];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [label=\"'(a)\"];\n\t}\n}\n")))
|
||||
|
||||
(define (pretty-print-reduced-state-graph sgr)
|
||||
(update-graph sgr
|
||||
#:v-func (λ (st) (~a "{" (pretty-print-set st) "}"))
|
||||
#:e-func pretty-print-set-sets))
|
||||
(: pretty-print-state-graph/simple-states (-> Graph Graph))
|
||||
(define (pretty-print-state-graph/simple-states sgr)
|
||||
(update-graph
|
||||
sgr
|
||||
#:v-func
|
||||
(λ (st) (~a "{" (pretty-print-set (assert-type st (Setof Species))) "}"))
|
||||
#:e-func
|
||||
(λ (e) (pretty-print-set (assert-type e (Listof ReactionName))))))
|
||||
|
||||
(module+ test
|
||||
(test-case "pretty-print-reduced-graph"
|
||||
(define rs (hash 'a (reaction (set 'x) (set 'y) (set 'z))
|
||||
'b (reaction (set 'x) (set) (set 'y))))
|
||||
(define ctx (list (set 'x) (set 'y) (set 'z) (set) (set 'z)))
|
||||
(define sgr (build-reduced-state-graph rs ctx))
|
||||
(graphviz (pretty-print-reduced-state-graph sgr))))
|
||||
(test-case "pretty-print-state-graph/simple-states"
|
||||
(define rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x))))
|
||||
(define ctx : (Listof (Setof Species)) (list (set) (set) (set 'x)))
|
||||
(check-equal?
|
||||
(graphviz (pretty-print-state-graph/simple-states
|
||||
(build-interactive-process-graph/simple-states rs ctx)))
|
||||
"digraph G {\n\tnode0 [label=\"{}\"];\n\tnode1 [label=\"{z}\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t\tnode0 -> node0 [label=\"\"];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [label=\"a\"];\n\t}\n}\n")
|
||||
))
|
||||
|
||||
|
||||
;;; Builds the interactive process driven by the given context
|
||||
;;; sequence. The output is a list of pairs of lists in which the
|
||||
;;; first element is the current context and the second element is the
|
||||
;;; result of the application of reactions to the previous state. The
|
||||
;;; interactive process stops one step after the end of the context
|
||||
;;; sequence, to show the effect of the last context.
|
||||
(: build-interactive-process (-> ReactionSystem (Listof (Setof Species))
|
||||
(Listof (Pairof (Setof Species) (Setof Species)))))
|
||||
(define (build-interactive-process rs contexts)
|
||||
(let ([dyn (dynamics rs)]
|
||||
[padded-contexts (append contexts (list (set)))])
|
||||
(for/fold ([proc '()]
|
||||
[st (state (set) padded-contexts)]
|
||||
#:result (reverse proc))
|
||||
([c padded-contexts])
|
||||
(values
|
||||
(cons (match st
|
||||
[(state res ctx)
|
||||
(list (if (empty? ctx) (set) (car ctx)) res)])
|
||||
proc)
|
||||
(set-first (dds-step-one dyn st))))))
|
||||
(define dyn (new dynamics% [rs rs]))
|
||||
(define padded-contexts
|
||||
(append contexts (list (assert-type (set) (Setof Species)))))
|
||||
(for/fold ([proc : (Listof (Pairof (Setof Species) (Setof Species))) '()]
|
||||
[st : State (state (set) padded-contexts)]
|
||||
#:result (reverse proc))
|
||||
([c padded-contexts])
|
||||
(define res (state-result st))
|
||||
(define ctx (state-rest-contexts st))
|
||||
(values
|
||||
((inst cons (Pairof (Setof Species) (Setof Species)))
|
||||
(cons (if (empty? ctx) (assert-type (set) (Setof Species)) (car ctx)) res)
|
||||
proc)
|
||||
(set-first (send dyn step st)))))
|
||||
|
||||
;;; Pretty-prints the context sequence and the current result of a
|
||||
;;; state of the reaction system. Note that we need to keep the full
|
||||
;;; context sequence in the name of each state to avoid confusion
|
||||
;;; between the states at different steps of the evolution.
|
||||
(module+ test
|
||||
(test-case "build-interactive-process"
|
||||
(define rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x))))
|
||||
(define ctx : (Listof (Setof Species)) (list (set 'x 'y) (set) (set 'x) (set)))
|
||||
(check-equal? (build-interactive-process rs ctx)
|
||||
(list
|
||||
(cons (set 'y 'x) (set))
|
||||
(cons (set) (set 'x))
|
||||
(cons (set 'x) (set 'z))
|
||||
(cons (set) (set 'z))
|
||||
(cons (set) (set))))))
|
||||
|
||||
(: build-interactive-process/org (-> ReactionSystem (Listof (Setof Species))
|
||||
(Listof (Listof (Setof Species)))))
|
||||
(define (build-interactive-process/org rs context)
|
||||
(for/list : (Listof (Listof (Setof Species)))
|
||||
([p (build-interactive-process rs context)])
|
||||
(list (car p) (cdr p))))
|
||||
|
||||
(module+ test
|
||||
(test-case "build-interactive-process/org"
|
||||
(define rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x))))
|
||||
(define ctx : (Listof (Setof Species)) (list (set 'x 'y) (set) (set 'x) (set)))
|
||||
(check-equal? (build-interactive-process/org rs ctx)
|
||||
(list
|
||||
(list (set 'y 'x) (set))
|
||||
(list (set) (set 'x))
|
||||
(list (set 'x) (set 'z))
|
||||
(list (set) (set 'z))
|
||||
(list (set) (set))))))
|
||||
|
||||
(: pretty-print-state (-> State String))
|
||||
(define/match (pretty-print-state st)
|
||||
[((state res ctx))
|
||||
(format "C:~a\nD:{~a}" (pretty-print-set-sets ctx) (pretty-print-set res))])
|
||||
|
||||
;;; Pretty prints the state graph of a reaction system.
|
||||
(module+ test
|
||||
(test-case "pretty-print-state"
|
||||
(check-equal? (pretty-print-state
|
||||
(state (set 'x 'y) (list (set 'z) (set) (set 'x))))
|
||||
"C:{z}{}{x}\nD:{x y}")))
|
||||
|
||||
(: pretty-print-state-graph (-> Graph Graph))
|
||||
(define (pretty-print-state-graph sgr)
|
||||
(update-graph sgr #:v-func pretty-print-state #:e-func pretty-print-set-sets))
|
||||
(update-graph
|
||||
sgr
|
||||
#:v-func (λ (st) (pretty-print-state (assert-type st State)))
|
||||
#:e-func (λ (e) (pretty-print-set (assert-type e (Listof ReactionName))))))
|
||||
|
||||
(module+ test
|
||||
(test-case "Dynamics of reaction systems"
|
||||
(define r1 (reaction (set 'x) (set 'y) (set 'z)))
|
||||
(define r2 (reaction (set 'x) (set) (set 'y)))
|
||||
(define rs (make-immutable-hash (list (cons 'a r1) (cons 'b r2))))
|
||||
(define dyn (dynamics rs))
|
||||
(define state1 (state (set) (list (set 'x) (set 'y) (set 'z) (set) (set 'z))))
|
||||
(define sgr (dds-build-state-graph-annotated dyn (set state1)))
|
||||
(define ip (build-interactive-process-graph rs (list (set 'x) (set 'y) (set 'z) (set) (set 'z))))
|
||||
|
||||
(check-equal? (dds-step-one-annotated dyn state1)
|
||||
(set (cons
|
||||
(set 'a 'b)
|
||||
(state (set 'y 'z) (list (set 'y) (set 'z) (set) (set 'z))))))
|
||||
(check-equal? (dds-step-one dyn state1)
|
||||
(set (state (set 'y 'z) (list (set 'y) (set 'z) (set) (set 'z)))))
|
||||
|
||||
(check-true (has-vertex? sgr (state (set 'y 'z) (list (set 'y) (set 'z) (set) (set 'z)))))
|
||||
(check-true (has-vertex? sgr (state (set) (list (set 'z) (set) (set 'z)))))
|
||||
(check-true (has-vertex? sgr (state (set) (list (set) (set 'z)))))
|
||||
(check-true (has-vertex? sgr (state (set) (list (set 'z)))))
|
||||
(check-true (has-vertex? sgr (state (set) (list (set 'x) (set 'y) (set 'z) (set) (set 'z)))))
|
||||
(check-true (has-vertex? sgr (state (set) '())))
|
||||
|
||||
(check-false (has-edge? sgr
|
||||
(state (set) '())
|
||||
(state (set) '())))
|
||||
(check-equal? (edge-weight sgr
|
||||
(state (set 'y 'z) (list (set 'y) (set 'z) (set) (set 'z)))
|
||||
(state (set) (list (set 'z) (set) (set 'z))))
|
||||
(set (set)))
|
||||
(check-equal? (edge-weight sgr
|
||||
(state (set) (list (set 'z) (set) (set 'z)))
|
||||
(state (set) (list (set) (set 'z))))
|
||||
(set (set)))
|
||||
(check-equal? (edge-weight sgr
|
||||
(state (set) (list (set) (set 'z)))
|
||||
(state (set) (list (set 'z))))
|
||||
(set (set)))
|
||||
(check-equal? (edge-weight sgr
|
||||
(state (set) (list (set 'z)))
|
||||
(state (set) '()))
|
||||
(set (set)))
|
||||
(check-equal? (edge-weight sgr
|
||||
(state (set) (list (set 'x) (set 'y) (set 'z) (set) (set 'z)))
|
||||
(state (set 'y 'z) (list (set 'y) (set 'z) (set) (set 'z))))
|
||||
(set (set 'a 'b)))
|
||||
|
||||
(check-equal? sgr ip)
|
||||
|
||||
(check-equal? (build-interactive-process rs (list (set 'x) (set 'y) (set 'z) (set) (set 'z)))
|
||||
(list
|
||||
(list (set 'x) (set))
|
||||
(list (set 'y) (set 'y 'z))
|
||||
(list (set 'z) (set))
|
||||
(list (set) (set))
|
||||
(list (set 'z) (set))
|
||||
(list (set) (set))))))
|
||||
(test-case "pretty-print-state-graph"
|
||||
(define rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x))))
|
||||
(define ctx : (Listof (Setof Species)) (list (set) (set) (set 'x)))
|
||||
(check-equal? (graphviz (build-interactive-process-graph rs ctx))
|
||||
"digraph G {\n\tnode0 [label=\"(state (set) '(#<set: x>))\"];\n\tnode1 [label=\"(state (set 'z) '())\"];\n\tnode2 [label=\"(state (set) '(#<set:> #<set: x>))\"];\n\tnode3 [label=\"(state (set) '(#<set:> #<set:> #<set: x>))\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [label=\"'(a)\"];\n\t\tnode2 -> node0 [label=\"'()\"];\n\t\tnode3 -> node2 [label=\"'()\"];\n\t}\n}\n")
|
||||
))
|
||||
|
|
|
@ -26,5 +26,7 @@ dds currently includes the following modules:
|
|||
|
||||
@include-section["utils.scrbl"]
|
||||
@include-section["functions.scrbl"]
|
||||
@include-section["dynamics.scrbl"]
|
||||
@include-section["networks.scrbl"]
|
||||
@include-section["tbn.scrbl"]
|
||||
@include-section["rs.scrbl"]
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
#lang scribble/manual
|
||||
|
||||
@(require scribble/example racket/sandbox
|
||||
(for-label "../dynamics.rkt" typed/racket/base
|
||||
(only-in racket/class object%)))
|
||||
|
||||
@(define-syntax-rule (deftypeform . args)
|
||||
(defform #:kind "type" . args))
|
||||
|
||||
@(define-syntax-rule (deftype . args)
|
||||
(defidform #:kind "polymorphic type" . args))
|
||||
|
||||
@title[#:tag "dynamics"]{dds/dynamics: Dynamics of DDS}
|
||||
|
||||
@defmodule[dds/dynamics]
|
||||
|
||||
This module provides a number of general definitions for building and analyzing
|
||||
the dynamics of discrete dynamical systems.
|
||||
|
||||
@defclass[dds% object% ()]{
|
||||
|
||||
The abstract base class for discrete dynamical systems.
|
||||
|
||||
This class has two type parameters:
|
||||
|
||||
@itemize[
|
||||
|
||||
@item{@racket[State] --- a state of the discrete dynamical system,}
|
||||
|
||||
@item{@racket[Modality] --- a description of the way in which the discrete
|
||||
dynamical system transitions from a given state @italic{s} to another state
|
||||
@italic{s}. For systems whose states are described by a set of variables,
|
||||
a @racket[Modality] is typically a list of variables updated during the
|
||||
state transition.}
|
||||
|
||||
]
|
||||
|
||||
@defmethod[(step [st State]) (Listof State)]{
|
||||
|
||||
Given a state @racket[st], produces the next states of the state.
|
||||
|
||||
This method falls back to calling @method[dds% step/annotated], and then
|
||||
discarding the annotations.
|
||||
|
||||
}
|
||||
|
||||
@defmethod[(step/annotated [st State]) (Listof (Pairof Modality State))]{
|
||||
|
||||
Given a state, produces the next states paired with the corresponding
|
||||
modalities. Typical usage would include giving the information about the
|
||||
update mode.
|
||||
|
||||
This method has no fallback and must be overridden.
|
||||
|
||||
}
|
||||
|
||||
@defmethod[(step* [sts (Listof State)]) (Listof State)]{
|
||||
|
||||
Given a set of starting states, produce the set of states reachable in
|
||||
one step.
|
||||
|
||||
This method falls back to running @method[dds% step] for all states.
|
||||
|
||||
Note that @method[dds% step*] has no direct @tt{/annotated} counterpart.
|
||||
This is because producing a list of @racket[(Pairof Modality State)] would not
|
||||
give enough information to identify to which particular transition the modality
|
||||
corresponds to.
|
||||
|
||||
}
|
||||
|
||||
@defmethod[(build-state-graph [sts (Listof State)]) Graph]{
|
||||
|
||||
Given a set of starting states, produces the state graph reachable from the
|
||||
starting states.
|
||||
|
||||
This method falls back to exploring the state graph with @method[dds% step].
|
||||
|
||||
}
|
||||
|
||||
@defmethod[(build-state-graph/annotated [sts (Listof State)]) Graph]{
|
||||
|
||||
Given a set of starting states, produces the labelled state graph reachable
|
||||
from the starting states.
|
||||
|
||||
This method falls back to exploring the state graph with @method[dds%
|
||||
step/annotated].
|
||||
|
||||
}
|
||||
|
||||
@defmethod[(build-state-graph* [sts (Listof State)]
|
||||
[nsteps (U Positive-Integer 'full)])
|
||||
Graph]{
|
||||
|
||||
Given a set of starting states and a number @racket[nsteps] of steps to run,
|
||||
produces the state graph reachable from the starting states @racket[nsteps]
|
||||
steps. If @racket[nsteps] is @racket['full], constructs the full state
|
||||
graph instead.
|
||||
|
||||
This method falls back to exploring the state graph with @method[dds% step].
|
||||
|
||||
}
|
||||
|
||||
@defmethod[(build-state-graph*/annotated [sts (Listof State)]
|
||||
[nsteps (U Positive-Integer 'full)])
|
||||
Graph]{
|
||||
|
||||
Given a set of starting states and a number @racket[nsteps] of steps to run,
|
||||
produces the labelled state graph reachable from the starting states
|
||||
@racket[nsteps] steps. If @racket[nsteps] is @racket['full], constructs the
|
||||
full state graph instead.
|
||||
|
||||
This method falls back to exploring the state graph with @method[dds%
|
||||
step/annotated].
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,20 @@
|
|||
#lang scribble/manual
|
||||
@(require (for-label racket "../functions.rkt" "../utils.rkt"))
|
||||
@(require scribble/example racket/sandbox
|
||||
(for-label typed/racket/base "../functions.rkt" dds/utils
|
||||
typed/racket/unsafe
|
||||
(only-in racket stream->list stream-first)))
|
||||
|
||||
@(define functions-evaluator
|
||||
(parameterize ([sandbox-output 'string]
|
||||
[sandbox-error-output 'string]
|
||||
[sandbox-memory-limit 500])
|
||||
(make-evaluator 'typed/racket #:requires '("functions.rkt"))))
|
||||
|
||||
@(define-syntax-rule (ex . args)
|
||||
(examples #:eval functions-evaluator . args))
|
||||
|
||||
@(define-syntax-rule (deftype . args)
|
||||
(defidform #:kind "type" . args))
|
||||
|
||||
@title[#:tag "functions"]{dds/functions: Formal Functions}
|
||||
|
||||
|
@ -10,10 +25,707 @@ This modules provides some definitions for working with functions: tabulating,
|
|||
Some definitions of particular kinds of functions are also provided (threshold
|
||||
Boolean functions, etc.).
|
||||
|
||||
@section{Tabulating functions}
|
||||
@section[#:tag "pseudovariadic"]{Pseudovariadic functions}
|
||||
|
||||
Functions for @seclink["tabulating"]{tabulating functions} take as an argument
|
||||
a function to tabulate or a list of functions to tabulate. Writing the type of
|
||||
such functions in Typed Racket and generalizing on the number of the arguments
|
||||
is hard, and using functions with such types seems even harder.
|
||||
The @seclink["tabulating"]{following section} contains some examples,
|
||||
illustrating among other things the difficulties of typing
|
||||
tabulating functions.
|
||||
|
||||
The type of @racket[apply] does not help in this situation, because Typed
|
||||
Racket treats @racket[apply] in
|
||||
@hyperlink["https://racket.discourse.group/t/replicating-the-type-of-apply/770/3"]{a
|
||||
special way}. This means that a user-defined function with the same type as
|
||||
@racket[apply] and directly calling it will not work in the same way.
|
||||
|
||||
@ex[
|
||||
apply
|
||||
(define myapply apply)
|
||||
myapply
|
||||
(apply (λ (x y) (and x y)) '(#t #f))
|
||||
(eval:error (myapply (λ (x y) (and x y)) '(#t #f)))
|
||||
]
|
||||
|
||||
One way to work around this issue is to write functions which disguise as
|
||||
variadic functions of type @racket[(-> a * b)], but which throw an exception
|
||||
when they receive a number of arguments different from a given constant value.
|
||||
Such functions are called @italic{pseudovariadic functions} in
|
||||
this documentation.
|
||||
|
||||
@deftogether[(@defform[(pseudovariadic-lambda (id ...+) body ...+)]
|
||||
@defform[(pvλ (id ...+) body ...+)])]{
|
||||
|
||||
Define a pseudovariadic anonymous function.
|
||||
|
||||
@ex[
|
||||
(: f (-> Boolean * Boolean))
|
||||
(define f (pseudovariadic-lambda (x y) (and x y)))
|
||||
(f #t #f)
|
||||
(eval:error (f #t #f #t))
|
||||
]}
|
||||
|
||||
@deftogether[(@defform[(pseudovariadic-define (name id ...+) body ...+)]
|
||||
@defform[(pvdefine (id ...+) body ...+)])]{
|
||||
|
||||
Define a pseudovariadic function called @racket[name].
|
||||
|
||||
@ex[
|
||||
(: g (-> Boolean * Boolean))
|
||||
(pseudovariadic-define (g x y) (and x y))
|
||||
(g #t #f)
|
||||
(eval:error (g #t #f #t))
|
||||
]}
|
||||
@section[#:tag "tabulating"]{Tabulating functions}
|
||||
|
||||
@defproc[(tabulate [func (-> a ... b)]
|
||||
[doms (List (Listof a) ... a)])
|
||||
(Listof (Listof (U Any b)))]{
|
||||
|
||||
Given a function @racket[func] and a list of domains @racket[doms] for each of
|
||||
its arguments, in order, produces a list of lists giving the values of
|
||||
arguments and the value of the functions for these inputs.
|
||||
|
||||
@ex[
|
||||
(tabulate (λ (x y) (and x y)) '((#f #t) (#f #t)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate/strict [func (-> a ... b)]
|
||||
[doms (List (Listof a) ... a)])
|
||||
(Listof (List (List a ...) (Listof b)))]{
|
||||
|
||||
Like @racket[tabulate], but the types of the arguments of @racket[func]
|
||||
explicitly appear in the return type.
|
||||
|
||||
As of 2022-03-06, I am not able to write the type of a list first containing
|
||||
elements of types @racket[a ...], followed by an element of type @racket[b].
|
||||
This is why this function returns a list of lists, each containing first a list
|
||||
of inputs, and then the output of @racket[func].
|
||||
|
||||
@ex[
|
||||
(tabulate/strict (λ (x y) (and x y)) '((#f #t) (#f #t)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate/pv [func (-> a * b)]
|
||||
[doms (Listof (Listof a))])
|
||||
(Listof (Listof (U a b)))]{
|
||||
|
||||
Like @racket[tabulate], but @racket[func]
|
||||
@seclink["pseudovariadic"]{pseudovariadic}.
|
||||
|
||||
@ex[
|
||||
(tabulate/pv (pvλ (x y) (and x y)) '((#f #t) (#f #t)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate/list [func (-> (Listof a) b)]
|
||||
[doms (Listof (Listof a))])
|
||||
(Listof (Listof (U a b)))]{
|
||||
|
||||
Like @racket[tabulate/list], but @racket[func] takes its arguments as a list.
|
||||
|
||||
@ex[
|
||||
(tabulate/list (λ ([xs : (Listof Boolean)])
|
||||
(and (car xs) (car xs)))
|
||||
'((#f #t) (#f #t)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate* [funcs (Listof (-> a ... b))]
|
||||
[doms (List (Listof a) ... a)])
|
||||
(Listof (Listof (U Any b)))]{
|
||||
|
||||
Like @racket[tabulate], but @racket[funcs] is a list of functions taking the
|
||||
same arguments over the same domains.
|
||||
|
||||
@ex[
|
||||
(tabulate* (list (λ (x y) (and x y))
|
||||
(λ (x y) (or x y)))
|
||||
'((#f #t) (#f #t)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate*/strict [funcs (Listof (-> a ... b))]
|
||||
[doms (List (Listof a) ... a)])
|
||||
(Listof (List (List a ...) (Listof b)))]{
|
||||
|
||||
Like @racket[tabulate*], but the types of the arguments of the functions
|
||||
explicitly appear in the return type.
|
||||
|
||||
As of 2022-03-06, I am not able to write the type of a list first containing
|
||||
elements of types @racket[a ...], followed by a list of elements of type
|
||||
@racket[b]. This is why this function returns a list of lists, each containing
|
||||
first a list of inputs, and then the list of outputs of @racket[funcs].
|
||||
|
||||
@ex[
|
||||
(tabulate*/strict (list (λ (x y) (and x y))
|
||||
(λ (x y) (or x y)))
|
||||
'((#f #t) (#f #t)))
|
||||
]
|
||||
|
||||
The result of @racket[tabulate*] can be obtained by applying
|
||||
@racket[append-lists]:
|
||||
|
||||
@ex[
|
||||
(require (only-in "utils.rkt" append-lists))
|
||||
(append-lists (tabulate*/strict (list (λ (x y) (and x y))
|
||||
(λ (x y) (or x y)))
|
||||
'((#f #t) (#f #t))))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate*/pv [funcs (Listof (-> a * b))]
|
||||
[doms (Listof (Listof a))])
|
||||
(Listof (Listof (U a b)))]{
|
||||
|
||||
Like @racket[tabulate*], but the functions in @racket[funcs]
|
||||
are @seclink["pseudovariadic"]{pseudovariadic}.
|
||||
|
||||
@ex[
|
||||
(tabulate*/pv (list (pvλ (x y) (and x y))
|
||||
(pvλ (x y) (or x y)))
|
||||
'((#f #t) (#f #t)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate*/list [funcs (Listof (-> (Listof a) b))]
|
||||
[doms (Listof (Listof a))])
|
||||
(Listof (Listof (U a b)))]{
|
||||
|
||||
Like @racket[tabulate*], but the functions in @racket[funcs] take their
|
||||
arguments as a list.
|
||||
|
||||
@ex[
|
||||
(tabulate*/list (list (λ ([xs : (Listof Boolean)])
|
||||
(and (car xs) (cadr xs)))
|
||||
(λ ([xs : (Listof Boolean)])
|
||||
(or (car xs) (cadr xs))))
|
||||
'((#f #t) (#f #t)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate/pv/boolean [arity Positive-Integer] [func (-> Boolean * Boolean)])
|
||||
(Listof (Listof Boolean))]{
|
||||
|
||||
Like @racket[tabulate/pv], but assumes the domains of all variables of the
|
||||
function are Boolean. The arity of @racket[func] must be explicitly supplied.
|
||||
|
||||
@ex[
|
||||
(tabulate/pv/boolean 2 (pvλ (x y) (and x y)))
|
||||
]
|
||||
|
||||
Explicitly supplying the arity is necessary because the actual arity of
|
||||
a pseudovariadic function cannot be determined programmatically. Note that
|
||||
@racket[tabulate] can be applied directly to a function, but the type of
|
||||
@racket[tabulate] requires a cast is required the domains argument
|
||||
@racket[doms].
|
||||
|
||||
@ex[
|
||||
(tabulate (λ (x y) (and x y))
|
||||
(cast (make-list 2 '(#f #t))
|
||||
(List (Listof Boolean) (Listof Boolean))))
|
||||
]
|
||||
|
||||
This cast is what makes it necessary to resort to pseudovariadic functions and
|
||||
explicit @racket[arity] to be able to write a type for
|
||||
@racket[tabulate/pv/boolean].
|
||||
|
||||
See also @secref{fuctions/untyped} for simpler, but untyped version of
|
||||
this function.
|
||||
|
||||
}
|
||||
|
||||
@defproc[(tabulate*/pv/boolean [arity Positive-Integer]
|
||||
[func (Listof (-> Boolean * Boolean))])
|
||||
(Listof (Listof Boolean))]{
|
||||
|
||||
Like @racket[tabulate/pv/boolean], but takes a list of functions of the
|
||||
same arity.
|
||||
|
||||
@ex[
|
||||
(tabulate*/pv/boolean 2 (list (pvλ (x y) (and x y))
|
||||
(pvλ (x y) (or x y))))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate/pv/01 [arity Positive-Integer] [func (-> (U Zero One) * (U Zero One))])
|
||||
(Listof (Listof (U Zero One)))]{
|
||||
|
||||
Like @racket[tabulate/pv], but assumes the domains of all variables of the
|
||||
function are @tt{{0,1}}. The arity of @racket[func] must be
|
||||
explicitly supplied.
|
||||
|
||||
@ex[
|
||||
(tabulate/pv/01 2 (pvλ (x y)
|
||||
(cast (modulo (+ x y) 2)
|
||||
(U Zero One))))
|
||||
]
|
||||
|
||||
See @racket[tabulate/pv/boolean] for an explanation of the explicit
|
||||
@racket[arity] argument.
|
||||
|
||||
}
|
||||
|
||||
@defproc[(tabulate*/pv/01 [arity Positive-Integer]
|
||||
[func (Listof (-> (U Zero One) * (U Zero One)))])
|
||||
(Listof (Listof (U Zero One)))]{
|
||||
|
||||
Like @racket[tabulate/pv/01], but takes a list of functions of the same arity.
|
||||
|
||||
@ex[
|
||||
(tabulate*/pv/01 2 `(,(pvλ (x y) (cast (min x y) (U Zero One)))
|
||||
,(pvλ (x y) (cast (max x y) (U Zero One)))))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate/list/boolean [arity Positive-Integer]
|
||||
[func (-> (Listof Boolean) Boolean)])
|
||||
(Listof (Listof Boolean))]{
|
||||
|
||||
Like @racket[tabulate/list], but assumes the domains of all variables of the
|
||||
function are Boolean.
|
||||
|
||||
@ex[
|
||||
(tabulate/list/boolean 2 (λ (xs) (and (car xs) (cadr xs))))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate*/list/boolean [arity Positive-Integer]
|
||||
[funcs (Listof (-> (Listof Boolean) Boolean))])
|
||||
(Listof (Listof Boolean))]{
|
||||
|
||||
Like @racket[tabulate*/list], but assumes the domains of all variables of the
|
||||
function are Boolean.
|
||||
|
||||
@ex[
|
||||
(tabulate*/list/boolean 2 (list (λ (xs) (and (car xs) (cadr xs)))
|
||||
(λ (xs) (or (car xs) (cadr xs)))))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate/list/01 [arity Positive-Integer]
|
||||
[func (-> (Listof (U Zero One)) (U Zero One))])
|
||||
(Listof (Listof (U Zero One)))]{
|
||||
|
||||
Like @racket[tabulate/list], but assumes the domains of all variables of the
|
||||
function are @tt{{0,1}}.
|
||||
|
||||
@ex[
|
||||
(tabulate/list/01 2 (λ (xs)
|
||||
(cast (modulo (+ (car xs) (cadr xs)) 2) (U Zero One))))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate*/list/01 [arity Positive-Integer]
|
||||
[funcs (Listof (-> (Listof (U Zero One)) (U Zero One)))])
|
||||
(Listof (Listof (U Zero One)))]{
|
||||
|
||||
Like @racket[tabulate*/list], but assumes the domains of all variables of the
|
||||
function are @tt{{0,1}}.
|
||||
|
||||
@ex[
|
||||
(tabulate*/list/01
|
||||
2
|
||||
`(,(λ (xs) (cast (min (car xs) (cadr xs)) (U Zero One)))
|
||||
,(λ (xs) (cast (max (car xs) (cadr xs)) (U Zero One)))))
|
||||
]}
|
||||
|
||||
@section{Constructing functions}
|
||||
|
||||
@defproc[(table->function/list [table (Listof (Listof a))])
|
||||
(-> (Listof a) a)]{
|
||||
|
||||
Given a table like the one produced by the functions of the @racket[tabulate]
|
||||
family, creates a function which has this behaviour.
|
||||
|
||||
More precisely, given a line of @racket[table] without its last element, the
|
||||
function returned by @racket[table->function/list] produces the corresponding
|
||||
last element.
|
||||
|
||||
@ex[
|
||||
(define tab : (Listof (Listof Boolean))
|
||||
'((#f #f #f)
|
||||
(#f #t #f)
|
||||
(#t #f #f)
|
||||
(#t #t #t)))
|
||||
(define and/list (table->function/list tab))
|
||||
(and/list '(#f #t))
|
||||
(and/list '(#t #t))
|
||||
]}
|
||||
|
||||
@defproc[(table->unary-function [table (Listof (List a b))])
|
||||
(-> a b)]{
|
||||
|
||||
Like @racket[table->function/list], but the @racket[table] contains
|
||||
exactly 2 columns: one column for the inputs and one column for the
|
||||
outputs, and the result is a unary function.
|
||||
|
||||
@ex[
|
||||
(let ([unary-negation (table->unary-function '((#t #f) (#f #t)))])
|
||||
(unary-negation #t))
|
||||
]}
|
||||
|
||||
@defproc[(table->function [table (Listof (Listof a))])
|
||||
(-> a * a)]{
|
||||
|
||||
Like @racket[table->function/list], but the resulting function takes a variable
|
||||
number of arguments rather than a list of values.
|
||||
|
||||
@ex[
|
||||
(define my-and (table->function tab))
|
||||
(my-and #f #t)
|
||||
(my-and #t #t)
|
||||
]}
|
||||
|
||||
@defproc[(table->function/pv [table (Listof (Listof a))])
|
||||
(-> a * a)]{
|
||||
|
||||
Like @racket[table->function], but the resulting function raises an explicit
|
||||
error about invalid arity, instead of the @racket[hash-ref]-related error
|
||||
raised by the function returned by @racket[table->function]. In other words,
|
||||
the returned by @racket[table->function/pv] is
|
||||
@seclink["pseudovariadic"]{pseudovariadic}.
|
||||
|
||||
@ex[
|
||||
(define my-and/pv (table->function/pv tab))
|
||||
(my-and/pv #f #t)
|
||||
(eval:error (my-and/pv #f))
|
||||
(eval:error (my-and #f))
|
||||
]}
|
||||
|
||||
@defproc[(enumerate-boolean-tables [n Positive-Integer])
|
||||
(Sequenceof (Listof (Listof Boolean)))]{
|
||||
|
||||
Returns the stream of the truth tables of all Boolean functions of
|
||||
arity @racket[n].
|
||||
|
||||
There are @tt{2^(2^n)} Boolean functions of arity @racket[n].
|
||||
|
||||
@ex[
|
||||
(require typed/racket/stream)
|
||||
(stream->list (enumerate-boolean-tables 1))
|
||||
]}
|
||||
|
||||
@defproc[(enumerate-boolean-functions [n Positive-Integer])
|
||||
(Sequenceof (-> Boolean * Boolean))]{
|
||||
|
||||
Returns the stream of all Boolean functions of a given arity @racket[n].
|
||||
|
||||
There are @tt{2^(2^n)} Boolean functions of arity @racket[n].
|
||||
|
||||
@ex[
|
||||
(length (stream->list (enumerate-boolean-functions 2)))
|
||||
]}
|
||||
|
||||
@defproc[(enumerate-boolean-functions/pv [n Positive-Integer])
|
||||
(Sequenceof (-> Boolean * Boolean))]{
|
||||
|
||||
Like @racket[enumerate-boolean-functions], but the returned functions are
|
||||
@seclink["pseudovariadic"]{pseudovariadic}.
|
||||
|
||||
@ex[
|
||||
(define bool-f1/pv (stream-first (enumerate-boolean-functions/pv 2)))
|
||||
(bool-f1/pv #f #f)
|
||||
(eval:error (bool-f1/pv #f))
|
||||
]}
|
||||
|
||||
@defproc[(enumerate-boolean-functions/list
|
||||
[n Positive-Integer])
|
||||
(Sequenceof (-> (Listof Boolean) Boolean))]{
|
||||
|
||||
Like @racket[enumerate-boolean-functions], but the returned functions take
|
||||
their arguments as a single list.
|
||||
|
||||
@ex[
|
||||
(define bool-f1/list (stream-first (enumerate-boolean-functions/list 2)))
|
||||
(bool-f1/list '(#f #f))
|
||||
]}
|
||||
|
||||
@section{Random functions}
|
||||
|
||||
@section{Threshold Boolean functions}
|
||||
@defproc[(random-boolean-table [n Positive-Integer]) (Listof (Listof Boolean))]{
|
||||
|
||||
Generates a random truth table for a Boolean function of arity @racket[n].
|
||||
|
||||
@ex[
|
||||
(random-boolean-table 2)
|
||||
]}
|
||||
|
||||
@defproc[(random-boolean-function [n Positive-Integer]) (-> Boolean * Boolean)]{
|
||||
|
||||
Generates a random Boolean function of arity @racket[n].
|
||||
|
||||
@ex[
|
||||
(define random-bool-f (random-boolean-function 2))
|
||||
(random-bool-f #t #f)
|
||||
]}
|
||||
|
||||
@defproc[(random-boolean-function/list [n Positive-Integer]) (-> (Listof Boolean) Boolean)]{
|
||||
|
||||
Like @racket[random-boolean-function], but the constructed function takes
|
||||
a list of arguments.
|
||||
|
||||
@ex[
|
||||
(define random-bool-f/list (random-boolean-function/list 2))
|
||||
(random-bool-f/list '(#t #f))
|
||||
]}
|
||||
|
||||
@section[#:tag "tbf"]{Threshold Boolean functions}
|
||||
|
||||
@defstruct*[tbf ([weights (Vectorof Real)] [threshold Real])]{
|
||||
|
||||
A threshold Boolean function (TBF) is a pair @tt{(w, θ)}, where @tt{w} is
|
||||
a vector of weights and @tt{θ} is the threshold.
|
||||
|
||||
Instances of @racket[tbf] have the type @racket[TBF].
|
||||
|
||||
}
|
||||
|
||||
@deftype[TBF]{
|
||||
|
||||
The type of the instances of @racket[tbf]:
|
||||
|
||||
@ex[
|
||||
(tbf #(1 2) 3)
|
||||
]}
|
||||
|
||||
@deftogether[(@defproc[(tbf-w [t TBF]) (Vectorof Real)]
|
||||
@defproc[(tbf-θ [t TBF]) Real])]{
|
||||
|
||||
Shortcuts for @racket[tbf-weights] and @racket[tbf-threshold].
|
||||
|
||||
}
|
||||
|
||||
@defproc[(boolean->01/vector [bool-v (Vectorof Boolean)])
|
||||
(Vectorof (U Zero One))]{
|
||||
|
||||
Converts a Boolean vector to a vector of zeros and ones.
|
||||
|
||||
@ex[
|
||||
(boolean->01/vector #(#t #f #f))
|
||||
]}
|
||||
|
||||
@defproc[(apply-tbf [t TBF] [inputs (Vectorof (U Zero One))]) (U Zero One)]{
|
||||
|
||||
Applies the TBF to its inputs.
|
||||
|
||||
Applying a TBF consists in multiplying the weights by the corresponding inputs
|
||||
and comparing the sum of the products to the threshold. If the product is
|
||||
above the threshold, the function is 1, otherwise it is 0.
|
||||
|
||||
@ex[
|
||||
(define simple-tbf (tbf #(2 -2) 1))
|
||||
(tabulate/pv/01 2 (pvλ (x y) (apply-tbf simple-tbf (vector x y))))
|
||||
]}
|
||||
|
||||
@defproc[(apply-tbf/boolean [t TBF] [inputs (Vectorof Boolean)]) Boolean]{
|
||||
|
||||
Like @racket[apply-tbf], but takes Boolean values as inputs and outputs
|
||||
a Boolean value.
|
||||
|
||||
@ex[
|
||||
(define simple-tbf (tbf #(2 -2) 1))
|
||||
(tabulate/pv/boolean 2 (pvλ (x y) (apply-tbf/boolean simple-tbf (vector x y))))
|
||||
]}
|
||||
|
||||
@defproc[(list->tbf [lst (Listof Real)]) TBF]{
|
||||
|
||||
Converts a list of numbers to a TBF. The last element of the list is taken to
|
||||
be the threshold, while the other elements are taken to be the weights.
|
||||
|
||||
@ex[
|
||||
(list->tbf '(1 2 3))
|
||||
]}
|
||||
|
||||
@defproc[(lists->tbfs [lsts (Listof (Listof Real))]) (Listof TBF)]{
|
||||
|
||||
Converts multiple lists of numbers to a list of TBFs.
|
||||
|
||||
The main use is for reading TBFs from Org-mode tables read by
|
||||
@racket[read-org-sexp].
|
||||
|
||||
@ex[
|
||||
(lists->tbfs '((1 2 3) (2 3 4)))
|
||||
]}
|
||||
|
||||
@defproc[(read-org-tbfs [str String] [#:headers headers Boolean #f])
|
||||
(Listof TBF)]{
|
||||
|
||||
Reads a list of TBF from an Org-mode string containing a sexp, containing
|
||||
a list of lists of numbers. If headers is @racket[#t], drops the first list,
|
||||
supposing that it contains the headers of the table.
|
||||
|
||||
The input is typically what @racket[read-org-sexp] reads.
|
||||
|
||||
@ex[
|
||||
(read-org-tbfs "((1 2 1) (1 0 1))")
|
||||
(read-org-tbfs "((x y f) (1 2 1) (1 0 1))" #:headers #t)
|
||||
]}
|
||||
|
||||
@defproc[(tbf-tabulate* [tbfs (Listof TBF)])
|
||||
(Listof (Listof (U Zero One)))]{
|
||||
|
||||
Tabulates a list of TBFs.
|
||||
|
||||
The result is a list of lists describing the truth table of the given TBFs.
|
||||
The first elements of each line give the values of the inputs, while the last
|
||||
elements give the values of each the functions corresponding to the input.
|
||||
|
||||
All the TBFs in @racket[tbfs] must have the same number of inputs as the first
|
||||
TBF in the list.
|
||||
|
||||
@ex[
|
||||
(tbf-tabulate* (list (tbf #(2 2) 1) (tbf #(1 1) 1)))
|
||||
]}
|
||||
|
||||
@defproc[(tbf-tabulate [t TBF])
|
||||
(Listof (Listof (U Zero One)))]{
|
||||
|
||||
Tabulates a single TBF.
|
||||
|
||||
@ex[
|
||||
(tbf-tabulate (tbf #(1 2) 1))
|
||||
]}
|
||||
|
||||
@defproc[(tbf-tabulate*/boolean [tbfs (Listof TBF)])
|
||||
(Listof (Listof Boolean))]{
|
||||
|
||||
Tabulates a list of TBFs like @racket[tbf-tabulate*], but uses Boolean values
|
||||
@racket[#f] and @racket[#t] instead of 0 and 1.
|
||||
|
||||
All the TBFs in @racket[tbfs] must have the same number of inputs as the first
|
||||
TBF in the list.
|
||||
|
||||
@ex[
|
||||
(tbf-tabulate*/boolean (list (tbf #(1 2) 1)))
|
||||
]}
|
||||
|
||||
@defproc[(sbf? [t TBF]) Boolean]{
|
||||
|
||||
A sign Boolean function (SBF) is a TBF whose threshold is 0.
|
||||
|
||||
@ex[
|
||||
(sbf? (tbf #(1 2) 3))
|
||||
(sbf? (tbf #(1 2) 0))
|
||||
]}
|
||||
|
||||
@defproc[(sbf [w (Vectorof Real)]) TBF]{
|
||||
|
||||
Creates a TBF which is an SBF from a vector of weights.
|
||||
|
||||
@ex[
|
||||
(sbf #(1 -1))
|
||||
]}
|
||||
|
||||
@defproc[(list->sbf [lst (Listof Real)])
|
||||
TBF]{
|
||||
|
||||
Converts a list of numbers to an SBF. The elements of the list are taken to be
|
||||
the weights of the SBF.
|
||||
|
||||
@ex[
|
||||
(list->sbf '(1 -1))
|
||||
]}
|
||||
|
||||
@defproc[(read-org-sbfs [str String] [#:headers headers Boolean #f])
|
||||
(Listof TBF)]{
|
||||
|
||||
Reads a list of SBF from an Org-mode string containing a sexp, containing
|
||||
a list of lists of numbers. If headers is @racket[#t], drops the first list,
|
||||
supposing that it contains the headers of the table.
|
||||
|
||||
The input is typically what @racket[read-org-sexp] reads.
|
||||
|
||||
@ex[
|
||||
(read-org-sbfs "((1 1) (1 -1))")
|
||||
]
|
||||
|
||||
See also @racket[read-org-tbfs].
|
||||
}
|
||||
@section[#:tag "fuctions/untyped"]{Untyped definitions}
|
||||
|
||||
@defmodule[(submod dds/functions untyped)]
|
||||
|
||||
@(require (for-label (only-in racket/contract/base listof any/c)
|
||||
(for-label (only-in (submod "../functions.rkt" untyped)
|
||||
tabulate/boolean tabulate*/boolean
|
||||
tabulate/01 tabulate*/01))))
|
||||
|
||||
This submodule contains some functions which cannot be typed or some functions
|
||||
for which Typed Racket cannot produce contracts, i.e. polymorphic functions of
|
||||
variable arity. The definitions in this submodule specifically target untyped
|
||||
user code.
|
||||
|
||||
Since the names of some of the definitions in this submodule are the same in
|
||||
the main module, and since they are imported in the same namespace for
|
||||
rendering this document, some references to untyped definitions may wrongfully
|
||||
point to typed definitions. As a tentative fix, all such references are
|
||||
accompanied by the explicit mention "untyped".
|
||||
|
||||
@(define functions-evaluator/untyped
|
||||
(parameterize ([sandbox-output 'string]
|
||||
[sandbox-error-output 'string]
|
||||
[sandbox-memory-limit 50])
|
||||
(make-evaluator 'racket #:requires '((submod "functions.rkt" untyped)))))
|
||||
|
||||
@(define-syntax-rule (ex/untyped . args)
|
||||
(examples #:eval functions-evaluator/untyped . args))
|
||||
|
||||
@defproc[(tabulate [funcs procedure?]
|
||||
[doms (listof list?)])
|
||||
(listof list?)]{
|
||||
|
||||
Given a function @racket[func] and a list of domains @racket[doms] for each of
|
||||
its arguments, in order, produces a list of lists giving the values of
|
||||
arguments and the value of the functions for these inputs.
|
||||
|
||||
@ex/untyped[
|
||||
(tabulate (λ (x y) (and x y)) '((#f #t) (#f #t)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate* [funcs (listof procedure?)]
|
||||
[doms (listof list?)])
|
||||
(listof list?)]{
|
||||
|
||||
Like @racket[tabulate] (untyped), but @racket[funcs] is a list of functions
|
||||
taking the same arguments over the same domains.
|
||||
|
||||
@ex/untyped[
|
||||
(tabulate* (list (λ (x y) (and x y))
|
||||
(λ (x y) (or x y)))
|
||||
'((#f #t) (#f #t)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate/boolean [func procedure?]) (listof (listof boolean?))]{
|
||||
|
||||
Like @racket[tabulate] (untyped), but assumes the domains of all variables of
|
||||
the function are Boolean. @racket[func] must have a fixed arity. It is an
|
||||
error to supply a function of variable arity.
|
||||
|
||||
@ex/untyped[
|
||||
(tabulate/boolean (lambda (x y) (and x y)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate*/boolean [funcs (non-empty-listof procedure?)])
|
||||
(listof (listof boolean?))]{
|
||||
|
||||
Like @racket[tabulate/boolean], but takes a list of functions of the
|
||||
same arity.
|
||||
|
||||
@ex/untyped[
|
||||
(tabulate*/boolean `(,(λ (x y) (and x y))
|
||||
,(λ (x y) (or x y))))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate/01 [func procedure?]) (listof (listof (or/c 0 1)))]{
|
||||
|
||||
Like @racket[tabulate] (untyped), but assumes the domains of all variables of
|
||||
the function are @tt{{0,1}}. @racket[func] must have a fixed arity. It is an
|
||||
error to supply a function of variable arity.
|
||||
|
||||
@ex/untyped[
|
||||
(tabulate/01 (λ (x y) (modulo (+ x y) 2)))
|
||||
]
|
||||
|
||||
The same remarks apply as for @racket[tabulate/boolean] (untyped).
|
||||
|
||||
}
|
||||
|
||||
@defproc[(tabulate*/01 [funcs (listof procedure?)]) (listof (listof (or/c 0 1)))]{
|
||||
|
||||
Like @racket[tabulate/01], but takes a list of functions of the same arity.
|
||||
|
||||
@ex/untyped[
|
||||
(tabulate*/01 `(,(λ (x y) (min x y)) ,(λ (x y) (max x y))))
|
||||
]}
|
||||
|
|
|
@ -1,5 +1,28 @@
|
|||
#lang scribble/manual
|
||||
@(require (for-label racket graph "../networks.rkt" "../utils.rkt" "../functions.rkt" racket/random racket/hash))
|
||||
@(require scribble/example racket/sandbox
|
||||
(for-label typed/racket/base
|
||||
graph
|
||||
(only-in typed/graph Graph)
|
||||
(only-in racket/class send)
|
||||
"../networks.rkt"
|
||||
"../utils.rkt"
|
||||
"../functions.rkt"
|
||||
"../dynamics.rkt"))
|
||||
|
||||
@(define networks-evaluator
|
||||
(parameterize ([sandbox-output 'string]
|
||||
[sandbox-error-output 'string]
|
||||
[sandbox-memory-limit 500])
|
||||
(make-evaluator 'typed/racket #:requires '("networks.rkt"))))
|
||||
|
||||
@(define-syntax-rule (ex . args)
|
||||
(examples #:eval networks-evaluator . args))
|
||||
|
||||
@(define-syntax-rule (deftypeform . args)
|
||||
(defform #:kind "type" . args))
|
||||
|
||||
@(define-syntax-rule (deftype . args)
|
||||
(defidform #:kind "type" . args))
|
||||
|
||||
@title[#:tag "networks"]{dds/networks: Formal Dynamical Networks}
|
||||
|
||||
|
@ -10,37 +33,914 @@ is a set of variables which are updated according to their corresponding update
|
|||
functions. The variables to be updated at each step are given by the mode.
|
||||
This model can generalise Boolean networks, TBANs, multivalued networks, etc.
|
||||
|
||||
@section[#:tag "networks-basics"]{Basic definitions}
|
||||
@section[#:tag "networks-basics"]{Basic types}
|
||||
|
||||
@deftypeform[(State a)]{
|
||||
|
||||
An immutable mapping (a hash table) assigning elements of type @racket[a] to
|
||||
the variables. A synonym of @racket[VariableMapping].
|
||||
|
||||
}
|
||||
|
||||
@deftypeform[(UpdateFunction a)]{
|
||||
|
||||
An update function is a function computing a value from the given
|
||||
state. This is a synonym of the type @racket[(-> (State a) a)].
|
||||
|
||||
}
|
||||
|
||||
@deftypeform[(Domain a)]{
|
||||
|
||||
A domain which is a subset of the type @racket[a].
|
||||
|
||||
@racket[(Domain a)] is a synonym of @racket[(Listof a)].
|
||||
|
||||
}
|
||||
|
||||
@deftypeform[(DomainMapping a)]{
|
||||
|
||||
A domain mapping is a hash table mapping variables to the lists of values in
|
||||
their domains.
|
||||
|
||||
}
|
||||
|
||||
@section{Common examples}
|
||||
|
||||
The examples in this document often use the same definitions, which are
|
||||
therefore grouped here to avoid duplicating them.
|
||||
|
||||
These are two functions calculating an @italic{AND} and an @italic{OR} between
|
||||
the variables @racket[a] and @racket[b]:
|
||||
|
||||
@ex[
|
||||
(: or-func (UpdateFunction Boolean))
|
||||
(define (or-func s)
|
||||
(or (hash-ref s 'a) (hash-ref s 'b)))
|
||||
|
||||
(: and-func (UpdateFunction Boolean))
|
||||
(define (and-func s)
|
||||
(and (hash-ref s 'a) (hash-ref s 'b)))
|
||||
]
|
||||
|
||||
These are two functions calculating an @italic{AND} and an @italic{OR} between
|
||||
two variables @racket[a] and @racket[b] whose values are in @tt{{0,1}}:
|
||||
|
||||
@ex[
|
||||
(require (only-in "utils.rkt" assert-type))
|
||||
|
||||
(: or-func/01 (UpdateFunction (U Zero One)))
|
||||
(define (or-func/01 s)
|
||||
(assert-type (max (hash-ref s 'a) (hash-ref s 'b))
|
||||
(U Zero One)))
|
||||
|
||||
(: and-func/01 (UpdateFunction (U Zero One)))
|
||||
(define (and-func/01 s)
|
||||
(assert-type (min (hash-ref s 'a) (hash-ref s 'b))
|
||||
(U Zero One)))
|
||||
]
|
||||
|
||||
@section{Utilities}
|
||||
|
||||
@defproc[(01->boolean/state [s (State (U Zero One))]) (State Boolean)]{
|
||||
|
||||
Converts the values 0 and 1 in a state to @racket[#f] and
|
||||
@racket[#t] respectively.
|
||||
|
||||
@ex[
|
||||
(01->boolean/state (hash 'a 0 'b 1))
|
||||
]}
|
||||
|
||||
@defproc[(make-same-domains [vars (Listof Variable)]
|
||||
[domain (Domain a)])
|
||||
(DomainMapping a)]{
|
||||
|
||||
Makes a hash set mapping all variables to a single domain.
|
||||
|
||||
@ex[
|
||||
(make-same-domains '(a b) '(1 2))
|
||||
]}
|
||||
|
||||
@defproc[(make-boolean-domains [vars (Listof Variable)])
|
||||
(DomainMapping Boolean)]{
|
||||
|
||||
Makes a hash set mapping all variables to the Boolean domain.
|
||||
|
||||
@ex[
|
||||
(make-boolean-domains '(a b))
|
||||
]}
|
||||
|
||||
@defproc[(make-01-domains [vars (Listof Variable)])
|
||||
(DomainMapping (U Zero One))]{
|
||||
|
||||
Makes a hash set mapping all variables to the Boolean domain, expressed as
|
||||
@tt{{0,1}}.
|
||||
|
||||
@ex[
|
||||
(make-01-domains '(a b))
|
||||
]}
|
||||
|
||||
@section{Networks}
|
||||
|
||||
@defstruct*[network ([functions (VariableMapping (UpdateFunction a))]
|
||||
[domains (DomainMapping a)])]{
|
||||
|
||||
A network consists of a mapping from its variables to its update variables, as
|
||||
a well as of a mapping from its variables to their domains.
|
||||
|
||||
Instances of @racket[network] have the type @racket[Network].
|
||||
|
||||
}
|
||||
|
||||
@deftypeform[(Network a)]{
|
||||
|
||||
The type of the instances of @racket[network].
|
||||
|
||||
@ex[
|
||||
(network (hash 'a or-func
|
||||
'b and-func)
|
||||
(hash 'a '(#f #t)
|
||||
'b '(#f #t)))
|
||||
]}
|
||||
|
||||
@defproc[(make-boolean-network [funcs (VariableMapping (UpdateFunction Boolean))])
|
||||
(Network Boolean)]{
|
||||
|
||||
Builds a Boolean network from a given hash table assigning functions to
|
||||
variables by attributing Boolean domains to every variable.
|
||||
|
||||
@ex[
|
||||
(make-boolean-network (hash 'a or-func 'b and-func))
|
||||
]}
|
||||
|
||||
@defproc[(make-01-network [funcs (VariableMapping (UpdateFunction (U Zero One)))])
|
||||
(Network (U Zero One))]{
|
||||
|
||||
Build a network from a given hash table assigning functions to variables by
|
||||
attributing the domain @tt{{0,1}} to every variable.
|
||||
|
||||
@ex[
|
||||
(make-01-network (hash 'a or-func/01 'b and-func/01))
|
||||
]}
|
||||
|
||||
@defproc[(update [network (Network a)] [s (State a)] [xs (Listof Variable)])
|
||||
(State a)]{
|
||||
|
||||
Given a state @racket[s] updates all the variables of @racket[network] from
|
||||
@racket[xs].
|
||||
|
||||
@ex[
|
||||
(update (make-boolean-network (hash 'a or-func 'b and-func))
|
||||
(hash 'a #f 'b #t)
|
||||
'(a))
|
||||
]}
|
||||
|
||||
@section{Syntactic description of networks}
|
||||
|
||||
@section{Inferring interaction graphs}
|
||||
@deftype[UpdateFunctionForm]{
|
||||
|
||||
This section provides inference of both unsigned and signed interaction graphs.
|
||||
Since the inference of signed interaction graphs is based on analysing the
|
||||
dynamics of the networks, it may be quite resource-consuming, especially since
|
||||
I allow any syntactic forms in the definitions of the functions.
|
||||
An update function form is any form which can appear as a body of a function
|
||||
and which can be evaluated with @racket[eval].
|
||||
|
||||
Note the fine difference between @emph{syntactic} interaction graphs and
|
||||
interaction graphs generated from the dynamics of the network.
|
||||
Syntactic interaction graphs are based on the whether a variable appears or not
|
||||
in the form of the function for another variable. On the other hand, the
|
||||
normal, conventional interaction graph records the fact that one variable has
|
||||
an impact on the dynamics of the other variable. Depending on the model, these
|
||||
may or may not be the same.
|
||||
@ex[
|
||||
(ann '(and x y (not z)) UpdateFunctionForm)
|
||||
(ann '(+ 1 a (- b 10)) UpdateFunctionForm)
|
||||
]
|
||||
|
||||
@racket[UpdateFunctionForm] is a synonym of @racket[Any].
|
||||
|
||||
}
|
||||
|
||||
@defstruct*[network-form ([functions (VariableMapping NetworkForm)]
|
||||
[domains (DomainMapping a)])]{
|
||||
|
||||
A network form consists of a mapping from variables to the forms of their
|
||||
update functions, together with a mapping from its variables to its
|
||||
update functions.
|
||||
|
||||
The domain mapping does not have to assign domains to all variables (e.g., it
|
||||
may be empty), but in this case the functions which need to know the domains
|
||||
will not work.
|
||||
|
||||
Instances of @racket[network-form] have the type @racket[NetworkForm].
|
||||
|
||||
}
|
||||
|
||||
@deftypeform[(NetworkForm a)]{
|
||||
|
||||
The type of instances of @racket[network-form].
|
||||
|
||||
@ex[
|
||||
(network-form (hash 'a '(and a b) 'b '(or a b))
|
||||
(hash 'a '(#f #t) 'b '(#f #t)))
|
||||
]}
|
||||
|
||||
@defproc[(update-function-form->update-function/any [func UpdateFunctionForm])
|
||||
(UpdateFunction Any)]{
|
||||
|
||||
Builds an update function from an update function form.
|
||||
|
||||
@ex[
|
||||
(define and-from-form (update-function-form->update-function/any '(and x y)))
|
||||
(and-from-form (hash 'x #f 'y #f))
|
||||
(and-from-form (hash 'x #f 'y #t))
|
||||
(and-from-form (hash 'x #t 'y #f))
|
||||
(and-from-form (hash 'x #t 'y #t))
|
||||
]}
|
||||
|
||||
@defproc[(update-function-form->update-function/boolean [func UpdateFunctionForm])
|
||||
(UpdateFunction Boolean)]{
|
||||
|
||||
Like @racket[update-function-form->update-function/any], but the resulting
|
||||
function operates on Boolean states.
|
||||
|
||||
@ex[
|
||||
(define and-from-form/boolean (update-function-form->update-function/boolean '(and x y)))
|
||||
(and-from-form/boolean (hash 'x #f 'y #f))
|
||||
(and-from-form/boolean (hash 'x #f 'y #t))
|
||||
(and-from-form/boolean (hash 'x #t 'y #f))
|
||||
(and-from-form/boolean (hash 'x #t 'y #t))
|
||||
]}
|
||||
|
||||
@defproc[(update-function-form->update-function/01 [func UpdateFunctionForm])
|
||||
(UpdateFunction (U Zero One))]{
|
||||
|
||||
Like @racket[update-function-form->update-function/01], but the resulting
|
||||
function operates on Boolean states, with the domain @tt{{0,1}}.
|
||||
|
||||
@ex[
|
||||
(define and-from-form/01 (update-function-form->update-function/01 '(min x y)))
|
||||
(and-from-form/01 (hash 'x 0 'y 0))
|
||||
(and-from-form/01 (hash 'x 0 'y 1))
|
||||
(and-from-form/01 (hash 'x 1 'y 0))
|
||||
(and-from-form/01 (hash 'x 1 'y 1))
|
||||
]}
|
||||
|
||||
@defproc[(network-form->network/any [nf (NetworkForm Any)]) (Network Any)]{
|
||||
|
||||
Builds a network from a network form.
|
||||
|
||||
@ex[
|
||||
(network-form->network/any
|
||||
(network-form (hash 'a '(and a b)
|
||||
'b '(not b))
|
||||
(hash 'a '(#f #t)
|
||||
'b '(#f #t))))
|
||||
]}
|
||||
|
||||
@defproc[(network-form->network/boolean [nf (NetworkForm Boolean)]) (Network Boolean)]{
|
||||
|
||||
Like @racket[network-form->network/any], but builds a Boolean network.
|
||||
|
||||
@ex[
|
||||
(network-form->network/boolean
|
||||
(network-form (hash 'a '(and a b)
|
||||
'b '(not b))
|
||||
(hash 'a '(#f #t)
|
||||
'b '(#f #t))))
|
||||
]}
|
||||
|
||||
@defproc[(network-form->network/01 [nf (NetworkForm (U Zero One))]) (Network (U Zero One))]{
|
||||
|
||||
Like @racket[network-form->network/any], but builds a Boolean network, whose
|
||||
domains are expressed as @tt{{0,1}}.
|
||||
|
||||
@ex[
|
||||
(network-form->network/01
|
||||
(network-form (hash 'a '(min a b)
|
||||
'b '(- 1 b))
|
||||
(hash 'a '(0 1)
|
||||
'b '(0 1))))
|
||||
]}
|
||||
|
||||
@defproc[(make-boolean-network-form [forms (VariableMapping UpdateFunctionForm)])
|
||||
(NetworkForm Boolean)]{
|
||||
|
||||
Build a Boolean network form from a given mapping assigning forms to variables.
|
||||
|
||||
@ex[
|
||||
(make-boolean-network-form (hash 'a '(and a b)
|
||||
'b '(not b)))
|
||||
]}
|
||||
|
||||
@defproc[(forms->boolean-network [forms (VariableMapping UpdateFunctionForm)])
|
||||
(Network Boolean)]{
|
||||
|
||||
Build a Boolean network from a given mapping assigning forms to variables.
|
||||
|
||||
@ex[
|
||||
(forms->boolean-network (hash 'a '(and a b)
|
||||
'b '(not b)))
|
||||
]}
|
||||
|
||||
@section{Dynamics of networks}
|
||||
|
||||
This section contains definitions for building and analysing the dynamics
|
||||
of networks.
|
||||
|
||||
@defproc[(build-all-states [vars-domains (DomainMapping a)])
|
||||
(Listof (State a))]{
|
||||
|
||||
Given a @racket[DomainMapping], constructs the list of all possible states.
|
||||
|
||||
@ex[
|
||||
(build-all-states (make-boolean-domains '(a b)))
|
||||
]}
|
||||
|
||||
@defproc[(build-all-boolean-states [vars (Listof Variable)])
|
||||
(Listof (State Boolean))]{
|
||||
|
||||
Builds all Boolean states over a given list of variables.
|
||||
|
||||
@ex[
|
||||
(build-all-boolean-states '(a b))
|
||||
]}
|
||||
|
||||
@defproc[(build-all-01-states [vars (Listof Variable)])
|
||||
(Listof (State Boolean))]{
|
||||
|
||||
Builds all Boolean states over a given set of variables, but with the Boolean
|
||||
values represented as 0 and 1.
|
||||
|
||||
@ex[
|
||||
(build-all-01-states '(a b))
|
||||
]}
|
||||
|
||||
@deftype[Modality]{
|
||||
|
||||
A modality is a list of variables. This is a synonym of @racket[(Listof
|
||||
Variable)].
|
||||
|
||||
}
|
||||
|
||||
@deftype[Mode]{
|
||||
|
||||
A mode is a list of modalities. This is a synonym of @racket[(Listof Modality)].
|
||||
|
||||
}
|
||||
|
||||
@defclass[dynamics% dds% ()]{
|
||||
|
||||
A model of dynamics of a network is a @racket[Network] with a @racket[Mode].
|
||||
|
||||
@defconstructor[([network (Network a)] [mode Mode])]{
|
||||
|
||||
Creates a new instance of @racket[dynamics%] from a @racket[network] and
|
||||
a @racket[mode]. Both are available as public fields of the class.
|
||||
|
||||
}
|
||||
|
||||
@defmethod[(step/annotated [st (State a)]) (Listof (Pairof Modality (State a)))]{
|
||||
|
||||
Apply the network stored in this class to the state @racket[st] with all
|
||||
modalities of the mode stored in this class.
|
||||
|
||||
This is the only method of @racket[dds%] overridden in this class.
|
||||
|
||||
@ex[
|
||||
(let* ([n (forms->boolean-network (hash 'a '(and a b)
|
||||
'b '(not b)))]
|
||||
[syn '((a) (b))]
|
||||
[syn-dynamics (new (inst dynamics% Boolean) [network n] [mode syn])]
|
||||
[st (hash 'a #f 'b #f)])
|
||||
(send syn-dynamics step/annotated st))
|
||||
]}}
|
||||
|
||||
@deftypeform[(Dynamics% a)]{
|
||||
|
||||
The type of an instance of @racket[dynamics%] with values of type @racket[a].
|
||||
|
||||
@ex[
|
||||
(let* ([n (forms->boolean-network (hash 'a '(and a b)
|
||||
'b '(not b)))]
|
||||
[syn '((a) (b))]
|
||||
[syn-dynamics (new (inst dynamics% Boolean) [network n] [mode syn])])
|
||||
(ann syn-dynamics (Dynamics% Boolean)))
|
||||
]
|
||||
|
||||
Note that, as of 2022-09-15, Typed Racket does not seem to allow to pass type
|
||||
parameters from a child class to a parent class. Therefore, @racket[dynamics%]
|
||||
inherits in fact from a @racket[dds%] with its first type parameter set to
|
||||
@racket[(State Any)]. This can be seen by examining the type of
|
||||
@racket[dynamics%].
|
||||
|
||||
@ex[dynamics%]
|
||||
|
||||
The type constructor @racket[Dynamics%] takes this limitation into account.
|
||||
|
||||
}
|
||||
|
||||
@defproc[(make-asyn [vars (Listof Variable)]) Mode]{
|
||||
|
||||
Given a list of variables, builds the asynchronous mode, i.e. a set of
|
||||
singleton sets of variables.
|
||||
|
||||
@ex[(make-asyn '(x y z))]
|
||||
|
||||
}
|
||||
|
||||
@defproc[(make-syn [vars (Listof Variable)]) Mode]{
|
||||
|
||||
Given a list of variables, builds the synchronous mode, i.e. a singleton set
|
||||
containing the set of all variables.
|
||||
|
||||
@ex[(make-syn '(x y z))]
|
||||
|
||||
}
|
||||
|
||||
@defproc[(make-asyn-dynamics [network (Network a)]) (Dynamics% a)]{
|
||||
|
||||
Creates the asynchronous dynamics for a given network: an instance of
|
||||
@racket[dynamics%] with @tt{network} as the network and the asynchronous
|
||||
mode as @tt{mode}.
|
||||
|
||||
See @racket[build-full-state-graph/annotated] for an example.
|
||||
|
||||
}
|
||||
|
||||
@defproc[(make-syn-dynamics [network (Network a)]) (Dynamics% a)]{
|
||||
|
||||
Creates the synchronous dynamics for a given network: an instance of
|
||||
@racket[dynamics%] with @tt{network} as the network and the synchronous mode as
|
||||
@tt{mode}.
|
||||
|
||||
See @racket[build-full-state-graph] for an example.
|
||||
|
||||
}
|
||||
|
||||
@defproc[(build-full-state-graph [dyn (Dynamics% a)]) Graph]{
|
||||
|
||||
Builds the full state graph of the given dynamics.
|
||||
|
||||
@ex[
|
||||
(require (only-in "utils.rkt" dotit))
|
||||
|
||||
(let* ([n (forms->boolean-network (hash 'a '(and a b)
|
||||
'b '(not b)))]
|
||||
[syn-dynamics (make-syn-dynamics n)])
|
||||
(dotit ((inst build-full-state-graph Boolean) syn-dynamics)))
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
@defproc[(build-full-state-graph/annotated [dyn (Dynamics% a)]) Graph]{
|
||||
|
||||
Builds the full annotated state graph of the given dynamics.
|
||||
|
||||
@ex[
|
||||
(require (only-in "utils.rkt" dotit))
|
||||
|
||||
(let* ([n (forms->boolean-network (hash 'a '(and a b)
|
||||
'b '(not b)))]
|
||||
[asyn-dynamics (make-asyn-dynamics n)])
|
||||
(dotit ((inst build-full-state-graph/annotated Boolean) asyn-dynamics)))
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
@section[#:tag "networks_Pretty_printing"]{Pretty printing}
|
||||
|
||||
This section defines various functions for nicely formatting node and edge
|
||||
labels in state graphs of networks.
|
||||
|
||||
@defproc[(pretty-print-state [s (State a)]) String]{
|
||||
|
||||
Pretty-prints a state of a network.
|
||||
|
||||
@ex[(pretty-print-state (hash 'a #f 'b 3 'c 4))]
|
||||
|
||||
}
|
||||
|
||||
@defproc[(pretty-print-state/01 [s (State a)]) String]{
|
||||
|
||||
Pretty-prints a state of a network, replacing all @racket[#f] values with 0 and
|
||||
all other values with 1.
|
||||
|
||||
@ex[(pretty-print-state/01 (hash 'a #f 'b 3 'c '() 'd #t))]
|
||||
|
||||
}
|
||||
|
||||
@defproc[(pretty-print-state-graph-with [gr Graph] [pprinter (-> Any Any)]) Graph]{
|
||||
|
||||
Given a state graph @racket[gr] and the pretty-printer for states
|
||||
@racket[pprinter], build a new state graph in which the nodes are
|
||||
pretty-printed with @racket[pprinter] and the edges with
|
||||
@racket[pretty-print-set-sets].
|
||||
|
||||
}
|
||||
|
||||
@defproc[(pretty-print-state-graph [gr Graph]) Graph]{
|
||||
|
||||
Calls @racket[pretty-print-state-graph-with] with @racket[pretty-print-state].
|
||||
|
||||
@ex[
|
||||
(let* ([n (forms->boolean-network (hash 'a '(and a b)
|
||||
'b '(not b)))]
|
||||
[syn-dynamics (make-syn-dynamics n)])
|
||||
(dotit (pretty-print-state-graph
|
||||
((inst build-full-state-graph Boolean) syn-dynamics))))
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
@defproc[(ppsg [gr Graph]) Graph]{
|
||||
|
||||
A shortcut for @racket[pretty-print-state-graph].
|
||||
|
||||
}
|
||||
|
||||
@defproc[(pretty-print-state-graph/01 [gr Graph]) Graph]{
|
||||
|
||||
Calls @racket[pretty-print-state-graph-with] with @racket[pretty-print-state/01].
|
||||
|
||||
@ex[
|
||||
(let* ([n (forms->boolean-network (hash 'a '(and a b)
|
||||
'b '(not b)))]
|
||||
[syn-dynamics (make-syn-dynamics n)])
|
||||
(dotit (pretty-print-state-graph/01
|
||||
((inst build-full-state-graph Boolean) syn-dynamics))))
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
@defproc[(ppsg01 [gr Graph]) Graph]{
|
||||
|
||||
A shortcut for @racket[pretty-print-state-graph/01].
|
||||
|
||||
}
|
||||
|
||||
|
||||
@section{Inferring interaction graphs}
|
||||
|
||||
This section provides inference of both unsigned and signed interaction graphs.
|
||||
Since the inference of signed interaction graphs is based on analysing the
|
||||
dynamics of the networks, it may be quite resource-consuming, especially since
|
||||
any syntactic forms are allowed in the definitions of the functions.
|
||||
|
||||
We use the term @emph{syntactic interaction graph} to refer to the graph in
|
||||
which the presence of an arc from @tt{x} to @tt{y} is based on whether @tt{x}
|
||||
appears in the form of @tt{y}. This is quite different from the canonical
|
||||
definition of the @emph{interaction graph}, in which the arc from @tt{x} to
|
||||
@tt{y} represents the fact that a change in the value of @tt{x} may lead to
|
||||
a change in the value of @tt{y}. Thus the syntactic interaction graph may have
|
||||
extra arcs if @tt{x} appears in the form of @tt{y}, but has no actual influence
|
||||
on @tt{y}.
|
||||
|
||||
@defproc[(list-syntactic-interactions [nf (NetworkForm a)]
|
||||
[x Variable])
|
||||
(Listof Variable)]{
|
||||
|
||||
Lists the variables of the network form appearing in the update function form
|
||||
for @racket[x].
|
||||
|
||||
The variables which are not part of the network are excluded from the listing.
|
||||
|
||||
@ex[
|
||||
(list-syntactic-interactions
|
||||
(make-boolean-network-form #hash((a . (+ a b))
|
||||
(b . (- b))))
|
||||
'a)
|
||||
(list-syntactic-interactions
|
||||
(make-boolean-network-form #hash((a . (+ a b c))
|
||||
(b . (- b c))))
|
||||
'a)
|
||||
]}
|
||||
|
||||
@defproc[(build-syntactic-interaction-graph [n (NetworkForm a)])
|
||||
Graph]{
|
||||
|
||||
Builds the graph in which the vertices are the variables of a given network,
|
||||
and which contains an arrow from @racket[x] to @racket[y] whenever @racket[x]
|
||||
appears in @racket[(list-interactions y)].
|
||||
|
||||
@ex[
|
||||
(require (only-in "utils.rkt" dotit))
|
||||
(dotit (build-syntactic-interaction-graph
|
||||
(make-boolean-network-form #hash((a . (+ a b))
|
||||
(b . (- b))))))
|
||||
]}
|
||||
|
||||
@defproc[(interaction? [network (Network a)]
|
||||
[x Variable]
|
||||
[y Variable])
|
||||
Boolean]{
|
||||
|
||||
Given two variables @racket[x] and @racket[y] of a @racket[network], verifies
|
||||
if they interact, i.e. that there exists a pair of states @italic{s} and
|
||||
@italic{s'} with the following properties:
|
||||
|
||||
@itemlist[
|
||||
@item{@italic{s} and @italic{s'} only differ in the value of @racket[x];}
|
||||
@item{running the network from @italic{s} and @italic{s'} yields different
|
||||
values for @racket[y].}
|
||||
]
|
||||
|
||||
@ex[
|
||||
(let ([bn (forms->boolean-network #hash((a . (and a b))
|
||||
(b . (not b))))])
|
||||
(values (interaction? bn 'a 'b)
|
||||
(interaction? bn 'b 'a)))
|
||||
]}
|
||||
|
||||
@defproc[(get-interaction-sign [network (Network a)]
|
||||
[x Variable]
|
||||
[y Variable])
|
||||
(Option Integer)]{
|
||||
|
||||
Given two variables @racket[x] and @racket[y] of @racket[network], checks
|
||||
whether they interact, and if they interact, returns 1 if increasing @racket[x]
|
||||
leads to an increase in @racket[y], -1 if it leads to a decrease in @racket[y],
|
||||
and 0 if it can lead to both. If @racket[x] has no impact on @racket[y], returns @racket[#f].
|
||||
|
||||
The values in the domains are ordered according to the order in which they are
|
||||
listed in @racket[network].
|
||||
|
||||
Since @racket[get-interaction-sign] needs to check all possible interactions
|
||||
between @racket[x] and @racket[y], it is more costly than calling
|
||||
@racket[interaction?].
|
||||
|
||||
@ex[
|
||||
(let ([bn (forms->boolean-network #hash((a . (and a b))
|
||||
(b . (not b))))])
|
||||
(values (get-interaction-sign bn 'a 'b)
|
||||
(get-interaction-sign bn 'b 'a)
|
||||
(get-interaction-sign bn 'b 'b)))
|
||||
]}
|
||||
|
||||
@defproc[(build-interaction-graph [network (Network a)]) Graph]{
|
||||
|
||||
Given a network, builds its interaction graph. The graph has variables as
|
||||
nodes and has a directed edge from @italic{x} to @italic{y} if
|
||||
@racket[interaction?] returns @racket[#t] for these variables, in this order.
|
||||
|
||||
@ex[
|
||||
(dotit (build-interaction-graph
|
||||
(forms->boolean-network #hash((a . (and a b))
|
||||
(b . (not b))))))
|
||||
]}
|
||||
|
||||
@defproc[(build-interaction-graph/form [nf (NetworkForm a)]) Graph]{
|
||||
|
||||
Like @racket[build-interaction-graph], but accepts a network form and
|
||||
converts it a to @racket[(Network a)] first.
|
||||
|
||||
@ex[
|
||||
(dotit (build-interaction-graph/form
|
||||
(make-boolean-network-form #hash((a . (and a b))
|
||||
(b . (not b))))))
|
||||
]}
|
||||
|
||||
@defproc[(build-signed-interaction-graph [network (Network a)]) Graph]{
|
||||
|
||||
Given a network, builds its signed interaction graph. The graph has variables
|
||||
as nodes and has a directed edge from @italic{x} to @racket{y} labelled by the
|
||||
value @racket[get-interaction-sign] produces for these variables, in that
|
||||
order, unless this value is @racket[#f].
|
||||
|
||||
@ex[
|
||||
(dotit (build-signed-interaction-graph
|
||||
(forms->boolean-network #hash((a . (and a b))
|
||||
(b . (not b))))))
|
||||
]}
|
||||
|
||||
@defproc[(build-signed-interaction-graph/form [nf (NetworkForm a)]) Graph]{
|
||||
|
||||
Like @racket[build-signed-interaction-graph], but takes a network form and
|
||||
converts it to a network.
|
||||
|
||||
@ex[
|
||||
(dotit (build-signed-interaction-graph/form
|
||||
(make-boolean-network-form #hash((a . (and a b))
|
||||
(b . (not b))))))
|
||||
]}
|
||||
|
||||
@section{Tabulating functions and networks}
|
||||
@defproc[(tabulate-state* [funcs (Listof (-> (State a) a))]
|
||||
[domains (DomainMapping a)])
|
||||
(Listof (Listof a))]{
|
||||
|
||||
Like @racket[tabulate*], but the functions operate on states.
|
||||
|
||||
This function will produce a joint truth table of the given functions—a list of
|
||||
lists, in which the first columns list all possible combinations of the values
|
||||
of the input values, and the last columns give the corresponding values of the
|
||||
functions. @racket[domains] defines the domains of each of the component of
|
||||
the states.
|
||||
|
||||
@ex[
|
||||
(require (only-in "utils.rkt" λ/:))
|
||||
(tabulate-state* (list (λ/: (State Integer) (+ :a :b))
|
||||
(λ/: (State Integer) (- :a :b)))
|
||||
(hash 'a '(1 2) 'b '(2 3)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate-state*+headers [funcs (Listof (-> (State a) a))]
|
||||
[domains (DomainMapping a)])
|
||||
(Pairof (Listof Symbol) (Listof (Listof a)))]{
|
||||
|
||||
Like @racket[tabulate-state*], but the first line of the result lists the names
|
||||
of the variables and the functions.
|
||||
|
||||
@ex[
|
||||
(tabulate-state*+headers (list (λ/: (State Integer) (+ :a :b))
|
||||
(λ/: (State Integer) (- :a :b)))
|
||||
(hash 'a '(1 2) 'b '(2 3)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate-state*/boolean [funcs (Listof (-> State Boolean) Boolean)]
|
||||
[args (Listof Variable)])
|
||||
(Listof (Listof Boolean))]{
|
||||
|
||||
Like @racket[tabulate-state*], but the functions operate on Boolean states.
|
||||
The list @racket[args] is used to generate all possible Boolean states
|
||||
containing the variables appearing on this list.
|
||||
|
||||
@ex[
|
||||
(tabulate-state*/boolean (list (λ/: (State Boolean) (and :a :b))
|
||||
(λ/: (State Boolean) (or :a :b)))
|
||||
'(a b))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate-state*+headers/boolean
|
||||
[funcs (Listof (-> State Boolean) Boolean)]
|
||||
[args (Listof Variable)])
|
||||
(Pairof (Listof Symbol) (Listof (Listof Boolean)))]{
|
||||
|
||||
Like @racket[tabulate-state*+headers], but the functions operate on Boolean
|
||||
states, like @racket[tabulate-state*].
|
||||
|
||||
@ex[
|
||||
(tabulate-state*+headers/boolean
|
||||
(list (λ/: (State Boolean) (and :a :b))
|
||||
(λ/: (State Boolean) (or :a :b)))
|
||||
'(a b))
|
||||
]}
|
||||
|
||||
@deftogether[(@defproc[(tabulate-state [func (-> (State a) a)]
|
||||
[domains (DomainMapping a)])
|
||||
(Listof (Listof a))]
|
||||
@defproc[(tabulate-state+headers
|
||||
[func (-> (State a) a)]
|
||||
[domains (DomainMapping a)])
|
||||
(Pairof (Listof Symbol) (Listof (Listof a)))]
|
||||
@defproc[(tabulate-state/boolean [func (-> (State Boolean) Boolean)]
|
||||
[args (Listof Variable)])
|
||||
(Listof (Listof Boolean))]
|
||||
@defproc[(tabulate-state+headers/boolean
|
||||
[func (-> (State Boolean) Boolean)]
|
||||
[args (Listof Variable)])
|
||||
(Pairof (Listof Symbol) (Listof (Listof Boolean)))])]{
|
||||
|
||||
Like the starred versions @racket[tabulate-state*],
|
||||
@racket[tabulate-state*+headers], @racket[tabulate-state/boolean], and
|
||||
@racket[tabulate-state+headers/boolean], but only tabulate one
|
||||
function instead of a list.
|
||||
|
||||
@ex[
|
||||
(tabulate-state
|
||||
(λ/: (State Boolean) (and :a :b))
|
||||
(hash 'a '(#f #t) 'b '(#f #t)))
|
||||
(tabulate-state+headers
|
||||
(λ/: (State Boolean) (and :a :b))
|
||||
(hash 'a '(#f #t) 'b '(#f #t)))
|
||||
(tabulate-state/boolean
|
||||
(λ/: (State Boolean) (and :a :b))
|
||||
'(a b))
|
||||
(tabulate-state+headers/boolean
|
||||
(λ/: (State Boolean) (and :a :b))
|
||||
'(a b))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate-network [network (Network a)]) (Listof (Listof a))]{
|
||||
|
||||
Tabulates all the functions of @racket[network], producing an output
|
||||
similar to that of @racket[tabulate-state*].
|
||||
|
||||
@ex[
|
||||
(tabulate-network (forms->boolean-network (hash 'a '(not a) 'b 'b)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate-network+headers [network (Network a)]) (Listof (Listof a))]{
|
||||
|
||||
Tabulates all the functions of @racket[network], producing an output
|
||||
similar to that of @racket[tabulate-state*+headers] , except that the
|
||||
function names in the corresponding column headers are of the form
|
||||
@tt{f-x}, where @tt{x} is the name of the corresponding variable.
|
||||
|
||||
@ex[
|
||||
(tabulate-network+headers (forms->boolean-network (hash 'a '(not a) 'b 'b)))
|
||||
]}
|
||||
|
||||
@section{Constructing functions and networks}
|
||||
|
||||
@defproc[(table+vars->network [var-names (Listof Variable)]
|
||||
[table (Listof (Listof a))])
|
||||
(Network a)]{
|
||||
|
||||
Given a @racket[table] like the one produced by
|
||||
@racket[tabulate-network] and the list of variable names
|
||||
@racket[var-names], constructs a network having this behaviour.
|
||||
|
||||
The columns defining the functions are taken to be in the same order
|
||||
as the columns defining the variables. The domains of the network are
|
||||
a mapping assigning to each variable the set of values which can
|
||||
appear in the corresponding column in the table.
|
||||
|
||||
This function relies on @racket[table->unary-function], so the same
|
||||
performance caveats apply.
|
||||
|
||||
This function does not check whether the table is complete.
|
||||
|
||||
@ex[
|
||||
(let ([n (table+vars->network '(a b)
|
||||
'((#f #f #f #f)
|
||||
(#f #t #f #t)
|
||||
(#t #f #t #f)
|
||||
(#t #t #t #t)))])
|
||||
(tabulate-network n))
|
||||
]}
|
||||
|
||||
@defproc[(table->network [table (Listof (Listof a))]) (Network a)]{
|
||||
|
||||
Like @racket[table+vars->network], but generates variable names as
|
||||
@tt{xi}, where 1 ≤ @tt{i} ≤ number of variables.
|
||||
|
||||
@ex[
|
||||
(let ([n (table->network '((#f #f #f #f)
|
||||
(#f #t #f #t)
|
||||
(#t #f #t #f)
|
||||
(#t #t #t #t)))])
|
||||
(network-domains n))
|
||||
]}
|
||||
|
||||
@defproc[(table+headers->network [table (Pairof (Listof Symbol) (Listof (Listof a)))])
|
||||
(Network a)]{
|
||||
|
||||
Like @racket[table+vars->network], but the variable names are taken
|
||||
from the first line of @racket[table].
|
||||
|
||||
The lines of @racket[table] are taken to be of the same length, so it
|
||||
is assumed that half of the first line contain variable names, and the
|
||||
other half function names. Function names are discarded.
|
||||
|
||||
@ex[
|
||||
(let ([n (table+headers->network '((a b fa fb)
|
||||
(#f #f #f #f)
|
||||
(#f #t #f #t)
|
||||
(#t #f #t #f)
|
||||
(#t #t #t #t)))])
|
||||
(network-domains n))
|
||||
]}
|
||||
|
||||
@section{Random functions and networks}
|
||||
|
||||
@section{TBF/TBN and SBF/SBN}
|
||||
@defproc[(random-function/state [arg-domains (DomainMapping a)]
|
||||
[func-doman (Domain a)])
|
||||
(-> (State a) a)]{
|
||||
|
||||
This section defines threshold Boolean functions (TBF) and networks (TBN), as
|
||||
well as sign Boolean functions (SBF) and networks (SBN).
|
||||
Generates a random function accepting a state over the domains given
|
||||
by @racket[arg-domains] and producing values in @racket[func-domain].
|
||||
|
||||
@ex[
|
||||
(let* ([doms (hash 'a '(1 2) 'b '(3 4))]
|
||||
[f (random-function/state doms '(e f))])
|
||||
(tabulate-state+headers f doms))
|
||||
]}
|
||||
|
||||
@defproc[(random-boolean-function/state [args (Listof Variable)])
|
||||
(-> (State a) a)]{
|
||||
|
||||
Generates a random Boolean function accepting states over the
|
||||
variables in @racket[args].
|
||||
|
||||
@ex[
|
||||
(tabulate-state+headers/boolean (random-boolean-function/state '(a b)) '(a b))
|
||||
]}
|
||||
|
||||
@defproc[(random-network [domains (DomainMapping a)])
|
||||
(Network a)]{
|
||||
|
||||
Generates a random network from the given domain mapping.
|
||||
|
||||
@ex[
|
||||
(tabulate-network+headers (random-network (hash 'a '(1 2) 'b '(#f #t))))
|
||||
]}
|
||||
|
||||
@defproc[(random-boolean-network [vars (Listof Variable)])
|
||||
(Network Boolean)]{
|
||||
|
||||
Generates a random Boolean network with the given variables.
|
||||
|
||||
@ex[
|
||||
(tabulate-network+headers (random-boolean-network '(x y z)))
|
||||
]}
|
||||
|
||||
@defproc[(random-boolean-network/n [n Positive-Integer])
|
||||
(Network Boolean)]{
|
||||
|
||||
Like @racket[random-boolean-network], but also generates the names of
|
||||
the variables for the network. The variables have the names @tt{x0}
|
||||
to @tt{xk}, where @italic{k = n - 1}.
|
||||
|
||||
@ex[
|
||||
(tabulate-network+headers (random-boolean-network/n 3))
|
||||
]}
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
#lang scribble/manual
|
||||
@(require (for-label racket graph "../rs.rkt"))
|
||||
@(require scribble/example racket/sandbox
|
||||
(for-label typed/racket/base
|
||||
"../rs.rkt"
|
||||
"../utils.rkt"
|
||||
"../dynamics.rkt"))
|
||||
|
||||
@(define rs-evaluator
|
||||
(parameterize ([sandbox-output 'string]
|
||||
[sandbox-error-output 'string]
|
||||
[sandbox-memory-limit 500])
|
||||
(make-evaluator 'typed/racket
|
||||
#:requires '("rs.rkt" "utils.rkt"))))
|
||||
|
||||
@(define-syntax-rule (ex . args)
|
||||
(examples #:eval rs-evaluator . args))
|
||||
|
||||
@(define-syntax-rule (deftypeform . args)
|
||||
(defform #:kind "type" . args))
|
||||
|
||||
@(define-syntax-rule (deftype . args)
|
||||
(defidform #:kind "type" . args))
|
||||
|
||||
@title[#:tag "rs"]{dds/rs: Reaction Systems}
|
||||
|
||||
|
@ -9,11 +29,165 @@ This module defines reaction systems and various tools for working with them.
|
|||
|
||||
@section[#:tag "rs-basics"]{Basic definitions}
|
||||
|
||||
@deftype[Species]{
|
||||
|
||||
A synonym of @racket[Symbol].
|
||||
|
||||
}
|
||||
|
||||
@defstruct*[reaction ([reactants (Setof Species)]
|
||||
[inhibitors (Setof Species)]
|
||||
[products (Setof Species)])]{
|
||||
|
||||
A reaction is a triple of sets, giving the reactants, the inhibitors,
|
||||
and the products, respectively.
|
||||
|
||||
}
|
||||
|
||||
@deftype[Reaction]{
|
||||
|
||||
The type of the instances of @racket[reaction].
|
||||
|
||||
}
|
||||
|
||||
@deftype[ReactionName]{
|
||||
|
||||
A reaction name is any @racket[Symbol].
|
||||
|
||||
}
|
||||
|
||||
@defproc[(make-reaction [r (Listof Reaction)]
|
||||
[i (Listof Reaction)]
|
||||
[p (Listof Reaction)])
|
||||
Reaction]{
|
||||
|
||||
A shortcut for constructing @racket[Reaction]s using list syntax
|
||||
instead of set syntax.
|
||||
|
||||
@ex[
|
||||
(make-reaction '(a b) '(c d) '(e f))
|
||||
]}
|
||||
|
||||
@defproc[(enabled? [r Reaction] [s (Setof Species)]) Boolean]{
|
||||
|
||||
A @racket[Reaction] is enabled on a set of species if all of its
|
||||
reactants are in the set and none of its inhibitors are.
|
||||
|
||||
@ex[
|
||||
(enabled? (make-reaction '(a b) '(c d) '())
|
||||
(set 'a 'b 'e))
|
||||
(enabled? (make-reaction '(a b) '(c d) '())
|
||||
(set 'a 'b 'c))
|
||||
(enabled? (make-reaction '(a b) '(c d) '())
|
||||
(set 'b 'e))
|
||||
]}
|
||||
|
||||
@deftype[ReactionSystem]{
|
||||
|
||||
A reaction system is a dictionary mapping @racket[ReactionName]s to
|
||||
@racket[Reaction]s.
|
||||
|
||||
}
|
||||
|
||||
@defproc[(list-enabled [rs ReactionSystem] [s (Setof Species)])
|
||||
(Listof ReactionName)]{
|
||||
|
||||
Returns the list of the names of reactions of @racket[rs] enabled on
|
||||
@racket[s].
|
||||
|
||||
@ex[
|
||||
(let ([rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(z)))])
|
||||
(values (list-enabled rs (set 'x 'y))
|
||||
(list-enabled rs (set 'x))))
|
||||
]}
|
||||
|
||||
@defproc[(union-products [rs ReactionSystem] [as (Listof ReactionName)])
|
||||
(Setof Species)]{
|
||||
|
||||
Returns the union of the product sets of the given reactions listed in
|
||||
@racket[as] in @racket[rs]. If no reactions are supplied, returns the
|
||||
empty set.
|
||||
|
||||
@ex[
|
||||
(union-products (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(z)))
|
||||
'(a b))
|
||||
]}
|
||||
|
||||
@defproc[(apply-rs [rs ReactionSystem] [s (Setof Species)])
|
||||
(Setof Species)]{
|
||||
|
||||
Applies the reaction system @racket[rs] to @racket[s].
|
||||
|
||||
@ex[
|
||||
(let ([rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(z)))])
|
||||
(apply-rs rs (set 'x)))
|
||||
]}
|
||||
|
||||
|
||||
@section{Org-mode interaction}
|
||||
|
||||
This section contains some useful primitives for
|
||||
@hyperlink["https://orgmode.org/"]{Org-mode} interoperability.
|
||||
|
||||
@defproc[(str-triple->reaction [lst (List String String String)]) Reaction]{
|
||||
|
||||
Converts a triple of strings to a reaction.
|
||||
|
||||
@ex[
|
||||
(str-triple->reaction '("a b" "c d" "e f"))
|
||||
]}
|
||||
|
||||
@defproc[(ht-str-triples->rs [ht (HashTable ReactionName (List String String String))])
|
||||
ReactionSystem]{
|
||||
|
||||
Converts a hash table mapping reaction names to triples of strings to
|
||||
a reaction system.
|
||||
|
||||
@ex[
|
||||
(ht-str-triples->rs (hash 'a (list "x y" "" "k i")
|
||||
'b (list "" "x y" "t j")))
|
||||
]}
|
||||
|
||||
@defproc[(read-org-rs [str String]) ReactionSystem]{
|
||||
|
||||
Reads a reaction system from an Org-mode style string.
|
||||
|
||||
@ex[
|
||||
(read-org-rs "((\"a\" \"x t\" \"y\" \"z\") (\"b\" \"x\" \"q\" \"z\"))")
|
||||
]}
|
||||
|
||||
@defproc[(read-context-sequence [str String]) (Listof (Setof Species))]{
|
||||
|
||||
Reads a context sequence (a list of sets of species) from a string
|
||||
containing a list, which may be produced by Org-mode.
|
||||
|
||||
@ex[
|
||||
(read-context-sequence "((\"x y\") (\"z\") (\"\") (\"t\"))")
|
||||
]}
|
||||
|
||||
@defproc[(reaction->str-triple [r Reaction]) (Listof String)]{
|
||||
|
||||
Converts a reaction to a triple of strings.
|
||||
|
||||
@ex[
|
||||
(reaction->str-triple (make-reaction '(x y) '(z t) '(k i)))
|
||||
]}
|
||||
|
||||
@defproc[(rs->ht-str-triples [rs ReactionSystem])
|
||||
(HashTable ReactionName (Listof String))]{
|
||||
|
||||
Converts a reaction system to a hash table mapping
|
||||
@racket[ReactionNames]s to triples of strings.
|
||||
|
||||
@ex[
|
||||
(rs->ht-str-triples (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(z))))
|
||||
]}
|
||||
|
||||
|
||||
@section{Dynamics of reaction systems}
|
||||
|
||||
The dynamics of reaction systems is typically defined as @emph{interaction
|
||||
|
@ -23,3 +197,177 @@ system starts with the initial context. Then, at every step, the result of
|
|||
applying the reaction system is merged with the next element of the context
|
||||
sequence, and the reaction system is then applied to the result of the union.
|
||||
If the sequence of contexts is empty, the reaction system cannot evolve.
|
||||
|
||||
@defstruct*[state ([result (Setof Species)]
|
||||
[rest-contexts (Listof (Setof Species))])]{
|
||||
|
||||
A state of a reaction system is a set of species representing the
|
||||
result of the application of the reactions from the previous steps
|
||||
(the field @racket[result]), plus the rest of the context sequence
|
||||
(the field @racket[rest-contexts]).
|
||||
|
||||
The length of @racket[rest-contexts] dictates for how many steps the
|
||||
reaction system will be run. If it is empty, no more reactions will
|
||||
be applied.
|
||||
|
||||
}
|
||||
|
||||
@deftype[State]{
|
||||
|
||||
The type of the instances of @racket[state].
|
||||
|
||||
}
|
||||
|
||||
@defclass[dynamics% dds% ()]{
|
||||
|
||||
A model of dynamics of a @racket[ReactionSystem].
|
||||
|
||||
@defconstructor[([rs ReactionSystem])]{
|
||||
|
||||
Constructs a model of dynamics for @racket[rs].
|
||||
|
||||
}
|
||||
|
||||
@defmethod[(step/annotated [s State]) (Listof (Pairof (Listof ReactionName) State))]{
|
||||
|
||||
Runs the @racket[ReactionSystem] stored in this class on the given
|
||||
state and produces a list containing one pair, indicated the reactions
|
||||
enabled on @racket[s] and the @racket[State] resulting after applying
|
||||
these reactions.
|
||||
|
||||
@ex[
|
||||
(let* ([rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x)))]
|
||||
[dyn (new dynamics% [rs rs])]
|
||||
[s0 (state (set 'x 'y)
|
||||
(list (set) (set) (set 'x)))])
|
||||
(send dyn step/annotated s0))
|
||||
]}
|
||||
|
||||
}
|
||||
|
||||
@deftype[Dynamics%]{
|
||||
|
||||
The type of an instance of @racket[dynamics%].
|
||||
|
||||
}
|
||||
|
||||
@defproc[(build-interactive-process-graph [rs ReactionSystem]
|
||||
[contexts (Listof (Setof Species))])
|
||||
Graph]{
|
||||
|
||||
Builds the state graph of the reaction system @racket[rs] driven by
|
||||
the context sequence @racket[contexts].
|
||||
|
||||
This function directly calls @method[dds% build-state-graph/annotated]
|
||||
under the hood, and is actually a light interface on top of
|
||||
that function.
|
||||
|
||||
@ex[
|
||||
(let ([rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x)))]
|
||||
[ctx : (Listof (Setof Species)) (list (set) (set) (set 'x))])
|
||||
(dotit (build-interactive-process-graph rs ctx)))
|
||||
]}
|
||||
|
||||
@defproc[(build-interactive-process-graph/simple-states
|
||||
[rs ReactionSystem]
|
||||
[contexts (Listof (Setof Species))])
|
||||
Graph]{
|
||||
|
||||
Builds the state graph of the reaction system @racket[rs] driven by
|
||||
the context sequence @racket[contexts], like
|
||||
@racket[build-interactive-process-graph], but omits the context
|
||||
sequences from the states.
|
||||
|
||||
@bold{Note:} If the reaction system visits the same set of species
|
||||
multiple times, all these visits will be conflated into a single node
|
||||
in the resulting graph. The number or the order of visits will not be
|
||||
reflected in any way.
|
||||
|
||||
@ex[
|
||||
(let ([rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x)))]
|
||||
[ctx : (Listof (Setof Species)) (list (set) (set) (set 'x))])
|
||||
(dotit (build-interactive-process-graph/simple-states rs ctx)))
|
||||
]}
|
||||
|
||||
@defproc[(pretty-print-state-graph/simple-states [sgr Graph]) Graph]{
|
||||
|
||||
Pretty prints the node and edge labels in a reaction system
|
||||
state graph with simple states.
|
||||
|
||||
Simple states, as opposed to @racket[State], do not include the
|
||||
remaining context sequence.
|
||||
See @racket[build-interactive-process-graph/simple-states] for further
|
||||
explanations and examples.
|
||||
|
||||
@ex[
|
||||
(let ([rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x)))]
|
||||
[ctx : (Listof (Setof Species)) (list (set) (set) (set 'x))])
|
||||
(dotit (pretty-print-state-graph/simple-states
|
||||
(build-interactive-process-graph/simple-states rs ctx))))
|
||||
]}
|
||||
|
||||
@defproc[(build-interactive-process [rs ReactionSystem]
|
||||
[ctx (Listof (Setof Species))])
|
||||
(Listof (Pairof (Setof Species) (Setof Species)))]{
|
||||
|
||||
Builds the interactive process driven by the given context sequence.
|
||||
|
||||
The output is a list of pairs of lists in which the first element is
|
||||
the current context and the second element is the result of the
|
||||
application of reactions to the previous state. The interactive
|
||||
process stops one step after the end of the context sequence, to show
|
||||
the effect of the last context.
|
||||
|
||||
@ex[
|
||||
(let ([rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x)))]
|
||||
[ctx : (Listof (Setof Species)) (list (set) (set) (set 'x))])
|
||||
(build-interactive-process rs ctx))
|
||||
]}
|
||||
|
||||
@defproc[(build-interactive-process/org
|
||||
[rs ReactionSystem]
|
||||
[ctx (Listof (Setof Species))])
|
||||
(Listof (Listof (Setof Species)))]{
|
||||
|
||||
Like @racket[build-interactive-process], but the type of a line of the
|
||||
output is @racket[(Listof (Setof Species))] instead of @racket[(Pairof
|
||||
(Setof Species) (Setof Species))].
|
||||
|
||||
This function may be more practical for outputting directly to an
|
||||
Org-mode table.
|
||||
|
||||
@ex[
|
||||
(let ([rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x)))]
|
||||
[ctx : (Listof (Setof Species)) (list (set) (set) (set 'x))])
|
||||
(build-interactive-process/org rs ctx))
|
||||
]}
|
||||
|
||||
@defproc[(pretty-print-state [st State]) String]{
|
||||
|
||||
Pretty prints the context sequence and the current result of
|
||||
@racket[st].
|
||||
|
||||
@ex[
|
||||
(pretty-print-state (state (set 'x 'y) (list (set 'z) (set) (set 'x))))
|
||||
]}
|
||||
|
||||
@defproc[(pretty-print-state-graph [sgr Graph]) Graph]{
|
||||
|
||||
Pretty prints the state graph of a reaction system.
|
||||
|
||||
Note that we need to keep the full context sequence in the name of
|
||||
each state to avoid confusion between the states at different steps of
|
||||
the evolution.
|
||||
|
||||
@ex[
|
||||
(let ([rs (hash 'a (make-reaction '(x) '(y) '(z))
|
||||
'b (make-reaction '(x y) '() '(x)))]
|
||||
[ctx : (Listof (Setof Species)) (list (set) (set) (set 'x))])
|
||||
(dotit (pretty-print-state-graph (build-interactive-process-graph rs ctx))))
|
||||
]}
|
||||
|
|
|
@ -0,0 +1,602 @@
|
|||
#lang scribble/manual
|
||||
@(require scribble/example racket/sandbox
|
||||
(for-label typed/racket/base
|
||||
"../tbn.rkt"
|
||||
"../networks.rkt"
|
||||
"../utils.rkt"
|
||||
"../functions.rkt"
|
||||
"../dynamics.rkt"))
|
||||
|
||||
@(define tbn-evaluator
|
||||
(parameterize ([sandbox-output 'string]
|
||||
[sandbox-error-output 'string]
|
||||
[sandbox-memory-limit 500])
|
||||
(make-evaluator 'typed/racket #:requires '("tbn.rkt"))))
|
||||
|
||||
@(define-syntax-rule (ex . args)
|
||||
(examples #:eval tbn-evaluator . args))
|
||||
|
||||
@(define-syntax-rule (deftypeform . args)
|
||||
(defform #:kind "type" . args))
|
||||
|
||||
@(define-syntax-rule (deftype . args)
|
||||
(defidform #:kind "type" . args))
|
||||
|
||||
@title[#:tag "tbn"]{dds/tbn: Threshold and Sign Boolean Networks (TBN and SBN)}
|
||||
|
||||
@defmodule[dds/tbn]
|
||||
|
||||
@section{TBFs and states}
|
||||
|
||||
This module defines threshold Boolean networks (TBN), as well as sign
|
||||
Boolean networks (SBN). The update functions in such networks are
|
||||
respectively @seclink["tbf" #:doc '(lib
|
||||
"dds/scribblings/dds.scrbl")]{threshold Boolean functions} and sign
|
||||
Boolean functions.
|
||||
|
||||
@defproc[(apply-tbf-to-state [a-tbf TBF] [st (State (U Zero One))])
|
||||
(U Zero One)]{
|
||||
|
||||
Applies a TBF to a state.
|
||||
|
||||
The values of the variables of the state are ordered by
|
||||
@racket[hash-map] and fed to the TBF in order. The number of the
|
||||
inputs of the TBF must match the number of variables in the state.
|
||||
|
||||
@ex[
|
||||
(require "functions.rkt")
|
||||
(apply-tbf-to-state (tbf #(1 1) 1) (hash 'x1 0 'x2 1))
|
||||
]}
|
||||
|
||||
@defstruct*[tbf/state ([weights (VariableMapping Real)]
|
||||
[threshold Real])]{
|
||||
|
||||
A state TBF is a @racket[TBF] with named inputs. A state TBF can be
|
||||
applied to states in an unambiguous ways.
|
||||
|
||||
}
|
||||
|
||||
@deftype[TBF/State]{
|
||||
|
||||
The type of the instances of @racket[tbf/state].
|
||||
|
||||
}
|
||||
|
||||
@deftogether[(@defproc[(tbf/state-w [tbfs TBF/State]) (VariableMapping Real)]
|
||||
@defproc[(tbf/state-θ [tbfs TBF/State]) Real])]{
|
||||
|
||||
Shorter synonyms for field accessors of @racket[tbf/state].
|
||||
|
||||
@ex[
|
||||
(let ([tbfs (tbf/state (hash 'a 1 'b 1) 1)])
|
||||
(values (tbf/state-w tbfs)
|
||||
(tbf/state-θ tbfs)))
|
||||
]}
|
||||
|
||||
@defproc[(make-tbf/state [pairs (Listof (Pairof Variable Real))]
|
||||
[threshold Real])
|
||||
TBF/State]{
|
||||
|
||||
Makes a @racket[TBF/State] from a list of pairs of names of variables
|
||||
and weights, as well as a threshold.
|
||||
|
||||
@ex[
|
||||
(make-tbf/state '((x1 . 1) (x2 . 1)) 1)
|
||||
]}
|
||||
|
||||
@defproc[(sbf/state? [tbfs TBF/State]) Boolean]{
|
||||
|
||||
A state sign Boolean function (SBF) is a @racket[TBF/State] whose
|
||||
threshold is 0.
|
||||
|
||||
@ex[
|
||||
(sbf/state? (tbf/state (hash 'a -1 'b 1) 0))
|
||||
(sbf/state? (tbf/state (hash 'a -1 'b 1) 1))
|
||||
]}
|
||||
|
||||
@defproc[(apply-tbf/state [tbfs TBF/State]
|
||||
[st (State (U Zero One))])
|
||||
(U Zero One)]{
|
||||
|
||||
Applies a @racket[TBF/State] to its inputs given by the state
|
||||
@racket[st].
|
||||
|
||||
Applying a TBF consists in multiplying the weights by the
|
||||
corresponding inputs and comparing the sum of the products to
|
||||
the threshold.
|
||||
|
||||
This function is similar to @racket[apply-tbf], but because it applies
|
||||
a @racket[TBF/State] to a @racket[(State (U Zero One))], it avoids
|
||||
potential mismatches between weights and the corresponding
|
||||
input values.
|
||||
|
||||
@ex[
|
||||
(apply-tbf/state (tbf/state (hash 'a 2 'b -2) 1)
|
||||
(hash 'a 1 'b 0 'c 1))
|
||||
]}
|
||||
|
||||
@defproc[(compact-tbf [tbf TBF/State]) TBF/State]{
|
||||
|
||||
Compacts (and denormalizes) a TBF by removing all inputs which are 0.
|
||||
|
||||
@ex[
|
||||
(compact-tbf (tbf/state (hash 'a 0 'b 1 'c 2 'd 0) 2))
|
||||
]}
|
||||
|
||||
@section{Reading and printing TBFs and SBFs}
|
||||
|
||||
@defproc[(lists+vars->tbfs/state [vars (Listof Variable)]
|
||||
[lsts (Listof (Listof Real))])
|
||||
(Listof TBF/State)]{
|
||||
|
||||
Reads a list of @racket[TBF/State] from a list of list of
|
||||
@racket[Real]s.
|
||||
|
||||
The last element of each list is taken to be the threshold of the
|
||||
TBFs, and the rest of the elements are taken to be the weights.
|
||||
|
||||
@ex[
|
||||
(lists+vars->tbfs/state '(x y) '((1 2 3) (1 1 2)))
|
||||
]}
|
||||
|
||||
@defproc[(lists+headers->tbfs/state [lsts+headers (Pairof (Listof Variable) (Listof (Listof Real)))])
|
||||
(Listof TBF/State)]{
|
||||
|
||||
Like @racket[lists+vars->tbfs/state], but the names of the variables
|
||||
are taken from the first line of @racket[lsts+headers].
|
||||
|
||||
All the lines in @racket[lsts+headers] are assumed to be of the same
|
||||
lenght, which means in particular that the last element of the first
|
||||
line (the threshold column) is discarded.
|
||||
|
||||
@ex[
|
||||
(lists+headers->tbfs/state '((x y f) (1 2 3) (1 1 2)))
|
||||
]}
|
||||
|
||||
@defproc[(lists->tbfs/state [lsts (Listof (Liostf Real))])
|
||||
(Listof TBF/State)]{
|
||||
|
||||
Like @racket[lists+vars->tbfs/state], but the names of the variables
|
||||
are generated as @tt{xi}, where @italic{i} is the index of the
|
||||
variable, starting from 0.
|
||||
|
||||
@ex[
|
||||
(lists->tbfs/state '((1 2 3) (1 1 2)))
|
||||
]}
|
||||
|
||||
@defproc[(lists->tbfs/state/opt-headers
|
||||
[lsts (Listof (Listof (U Variable Real)))]
|
||||
[#:headers hdr Boolean])
|
||||
(Listof TBF/State)]{
|
||||
|
||||
This function allows selecting between @racket[lists->tbfs/state] and
|
||||
@racket[lists+headers->tbfs/state] based on the value of @racket[hdr].
|
||||
If @racket[hdr] is @racket[#f], then @racket[lists->tbfs/state] is
|
||||
applied to @racket[lsts], otherwise @racket[lists+headers->tbfs/state]
|
||||
is applied.
|
||||
|
||||
@ex[
|
||||
(lists->tbfs/state/opt-headers '((1 2 3) (1 1 2)) #:headers #f)
|
||||
(lists->tbfs/state/opt-headers '((x y f) (1 2 3) (1 1 2)) #:headers #t)
|
||||
]}
|
||||
|
||||
@deftogether[(@defproc[(lists+vars->sbfs/state [vars (Listof Variable)]
|
||||
[lsts (Listof (Listof Real))])
|
||||
(Listof TBF/State)]
|
||||
@defproc[(lists+headers->sbfs/state
|
||||
[lsts (Pairof (Listof Variable) (Listof (Listof Real)))])
|
||||
(Listof TBF/State)]
|
||||
@defproc[(lists->sbfs/state [lsts (Listof (Listof Real))])
|
||||
(Listof TBF/State)])]{
|
||||
|
||||
Like the corresponding TBF-related functions, but which create SBFs.
|
||||
In other words, the input lists are treated as lists of weights, and
|
||||
the thresholds are set to 0.
|
||||
|
||||
@ex[
|
||||
(lists+vars->sbfs/state '(x y) '((1 2) (1 1)))
|
||||
(lists+headers->sbfs/state '((x y) (1 2) (1 1)))
|
||||
(lists->sbfs/state '((1 2) (1 1)))
|
||||
]}
|
||||
|
||||
@defproc[(read-org-tbfs/state [str String]) (Listof TBF/State)]{
|
||||
|
||||
Reads a list of @racket[TBF/State] from an Org-mode string containing
|
||||
a sexp, containing a list of lists of numbers. As in
|
||||
@racket[lists->tbfs/state], the last element of each list is taken to
|
||||
be the threshold of the TBF, and the rest of the elements are taken to
|
||||
be the weights.
|
||||
|
||||
Similarly to @racket[lists->tbfs/state], the names of the variables
|
||||
are generated as @tt{xi}, where @italic{i} is the index of the
|
||||
variable, starting from 0.
|
||||
|
||||
@ex[
|
||||
(read-org-tbfs/state "((1 2 3) (1 1 2))")
|
||||
]}
|
||||
|
||||
@defproc[(read-org-tbfs/state+headers [str String]) (Listof TBF/State)]{
|
||||
|
||||
Like @racket[read-org-tbfs/state], but the first list in @racket[str]
|
||||
is taken to contain the names of the variables, similarly to
|
||||
@racket[lists+headers->tbfs/state].
|
||||
|
||||
@ex[
|
||||
(read-org-tbfs/state+headers "((a b f) (1 2 3) (1 1 2))")
|
||||
]}
|
||||
|
||||
@defproc[(tbfs/state->lists [tbfs (Listof TBF/State)]) (Listof (Listof Real))]{
|
||||
|
||||
Given a list of @racket[TBF/State], produces a sexp that Org-mode can
|
||||
interpret as a table.
|
||||
|
||||
All @racket[TBF/State] in the list must have the same inputs.
|
||||
The function does not check this property.
|
||||
|
||||
@ex[
|
||||
(tbfs/state->lists (list (tbf/state (hash 'a 1 'b 2) 3)
|
||||
(tbf/state (hash 'a -2 'b 1) 1)))
|
||||
]}
|
||||
|
||||
@defproc[(tbfs/state->lists+headers [tbfs (Listof TBF/State)])
|
||||
(Pairof (Listof Variable) (Listof (Listof Real)))]{
|
||||
|
||||
Like @racket[tbfs/state->lists+headers], but the first list of the
|
||||
result is the list of input names of the first @racket[TBF/State] in
|
||||
@racket[tbfs]. The last element of this first list is @racket['θ] and
|
||||
corresponds to the column giving the thresholds of the TBFs.
|
||||
|
||||
@ex[
|
||||
(tbfs/state->lists+headers
|
||||
(list (tbf/state (hash 'a 1 'b 2) 3)
|
||||
(tbf/state (hash 'a -2 'b 1) 1)))
|
||||
]}
|
||||
|
||||
@defproc[(sbfs/state->lists [sbfs (Listof TBF/State)])
|
||||
(Listof (Listof Real))]{
|
||||
|
||||
Like @racket[tbfs/state->lists], but the thresholds are omitted.
|
||||
|
||||
@ex[
|
||||
(sbfs/state->lists (list (tbf/state (hash 'a 1 'b 2) 0)
|
||||
(tbf/state (hash 'a -2 'b 1) 0)))
|
||||
]
|
||||
|
||||
Note that this function just drops the threshold, without checking
|
||||
whether it is actually 0:
|
||||
|
||||
@ex[
|
||||
(sbfs/state->lists (list (tbf/state (hash 'a 1 'b 2) 3)))
|
||||
]}
|
||||
|
||||
@defproc[(sbfs/state->lists+headers [sbfs (Listof TBF/State)])
|
||||
(Pairof (Listof Variable) (Listof (Listof Real)))]{
|
||||
|
||||
Like @racket[sbfs/state->lists], but also shows the names of the
|
||||
variables as column headers.
|
||||
|
||||
@ex[
|
||||
(sbfs/state->lists+headers (list (tbf/state (hash 'a 1 'b 2) 0)
|
||||
(tbf/state (hash 'a -2 'b 1) 0)))
|
||||
]}
|
||||
|
||||
@section{Tabulating TBFs and SBFs}
|
||||
|
||||
@defproc[(tabulate-tbfs/state [tbfs (Listof TBF/State)]) (Listof (Listof Real))]{
|
||||
|
||||
Tabulates a list of @racket[TBF/State].
|
||||
|
||||
As in the case of @racket[tbf-tabulate*], the result is a list of
|
||||
lists giving the truth tables of the given TBFs. The first elements
|
||||
of each row give the values of the inputs, while the last elements
|
||||
give the values of each function corresponding to the input.
|
||||
|
||||
All the TBFs must have exactly the same inputs. This function does
|
||||
not check this property.
|
||||
|
||||
@ex[
|
||||
(tabulate-tbfs/state
|
||||
(list (tbf/state (hash 'a 1 'b 2) 2)
|
||||
(tbf/state (hash 'a -2 'b 2) 1)))
|
||||
]}
|
||||
|
||||
@defproc[(tabulate-tbfs/state+headers [tbfs (Listof TBF/State)])
|
||||
(Pairof (Listof Variable)
|
||||
(Listof (Listof Real)))]{
|
||||
|
||||
Like @racket[tabulate-tbfs/state], but the first list of the result is
|
||||
a gives the names of the variables appearing in the inputs of
|
||||
@racket[(car tbfs)], followed by function names. The function names
|
||||
are generated as @tt{fi}, where @tt{i} is the number of the TBF in
|
||||
the list.
|
||||
|
||||
@ex[
|
||||
(tabulate-tbfs/state+headers
|
||||
(list (tbf/state (hash 'a 1 'b 2) 2)
|
||||
(tbf/state (hash 'a -2 'b 2) 1)))
|
||||
]}
|
||||
|
||||
@deftogether[(@defproc[(tabulate-tbf/state [tbf TBF/State])
|
||||
(Listof (Listof Real))]
|
||||
@defproc[(tabulate-tbf/state+headers [tbf TBF/State])
|
||||
(Pairof (Listof Variable) (Listof (Listof Real)))])]{
|
||||
|
||||
Like @racket[tabulate-tbfs/state] and
|
||||
@racket[tabulate-tbfs/state+headers], but only tabulate single TBFs.
|
||||
|
||||
@ex[
|
||||
(tabulate-tbf/state (tbf/state (hash 'a 1 'b 2) 2))
|
||||
(tabulate-tbf/state+headers (tbf/state (hash 'a 1 'b 2) 2))
|
||||
]}
|
||||
|
||||
@section{TBNs and SBNs}
|
||||
|
||||
@deftype[TBN]{
|
||||
|
||||
The type of a TBN, i.e. a mapping assigning to each variable
|
||||
a @racket[TBF/State].
|
||||
|
||||
}
|
||||
|
||||
@defproc[(sbn? [tbn TBN]) Boolean]{
|
||||
|
||||
A SBN is a @racket[TBN] in which all @racket[TBF/State]s satisfy
|
||||
@racket[sbf/state?].
|
||||
|
||||
All functions in @racket[tbn] must only reference variables appearing
|
||||
in the network. This function does not check this condition.
|
||||
|
||||
@ex[
|
||||
(let ([f1 (tbf/state (hash 'a -1 'b 1) 0)]
|
||||
[f2 (tbf/state (hash 'a -1 'b 1) 1)])
|
||||
(values (sbn? (hash 'a f1 'b f1))
|
||||
(sbn? (hash 'a f1 'b f2))))
|
||||
]}
|
||||
|
||||
@defproc[(tbn->network [tbn TBN]) (Network (U Zero One))]{
|
||||
|
||||
Constructs a @racket[Network] out of the given @racket[tbn].
|
||||
|
||||
@ex[
|
||||
(require (only-in "networks.rkt" update))
|
||||
(let* ([tbn-form (hash 'a (tbf/state (hash 'a -1 'b 1) 0)
|
||||
'b (tbf/state (hash 'a -1 'b 1) 1))]
|
||||
[tbn (tbn->network tbn-form)]
|
||||
[s (hash 'a 0 'b 1)])
|
||||
(update tbn s '(a b)))
|
||||
]}
|
||||
|
||||
@defproc[(build-tbn-state-graph [tbn TBN]) Graph]{
|
||||
|
||||
Builds the state graph of a @racket[TBN].
|
||||
|
||||
This function constructs a @racket[(Network (U Zero One))] from
|
||||
@racket[tbn], then builds the state graph of its synchronous dynamics,
|
||||
and pretty-prints the node labels.
|
||||
|
||||
@ex[
|
||||
(require (only-in "utils.rkt" dotit))
|
||||
(dotit (build-tbn-state-graph
|
||||
(hash 'a (tbf/state (hash 'a -1 'b 1) 0)
|
||||
'b (tbf/state (hash 'a -1 'b 1) 1))))
|
||||
]}
|
||||
|
||||
@defproc[(normalized-tbn? [tbn TBN]) Boolean]{
|
||||
|
||||
Checks whether @racket[tbn] is normalized: whether all of the
|
||||
functions have the same inputs, and whether these inputs are exactly
|
||||
the variables of @racket[tbn].
|
||||
|
||||
@ex[
|
||||
(normalized-tbn?
|
||||
(hash 'x (tbf/state (hash 'x 0 'y -1) -1)
|
||||
'y (tbf/state (hash 'x -1 'y 0) -1)))
|
||||
(normalized-tbn?
|
||||
(hash 'x (tbf/state (hash 'x 0 ) -1)
|
||||
'y (tbf/state (hash 'y 0) -1)))
|
||||
]}
|
||||
|
||||
@defproc[(normalize-tbn (tbn TBF)) TBN]{
|
||||
|
||||
Normalizes @racket[tbn]: for every @racket[TBF/State], removes the
|
||||
inputs that are not in the variables of @racket[tbn], and adds missing
|
||||
inputs with 0 weight.
|
||||
|
||||
@ex[
|
||||
(normalize-tbn (hash 'x (tbf/state (hash 'x 2) -1)
|
||||
'y (tbf/state (hash 'y 3) 1)))
|
||||
]}
|
||||
|
||||
@defproc[(compact-tbn [tbn TBN]) TBN]{
|
||||
|
||||
Compacts the @racket[tbn] by removing all inputs which are 0 or which
|
||||
are not variables of the network.
|
||||
|
||||
@ex[
|
||||
(compact-tbn (hash 'a (tbf/state (hash 'a 0 'b 1 'c 3 'd 0) 0)
|
||||
'b (tbf/state (hash 'a -1 'b 1) -1)))
|
||||
]}
|
||||
|
||||
@defproc[(tbn-interaction-graph [tbn TBN]
|
||||
[#:zero-edges zero-edges Boolean #t])
|
||||
Graph]{
|
||||
|
||||
Constructs the interaction graph of @racket[tbn]. The nodes of this
|
||||
graph are labeled with pairs (variable name, threshold), while the
|
||||
edges are labeled with the weights.
|
||||
|
||||
If @racket[#:zero-edges] is @racket[#t], the edges with zero weights
|
||||
will also appear in the interaction graph.
|
||||
|
||||
@ex[
|
||||
(dotit (tbn-interaction-graph (hash 'a (tbf/state (hash 'b 1) 0)
|
||||
'b (tbf/state (hash 'a -1) -1))))
|
||||
(dotit (tbn-interaction-graph (hash 'a (tbf/state (hash 'b 1) 0)
|
||||
'b (tbf/state (hash 'a -1) -1))
|
||||
#:zero-edges #f))
|
||||
]}
|
||||
|
||||
@defproc[(pretty-print-tbn-interaction-graph [ig Graph]) Graph]{
|
||||
|
||||
Pretty prints the node labels of the interaction graph of a TBN.
|
||||
|
||||
@ex[
|
||||
(dotit (pretty-print-tbn-interaction-graph
|
||||
(tbn-interaction-graph (hash 'a (tbf/state (hash 'b 1) 0)
|
||||
'b (tbf/state (hash 'a -1) -1)))))
|
||||
]}
|
||||
|
||||
@defproc[(sbn-interaction-graph [sbn TBN]) Graph]{
|
||||
|
||||
Constructs the interaction graph of @racket[sbn], like
|
||||
@racket[tbn-interaction-graph], but the nodes of the graph are labeled
|
||||
with variable names only. This is an adaptation to SBNs, in which all
|
||||
weights are 0. The function does not check whether @racket[sbn] is
|
||||
indeed an SBN.
|
||||
|
||||
@ex[
|
||||
(dotit (sbn-interaction-graph (hash 'a (tbf/state (hash 'b 1) 0)
|
||||
'b (tbf/state (hash 'a -1) -1))))
|
||||
]}
|
||||
|
||||
@section{Reading and printing TBNs and SBNs}
|
||||
|
||||
@defproc[(parse-org-tbn [tab (Listof (Listof (U Symbol Real)))]
|
||||
[#:headers headers Boolean #t]
|
||||
[#:func-names func-names Boolean #t])
|
||||
TBN]{
|
||||
|
||||
Reads a TBN from a list of lists of numbers or symbols, which may
|
||||
represent an Org-mode table. As in @racket[lists->tbfs/state], the
|
||||
last element of each list is taken to be the threshold of the TBF, and
|
||||
the rest of the elements are taken to be the weights.
|
||||
|
||||
If @racket[headers] is @racket[#t], the names of the variables to
|
||||
appear as the inputs of the TBF are taken from the first list.
|
||||
The last element of this list (corresponding to the column giving the
|
||||
threshold) is discarded. If @racket[headers] is @racket[#f], the
|
||||
names of the variables are generated as @tt{xi}, where @tt{i} is
|
||||
the index of the variable.
|
||||
|
||||
If @racket[func-names] is @racket[#t], the first element in every row
|
||||
except the first one are taken to be the name of the variable to which
|
||||
the TBF should be associated. If @racket[func-names] is @racket[#f],
|
||||
the functions are assigned to variables in lexicographic order.
|
||||
|
||||
@racket[func-names] cannot be @racket[#t] if @racket[headers] is
|
||||
@racket[#f]. The function does not check this condition.
|
||||
|
||||
This is a helper function for @racket[read-org-tbn] and
|
||||
@racket[read-org-sbn].
|
||||
|
||||
@ex[
|
||||
(parse-org-tbn '((1 2 3) (3 2 1)) #:headers #f #:func-names #f)
|
||||
(parse-org-tbn '((a b θ) (1 2 3) (3 2 1)) #:headers #t #:func-names #f)
|
||||
(parse-org-tbn '((dummy a b θ) (b 3 2 1) (a 1 2 3)) #:headers #t #:func-names #t)
|
||||
]}
|
||||
|
||||
@defproc[(read-org-tbn [str String]
|
||||
[#:headers headers Boolean #t]
|
||||
[#:func-names func-names Boolean #t])
|
||||
TBN]{
|
||||
|
||||
Reads a TBN from an string containing a sexp, containing a list of
|
||||
lists of numbers and possibly symbols. This string may be produced by
|
||||
Org-mode.
|
||||
|
||||
As in @racket[lists->tbfs/state], the last element of each list is
|
||||
taken to be the threshold of the TBFs, and the rest of the elements
|
||||
are taken to be the weights.
|
||||
|
||||
As in @racket[parse-org-tbn], if @racket[headers] is @racket[#t], the
|
||||
names of the variables to appear as the inputs of the TBF are taken
|
||||
from the first list. The last element of this list is discarded.
|
||||
If @racket[headers] is @racket[#f], the names of the variables are
|
||||
generated as @tt{xi}, where @tt{i} is the index of the variable.
|
||||
|
||||
If @racket[func-names] is @racket[#t], the first element in every row
|
||||
except the first one, are taken to be the name of the variable to
|
||||
which the TBF should be associated. If @racket[func-names] is
|
||||
@racket[#f], the functions are assigned to variables in
|
||||
alphabetical order.
|
||||
|
||||
As in @racket[parse-org-tbn], @racket[func-names] cannot be
|
||||
@racket[#t] if @racket[headers] is @racket[#f]. The function does not
|
||||
check this condition.
|
||||
|
||||
@ex[
|
||||
(read-org-tbn "((\"-\" \"x\" \"y\" \"θ\") (\"y\" -1 0 -1) (\"x\" 0 -1 -1))")
|
||||
]}
|
||||
|
||||
@defproc[(read-org-sbn [str String]
|
||||
[#:headers headers Boolean #t]
|
||||
[#:func-names func-names Boolean #t])
|
||||
TBN]{
|
||||
|
||||
Like @racket[read-org-tbn], but reads an SBN from the input string,
|
||||
i.e. all the numbers are taken to be the weights, and the threshold is
|
||||
set to 0.
|
||||
|
||||
@ex[
|
||||
(read-org-sbn "((\"-\" \"x\" \"y\") (\"y\" -1 0) (\"x\" 0 -1))")
|
||||
]}
|
||||
|
||||
@defproc[(tbn->lists [tbn TBN]
|
||||
[#:headers headers Boolean #t]
|
||||
[#:func-names func-names Boolean #t])
|
||||
(Listof (Listof (U Symbol Real)))]{
|
||||
|
||||
Given a @racket[tbn], produces a list of lists of numbers or symbols,
|
||||
containing the description of the functions of the TBN. This list can
|
||||
be read back by @racket[parse-org-tbn], and Org-mode can interpret it
|
||||
as a table.
|
||||
|
||||
Similarly to @racket[parse-org-tbn], if @racket[#:headers] is
|
||||
@racket[#f], this function does not print the names of the inputs of
|
||||
the TBFs. If @racket[#:headers] is @racket[#t], the output starts by
|
||||
a list giving the names of the variables, as well as the symbol
|
||||
@racket['θ] to represent the column giving the thresholds of the TBF.
|
||||
If @racket[#:func-names] is @racket[#t], the first column of the table
|
||||
gives the name of the variable which the corresponding TBF updates.
|
||||
|
||||
If both @racket[#:func-names] and @racket[#:headers] are @racket[#t],
|
||||
the first cell of the first column contains the symbol
|
||||
@racket['-].
|
||||
|
||||
@ex[
|
||||
(tbn->lists (hash 'a (tbf/state (hash 'b 1) 0)
|
||||
'b (tbf/state (hash 'a -1) -1)))
|
||||
]}
|
||||
|
||||
@defproc[(sbn->lists [sbn TBN]
|
||||
[#:headers headers Boolean #t]
|
||||
[#:func-names func-names Boolean #t])
|
||||
(Listof (Listof (U Symbol Real)))]{
|
||||
|
||||
Like @racket[tbn->lists], but does not show the thresholds—an
|
||||
adaptation for printing SBNs.
|
||||
|
||||
@ex[
|
||||
(sbn->lists (hash 'a (tbf/state (hash 'b 1) 0)
|
||||
'b (tbf/state (hash 'a -1) 0)))
|
||||
]}
|
||||
|
||||
@section{Miscellaneous utilities}
|
||||
|
||||
@defproc[(group-truth-table-by-nai [tt (Listof (Listof Integer))])
|
||||
(Listof (Listof (Listof Integer)))]{
|
||||
|
||||
Given the truth table @racket[tt] of a Boolean function, groups the
|
||||
lines by the @italic{N}umber of @italic{A}ctivated @italic{I}nputs—the
|
||||
number of inputs which are 1 in the input vector.
|
||||
|
||||
@ex[
|
||||
(group-truth-table-by-nai '((0 0 0 1)
|
||||
(0 0 1 1)
|
||||
(0 1 0 0)
|
||||
(0 1 1 1)
|
||||
(1 0 0 0)
|
||||
(1 0 1 0)
|
||||
(1 1 0 1)
|
||||
(1 1 1 0)))
|
||||
]}
|
|
@ -1,6 +1,26 @@
|
|||
#lang scribble/manual
|
||||
@(require scribble/example racket/sandbox
|
||||
(for-label racket graph "../utils.rkt"))
|
||||
(for-label typed/racket/base graph
|
||||
"../utils.rkt"
|
||||
(only-in typed/graph Graph)
|
||||
(only-in racket/set set)
|
||||
(only-in racket/stream stream->list stream-take)
|
||||
(only-in typed/racket/class class super-new new send)))
|
||||
|
||||
@(define utils-evaluator
|
||||
(parameterize ([sandbox-output 'string]
|
||||
[sandbox-error-output 'string]
|
||||
[sandbox-memory-limit 500])
|
||||
(make-evaluator 'typed/racket #:requires '("utils.rkt"))))
|
||||
|
||||
@(define-syntax-rule (ex . args)
|
||||
(examples #:eval utils-evaluator . args))
|
||||
|
||||
@(define-syntax-rule (deftypeform . args)
|
||||
(defform #:kind "type" . args))
|
||||
|
||||
@(define-syntax-rule (deftype . args)
|
||||
(defidform #:kind "polymorphic type" . args))
|
||||
|
||||
@title[#:tag "utils"]{dds/utils: Various Utilities}
|
||||
|
||||
|
@ -10,12 +30,103 @@ This module defines miscellaneous utilities, supporting the other modules of
|
|||
the package: evaluating sexps, manipulating lists,
|
||||
@hyperlink["https://orgmode.org/"]{Org-mode} interoperability, etc.
|
||||
|
||||
@section{Base types}
|
||||
|
||||
@(define utils-evaluator
|
||||
(parameterize ([sandbox-output 'string]
|
||||
[sandbox-error-output 'string]
|
||||
[sandbox-memory-limit 50])
|
||||
(make-evaluator 'racket/base #:requires '("utils.rkt"))))
|
||||
@deftype[Variable]{
|
||||
|
||||
Any Racket symbol. Designates a variable in a discrete dynamical network.
|
||||
|
||||
}
|
||||
|
||||
@deftypeform[(VariableMapping A)]{
|
||||
|
||||
An immutable mapping (a hash table) assigning elements of type @racket[A] to
|
||||
the variables.
|
||||
|
||||
}
|
||||
|
||||
@defform[(NonemptyListof a)]{
|
||||
|
||||
A @racket[(Listof a)] which contains at least one element.
|
||||
|
||||
}
|
||||
|
||||
@section{Type utilities}
|
||||
|
||||
Typed Racket's @racket[cast] should only be used as a last resort, because it
|
||||
installs two contracts which may have a significant performance penalty.
|
||||
See
|
||||
@hyperlink["https://racket.discourse.group/t/managing-cast-performance-penalty/905"]{this
|
||||
discussion} for more details. The best approach is to use
|
||||
@hyperlink["https://docs.racket-lang.org/ts-guide/occurrence-typing.html"]{occurrence
|
||||
typing} either via a direct @racket[if] check using a predicate, or using
|
||||
@racket[assert].
|
||||
|
||||
@defform[(assert-type expr type)]{
|
||||
|
||||
@racket[assert] that the type of @racket[expr] is @racket[type].
|
||||
|
||||
@ex[
|
||||
(define some-number : Any 1)
|
||||
(assert-type some-number Integer)
|
||||
(assert-type some-number Positive-Integer)
|
||||
(eval:error (assert-type some-number Zero1))
|
||||
]}
|
||||
|
||||
@deftogether[(@defform[(for/first/typed type-ann (for-clause ...) expr ...+)])
|
||||
@defform[(for*/first/typed type-ann (for-clause ...) expr ...+)]]{
|
||||
|
||||
Typed versions of @racket[for/first] and @racket[for*/first].
|
||||
|
||||
@ex[
|
||||
(for/first/typed : (Option Integer)
|
||||
([i (in-range 1 10)]
|
||||
#:when (zero? (modulo i 5)))
|
||||
(* i 3))
|
||||
(for*/first/typed : (Option (Pairof Integer Integer))
|
||||
([i (in-range 1 10)]
|
||||
[j (in-range 1 10)]
|
||||
#:when (> (+ i j) 5))
|
||||
(cons i j))
|
||||
]
|
||||
|
||||
The implementation of these macros is a simplified version the definition of
|
||||
@hyperlink["https://github.com/racket/typed-racket/blob/9d3264c97aa63b6a7163a219937b88a612add8ab/typed-racket-lib/typed-racket/base-env/prims.rkt#L512"]{@racket[define-for/acc:-variant]}.
|
||||
|
||||
}
|
||||
|
||||
@defform[(define/abstract/error method-name args ...)]{
|
||||
|
||||
In a typed class, defines a public method @racket[method-name] with the
|
||||
arguments @racket[args] and with the body announcing that this method
|
||||
is abstract.
|
||||
|
||||
@ex[
|
||||
(define my-abstract-class%
|
||||
(class object%
|
||||
(super-new)
|
||||
|
||||
(: abstract-increment (-> Integer Integer))
|
||||
(define/abstract/error (abstract-increment x))))
|
||||
|
||||
(define obj (new my-abstract-class%))
|
||||
(eval:error (send obj abstract-increment 1))
|
||||
]}
|
||||
|
||||
@defform[(relax-arg-type/any name arg-type)]{
|
||||
|
||||
Defines a unary anonymous function whose argument type is @racket[Any], and
|
||||
which calls @racket[name], with the argument coerced to @racket[arg-type].
|
||||
|
||||
@ex[
|
||||
(relax-arg-type/any add1 Number)
|
||||
]
|
||||
|
||||
The main use of this macro is to allow easily passing different one-argument
|
||||
functions as arguments of the type @racket[(-> Any Any)]. See for example
|
||||
@racket[update-graph].
|
||||
|
||||
}
|
||||
|
||||
@section{Hashtable injection}
|
||||
|
||||
|
@ -23,12 +134,12 @@ This section defines some utilities to streamline the usage of hash tables
|
|||
mapping symbols to values. The goal is essentially to avoid having to write
|
||||
explicit @racket[hash-ref] calls.
|
||||
|
||||
@defproc[(eval-with [ht variable-mapping?] [expr any/c]) any]{
|
||||
@defproc[(eval-with [ht (VariableMapping Any)] [expr Any]) AnyValues]{
|
||||
|
||||
Temporarily injects the mappings from the given hash table as bindings in
|
||||
a namespace including @racket[racket/base] and then evaluates the expression.
|
||||
|
||||
@examples[#:eval utils-evaluator
|
||||
@ex[
|
||||
(let ([ht (hash 'a 1 'b 1)])
|
||||
(eval-with ht '(+ b a 1)))
|
||||
]
|
||||
|
@ -37,15 +148,104 @@ The local bindings from the current lexical scope are not
|
|||
conserved. Therefore, the following outputs an error about a
|
||||
missing identifier:
|
||||
|
||||
@examples[#:eval utils-evaluator
|
||||
@ex[
|
||||
(eval:error
|
||||
(let ([ht (hash 'a 1 'b 1)]
|
||||
[z 1])
|
||||
(eval-with ht '(+ b z a 1)))
|
||||
)]}
|
||||
|
||||
@defproc[(eval1-with [ht (VariableMapping Any)] [expr Any]) Any]{
|
||||
|
||||
Like @racket[eval-with], but returns only the first value computed by
|
||||
@racket[expr].
|
||||
|
||||
@ex[
|
||||
(let ([ht (hash 'a 1 'b 1)])
|
||||
(eval1-with ht '(+ b a 1)))
|
||||
]}
|
||||
|
||||
@defform[(auto-hash-ref/explicit stx)
|
||||
#:contracts ([stx (VariableMapping A)])]{
|
||||
|
||||
Given a @racket[VariableMapping] and a sequence of symbols, binds these symbols
|
||||
to the values they are associated with in the hash table, then puts the body in
|
||||
the context of these bindings.
|
||||
|
||||
@ex[
|
||||
(define env #hash((a . 1) (b . 2)))
|
||||
(auto-hash-ref/explicit (env a b) (+ a (* 2 b)))
|
||||
]
|
||||
|
||||
Note that only one expression can be supplied in the body.
|
||||
|
||||
}
|
||||
|
||||
@defform[(auto-hash-ref/: stx)
|
||||
#:contracts ([stx (VariableMapping A)])]{
|
||||
|
||||
Given an expression and a @racket[VariableMapping], looks up the symbols with
|
||||
a leading semicolon and binds them to the value they are associated with in the
|
||||
hash table.
|
||||
|
||||
@ex[
|
||||
(define env #hash((a . 1) (b . 2)))
|
||||
(auto-hash-ref/: env (+ :a (* 2 :b)))
|
||||
]
|
||||
|
||||
Thus the symbol @racket[:a] is matched to the key @racket['a] in the
|
||||
hash table.
|
||||
|
||||
Note that only one expression can be supplied in the body.
|
||||
|
||||
}
|
||||
|
||||
@deftogether[(@defform*[((lambda/: body) (lambda/: type body))]
|
||||
@defform*[((λ/: body) (λ/: type body))])]{
|
||||
|
||||
Defines an anonymous function with the body @racket[body], taking a hash table
|
||||
as an argument, and applying @racket[auto-hash-ref/:] to @racket[body] in the
|
||||
context of this hash table.
|
||||
|
||||
@ex[
|
||||
(let ([ht (hash 'a 1 'b 2)])
|
||||
((λ/: (+ :a :b)) ht))
|
||||
]
|
||||
|
||||
If the optional annotation @racket[type] is specified, the only argument of the
|
||||
resulting lambda will be of type @racket[type].
|
||||
|
||||
@ex[
|
||||
(let ([ht (hash 'a 1 'b 2)])
|
||||
((λ/: (HashTable Symbol Natural) (+ :a :b)) ht))
|
||||
]}
|
||||
|
||||
@defform*[((define/: name body)
|
||||
(define/: name type body))]{
|
||||
|
||||
A shortcut for @racket[(define name (lambda/: body))], with the optional
|
||||
type annotation.
|
||||
|
||||
@ex[
|
||||
(let ([ht (hash 'a 1 'b 2)])
|
||||
(: f (-> (HashTable Symbol Natural) Natural))
|
||||
(define/: f (+ :a :b))
|
||||
(f ht))
|
||||
]}
|
||||
|
||||
@section{Analysis of quoted expressions}
|
||||
|
||||
@defproc[(extract-symbols [form Any]) (Listof Symbol)]{
|
||||
|
||||
Produces a list of symbols appearing in the quoted expression
|
||||
passed in the first argument.
|
||||
|
||||
@ex[
|
||||
(extract-symbols '(1 (2 3) x (y z 3)))
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
@section{Org-mode interoperability}
|
||||
|
||||
Org-mode supports laying out the output of code blocks as tables, which is very
|
||||
|
@ -58,16 +258,436 @@ See
|
|||
@hyperlink["https://git.marvid.fr/scolobb/dds/src/branch/master/example/example.org"]{example.org}
|
||||
for examples of usage.
|
||||
|
||||
@defproc[(any->string [x Any]) String]{
|
||||
|
||||
Converts any value to string by calling @racket[display] on it and capturing
|
||||
the result in a string.
|
||||
|
||||
@ex[
|
||||
(any->string '(a 1 (x y)))
|
||||
]}
|
||||
|
||||
@defproc[(stringify-variable-mapping [ht (VariableMapping Any)]) (VariableMapping String)]{
|
||||
|
||||
Converts all the values of a @racket[VariableMapping] to string.
|
||||
|
||||
@ex[
|
||||
(stringify-variable-mapping (hash 'a '(and a b) 'b '(not b)))
|
||||
]}
|
||||
|
||||
@defproc[(string->any [str String]) Any]{
|
||||
|
||||
Reads any value from string.
|
||||
|
||||
@ex[
|
||||
(string->any "(or b (not a))")
|
||||
]}
|
||||
|
||||
@defproc[(map-sexp [func (-> Any Any)] [sexp Any]) Any]{
|
||||
|
||||
Given a @racket[Sexp], applies the @racket[func] to any object which is not
|
||||
a list.
|
||||
|
||||
@racket[map-sexp] will not check whether @racket[func] is indeed applicable to
|
||||
every non-list element of @racket[sexp]. If this is not the case, a contract
|
||||
violation for func will be generated.
|
||||
|
||||
@ex[
|
||||
(map-sexp (λ (x) (add1 (cast x Number))) '(1 2 (4 10) 3))
|
||||
]}
|
||||
|
||||
@defproc*[([(read-org-sexp [str String]) Any]
|
||||
[(unorg [str String]) Any])]{
|
||||
|
||||
Reads a @racket[sexp] from a string produced by Org-mode for a named table.
|
||||
|
||||
@racket[unorg] is a shortcut for @racket[read-org-sexp].
|
||||
|
||||
@ex[
|
||||
(unorg "(#t \"#t\" \"#t \" '(1 2 \"#f\"))")
|
||||
]}
|
||||
|
||||
@defform[(GeneralPair A B)]{
|
||||
|
||||
A @racket[(Pair A B)] or a @racket[(List A B)].
|
||||
|
||||
}
|
||||
|
||||
@defproc[(unstringify-pairs [pairs (Listof (GeneralPair String Any))])
|
||||
(Listof (GeneralPair Symbol Any))]{
|
||||
|
||||
Given a list of pairs of strings and some other values (possibly strings),
|
||||
converts the first element of each pair to a string, and reads the second
|
||||
element with @racket[string->any] or keeps it as is if it is not a string.
|
||||
|
||||
@ex[
|
||||
(unstringify-pairs '(("a" . 1) ("b" . "(and a (not b))")))
|
||||
]}
|
||||
|
||||
@defproc*[([(read-org-variable-mapping [str String]) (VariableMapping Any)]
|
||||
[(unorgv [str String]) (VariableMapping Any)])]{
|
||||
|
||||
Reads a @racket[VariableMapping] from a string, such as the one which Org-mode
|
||||
produces from tables.
|
||||
|
||||
@racket[unorgv] is a synonym of @racket[read-org-variable-mapping].
|
||||
|
||||
@ex[
|
||||
(read-org-variable-mapping
|
||||
"((\"a\" . \"(and a b)\") (\"b\" . \"(or b (not a))\"))")
|
||||
]}
|
||||
|
||||
@defproc[(read-symbol-list (str String)) (Listof Symbol)]{
|
||||
|
||||
Reads a list of symbols from a string.
|
||||
|
||||
@ex[
|
||||
(read-symbol-list "a b c")
|
||||
]}
|
||||
|
||||
@defproc[(drop-first-last (str String)) String]{
|
||||
|
||||
Removes the first and the last symbol of a given string.
|
||||
|
||||
Useful for removing the parentheses in string representations of lists.
|
||||
|
||||
@ex[
|
||||
(drop-first-last "(a b)")
|
||||
]}
|
||||
|
||||
@defproc[(list-sets->list-strings (lst (Listof (Setof Any)))) (Listof String)]{
|
||||
|
||||
Converts a list of sets of symbols to a list of strings containing
|
||||
those symbols.
|
||||
|
||||
@ex[
|
||||
(list-sets->list-strings (list (set 'x 'y) (set 'z) (set) (set 't)))
|
||||
]}
|
||||
|
||||
@section[#:tag "utils_Pretty_printing"]{Pretty printing}
|
||||
|
||||
@defproc[(pretty-print-set [s (U (Setof Any) (Listof Any))]) String]{
|
||||
|
||||
Pretty prints a set by listing its elements in alphabetic order.
|
||||
|
||||
@ex[
|
||||
(pretty-print-set (set 'a 'b 1))
|
||||
(pretty-print-set (list 'a 'b 1))
|
||||
]}
|
||||
|
||||
@defproc[(pretty-print-set-sets [ms (U (Listof (Setof Any)) (Setof (Setof Any)))])
|
||||
String]{
|
||||
|
||||
Pretty-prints a set of sets of symbols.
|
||||
|
||||
Typically used for pretty-printing the annotations on the edges of
|
||||
a state graph.
|
||||
|
||||
@ex[
|
||||
(pretty-print-set-sets (set (set 'a 'b) (set 'c)))
|
||||
(pretty-print-set-sets (list (set 'a 'b) (set 'c)))
|
||||
]}
|
||||
|
||||
@section{Additional graph utilities}
|
||||
|
||||
@section{Pretty printing}
|
||||
All examples in this section depend on @racket[typed/graph]:
|
||||
|
||||
@ex[
|
||||
(require typed/graph)
|
||||
]
|
||||
|
||||
@defproc[(dotit [graph Graph]) Void]{
|
||||
|
||||
Typesets the graph via @racket[graphviz] and @racket[display]s it.
|
||||
|
||||
@ex[
|
||||
(dotit (weighted-graph/directed '((1 a b) (2 b c))))
|
||||
]}
|
||||
|
||||
@defproc[(update-vertices/unweighted [graph Graph] [func (-> Any Any)]) Graph]{
|
||||
|
||||
Applies a transformation to every vertex in the unweighted graph and returns
|
||||
the new graph.
|
||||
|
||||
If the transformation function maps two vertices to the same values, these
|
||||
vertices will be merged in the resulting graph. The transformation function
|
||||
may be called multiple times for the same vertex.
|
||||
|
||||
This function does not rely on @racket[rename-vertex!], so it can be used to
|
||||
permute vertex labels.
|
||||
|
||||
@ex[
|
||||
(define g (directed-graph '((a b) (b c))))
|
||||
(define (double-labels [x : Any])
|
||||
(define x-str (symbol->string (cast x Symbol)))
|
||||
(string->symbol (string-append x-str x-str)))
|
||||
(dotit (update-vertices/unweighted g double-labels))
|
||||
]}
|
||||
|
||||
@defproc[(update-graph [graph Graph]
|
||||
[#:v-func v-func (-> Any Any) identity]
|
||||
[#:e-func e-func (-> Any Any) identity])
|
||||
Graph]{
|
||||
|
||||
Given a (directed) graph, apply the transformation @racket[v-func] to every
|
||||
vertex label and, if the graph is a weighted graph, the transformation
|
||||
@racket[e-func] to every edge label. Both transformations default to identity
|
||||
functions. If @racket[graph] is an weighted graph, the result is a weighted
|
||||
graph. If @racket[graph] is an unweighted graph, the result is an
|
||||
unweighted graph.
|
||||
|
||||
@ex[
|
||||
(define g (weighted-graph/directed '((10 a b) (11 b c))))
|
||||
(define (double-labels [x : Any])
|
||||
(define x-str (symbol->string (cast x Symbol)))
|
||||
(string->symbol (string-append x-str x-str)))
|
||||
(define (double-edges [x : Any])
|
||||
(* 2 (cast x Number)))
|
||||
(dotit (update-graph g #:v-func double-labels #:e-func double-edges))
|
||||
]}
|
||||
|
||||
@section{Additional list and hash map utilities}
|
||||
|
||||
@section{Functions and procedures}
|
||||
@defproc[(collect-by-key [keys (Listof a)] [vals (Listof b)])
|
||||
(Values (Listof a) (Listof (Listof b)))]{
|
||||
|
||||
Given a list of keys and the corresponding values, collects all the values
|
||||
associated to any given key and returns a list of keys without duplicates, and
|
||||
a list containing the corresponding list of values.
|
||||
|
||||
If @racket[keys] can be treated as edges (i.e. pairs of vertices), the results
|
||||
produced by this function are suitable for graph constructors.
|
||||
|
||||
@ex[
|
||||
(collect-by-key '(a b a) '(1 2 3))
|
||||
]}
|
||||
|
||||
@defproc[(collect-by-key/sets [keys (Listof a)] [vals (Listof b)])
|
||||
(Values (Listof a) (Listof (Setof b)))]{
|
||||
|
||||
Like @racket[collect-by-key], but produce a list of sets instead of a list
|
||||
of lists.
|
||||
|
||||
@ex[
|
||||
(collect-by-key/sets '(a b a) '(1 2 3))
|
||||
]}
|
||||
|
||||
@defproc[(ht-values/list->set [ht (HashTable a (Listof b))])
|
||||
(HashTable a (Setof b))]{
|
||||
|
||||
Converts the values of a hash table from lists to sets.
|
||||
|
||||
@ex[
|
||||
(ht-values/list->set #hash((a . (1 1))))
|
||||
]}
|
||||
|
||||
@defproc[(hash->list/ordered [ht (HashTable a b)])
|
||||
(Listof (Pairof a b))]{
|
||||
|
||||
|
||||
Returns the key-value pairs of a given hash table in the order in which the
|
||||
hash table orders them for @racket[hash-map].
|
||||
|
||||
@bold{TODO:} Remove after Typed Racket has caught up with Racket 8.4, in which
|
||||
@racket[hash->list] gets a new optional argument @racket[try-order?].
|
||||
|
||||
@ex[
|
||||
(hash->list/ordered #hash((b . 1) (a . 1)))
|
||||
]}
|
||||
|
||||
@defproc[(hash-replace-keys/ordered [ht (Immutable-HashTable (K1 V))]
|
||||
[new-keys (Listof K2)])
|
||||
(Immutable-HashTable K2 V)]{
|
||||
|
||||
Replaces the keys in @racket[ht] by the keys in @racket[new-keys].
|
||||
|
||||
The key-value pairs of the hash table @racket[ht] are processed in the
|
||||
order produced by @racket[hash-map] with @racket[#:try-order?] set to
|
||||
@racket[#t].
|
||||
|
||||
@ex[
|
||||
(hash-replace-keys/ordered (hash 'a 1 'b 2) '(x y))
|
||||
]}
|
||||
|
||||
@defproc[(multi-split-at [lists (Listof (Listof a))]
|
||||
[pos Integer])
|
||||
(Values (Listof (Listof a)) (Listof (Listof a)))]{
|
||||
|
||||
Given a list of lists, splits every single list at the given position, and then
|
||||
returns two lists of lists: one consisting of the first halves, and the one
|
||||
consisting of the second halves.
|
||||
|
||||
@ex[
|
||||
(multi-split-at '((1 2 3) (a b c)) 2)
|
||||
]}
|
||||
|
||||
@defproc[(lists-transpose [lists (List (Listof a) ... a)])
|
||||
(Listof (List a ... a))]{
|
||||
|
||||
Transposes a list of lists. The length of the resulting list is the length of
|
||||
the shortest list in @racket[lists].
|
||||
|
||||
This function is essentially @racket[in-parallel], wrapped in
|
||||
a couple conversions.
|
||||
|
||||
@ex[
|
||||
(lists-transpose '((a b) (1 2)))
|
||||
(lists-transpose '((a b) (1 2 3) (#t)))
|
||||
]
|
||||
|
||||
As of 2022-04-07, Typed Racket cannot convert the type of
|
||||
@racket[lists-transpose] to a contract. The @seclink["utils/untyped"]{untyped
|
||||
submodule} provides a version of this function which can be used in
|
||||
untyped code.
|
||||
|
||||
}
|
||||
|
||||
@defproc[(append-lists [lsts (Listof (List (Listof a) (Listof a)))])
|
||||
(Listof (Listof a))]{
|
||||
|
||||
@racket[lsts] is a list of rows, in which each row is split in two halves.
|
||||
The function returns the list of the same rows, with the two halves appended.
|
||||
|
||||
@ex[
|
||||
(append-lists '(((1 2) (a b))
|
||||
((3 4) (c d))))
|
||||
]}
|
||||
|
||||
@section{Randomness}
|
||||
|
||||
@defproc*[([(in-random) (Sequenceof Flonum)]
|
||||
[(in-random [k Integer]) (Sequenceof Nonnegative-Fixnum)]
|
||||
[(in-random [min Integer] [max Integer]) (Sequenceof Nonnegative-Fixnum)])]{
|
||||
|
||||
Generates a stream of (inexact) random numbers. The meaning of the arguments
|
||||
is the same as for the function @racket[random]:
|
||||
|
||||
@itemlist[
|
||||
|
||||
@item{@racket[(in-random)] — a stream of random inexact numbers between
|
||||
0 and 1,}
|
||||
|
||||
@item{@racket[(in-random k)] — a stream of random exact integers in the range
|
||||
@racket[0] to @racket[k]-1.}
|
||||
|
||||
@item{@racket[(in-random min max)] — a stream of random exact integers the
|
||||
range @racket[min] to @racket[max]-1.}
|
||||
|
||||
]
|
||||
|
||||
@ex[
|
||||
(require typed/racket/stream)
|
||||
(stream->list (stream-take (in-random) 5))
|
||||
(stream->list (stream-take (in-random 10) 5))
|
||||
(stream->list (stream-take (in-random 5 10) 5))
|
||||
]}
|
||||
|
||||
@section{Additional stream utilities}
|
||||
|
||||
@defproc[(cartesian-product-2/stream [s1 (Sequenceof a)]
|
||||
[s2 (Sequenceof b)])
|
||||
(Sequenceof (Pair a b))]{
|
||||
|
||||
Generates a stream containing all the pairs of the elements from @racket[s1]
|
||||
and @racket[s2]. The elements of @racket[s2] are enumerated in order for every
|
||||
element of the @racket[s1], taken in order as well.
|
||||
|
||||
@ex[
|
||||
(require typed/racket/stream)
|
||||
(stream->list (cartesian-product-2/stream (in-range 1 5) '(a b)))
|
||||
]
|
||||
|
||||
The streams can be infinite. If the second stream is infinite, only the first
|
||||
element of @racket[s1] will be enumerated.
|
||||
|
||||
@ex[
|
||||
(stream->list (stream-take (cartesian-product-2/stream '(a b) (in-naturals)) 10))
|
||||
]}
|
||||
|
||||
@defproc[(cartesian-product/stream [ss (Listof (Sequenceof a))])
|
||||
(Sequenceof (Listof a))]{
|
||||
|
||||
Generates a stream containing all the elements of the Cartesian product between
|
||||
the streams of @racket[ss].
|
||||
|
||||
This function relies on @racket[cartesian-product-2/stream] to build the
|
||||
Cartesian product, so it has the same properties with respect to the order in
|
||||
which the streams are enumerated.
|
||||
|
||||
Union types can be used to build the Cartesian product of streams containing
|
||||
values of different types.
|
||||
|
||||
@ex[
|
||||
(stream->list (cartesian-product/stream (list (in-range 3) (in-range 4 6) '(a b))))
|
||||
]}
|
||||
|
||||
@section{Boolean operations}
|
||||
|
||||
@defproc[(boolean-power [n Integer])
|
||||
(Listof (Listof Boolean))]{
|
||||
|
||||
Returns the @racket[n]-th Cartesian power of the Boolean domain.
|
||||
|
||||
@ex[
|
||||
(boolean-power 2)
|
||||
]}
|
||||
|
||||
@defproc[(boolean-power/stream [n Integer])
|
||||
(Sequenceof (Listof Boolean))]{
|
||||
|
||||
Like @racket[boolean-power], but returns a stream.
|
||||
|
||||
@ex[
|
||||
(stream->list (boolean-power/stream 2))
|
||||
]}
|
||||
|
||||
@defproc[(any->01 [x Any]) (U Zero One)]{
|
||||
|
||||
Converts any non-@racket[#f] value to 1 and @racket[#f] to 0.
|
||||
|
||||
@ex[
|
||||
(any->01 #t)
|
||||
(any->01 #f)
|
||||
(any->01 'hello)
|
||||
]}
|
||||
|
||||
@defproc[(01->boolean [x (U Zero One)]) Boolean]{
|
||||
|
||||
Converts 0 to @racket[#f] and 1 to @racket[#t].
|
||||
|
||||
@ex[
|
||||
(01->boolean 0)
|
||||
(01->boolean 1)
|
||||
]}
|
||||
|
||||
@section[#:tag "utils/untyped"]{Untyped definitions}
|
||||
|
||||
@defmodule[(submod dds/utils untyped)]
|
||||
|
||||
@(require (for-label (only-in racket/contract/base listof any/c)))
|
||||
|
||||
This submodule contains some functions whose types cannot be converted to
|
||||
contracts by Typed Racket.
|
||||
|
||||
@(define utils-evaluator/untyped
|
||||
(parameterize ([sandbox-output 'string]
|
||||
[sandbox-error-output 'string]
|
||||
[sandbox-memory-limit 50])
|
||||
(make-evaluator 'racket #:requires '((submod "utils.rkt" untyped)))))
|
||||
|
||||
@(define-syntax-rule (ex/untyped . args)
|
||||
(examples #:eval utils-evaluator/untyped . args))
|
||||
|
||||
@defproc[(lists-transpose [lists (listof (listof any/c))])
|
||||
(listof (listof any/c))]{
|
||||
|
||||
Transposes a list of lists. The length of the resulting list is the length of
|
||||
the shortest list in @racket[lists].
|
||||
|
||||
This function is essentially @racket[in-parallel], wrapped in
|
||||
a couple conversions.
|
||||
|
||||
@ex/untyped[
|
||||
(lists-transpose '((a b) (1 2)))
|
||||
(lists-transpose '((a b) (1 2 3) (#t)))
|
||||
]}
|
||||
|
|
|
@ -0,0 +1,715 @@
|
|||
#lang typed/racket
|
||||
|
||||
(require (except-in "utils.rkt" lists-transpose)
|
||||
"utils.rkt" "functions.rkt" "networks.rkt"
|
||||
typed/graph typed/racket/random)
|
||||
|
||||
(require/typed racket/hash
|
||||
[hash-intersect
|
||||
(->* ((HashTable Variable Real))
|
||||
(#:combine (-> Real Real Real))
|
||||
#:rest (HashTable Variable Real)
|
||||
(HashTable Variable Real))]
|
||||
[(hash-intersect hash-intersect/tbn-tbf)
|
||||
(->* ((HashTable Variable TBF/State))
|
||||
(#:combine (-> TBF/State Real Real))
|
||||
#:rest (HashTable Variable Real)
|
||||
(HashTable Variable Real))]
|
||||
[hash-union
|
||||
(->* ((HashTable Variable Real))
|
||||
(#:combine (-> Real Real Real))
|
||||
#:rest (HashTable Variable Real)
|
||||
(HashTable Variable Real))])
|
||||
|
||||
(module+ test
|
||||
(require typed/rackunit))
|
||||
|
||||
(provide
|
||||
apply-tbf-to-state
|
||||
|
||||
(struct-out tbf/state) TBF/State tbf/state-w tbf/state-θ make-tbf/state
|
||||
sbf/state? apply-tbf/state compact-tbf
|
||||
|
||||
lists+vars->tbfs/state lists+headers->tbfs/state lists->tbfs/state
|
||||
lists+vars->sbfs/state lists+headers->sbfs/state lists->sbfs/state
|
||||
read-org-tbfs/state read-org-tbfs/state+headers
|
||||
tbfs/state->lists tbfs/state->lists+headers lists->tbfs/state/opt-headers
|
||||
sbfs/state->lists sbfs/state->lists+headers
|
||||
|
||||
tabulate-tbfs/state tabulate-tbfs/state+headers
|
||||
tabulate-tbf/state tabulate-tbf/state+headers
|
||||
|
||||
group-truth-table-by-nai
|
||||
|
||||
TBN sbn? tbn->network
|
||||
build-tbn-state-graph normalized-tbn? normalize-tbn compact-tbn
|
||||
tbn-interaction-graph pretty-print-tbn-interaction-graph
|
||||
sbn-interaction-graph
|
||||
parse-org-tbn read-org-tbn read-org-sbn tbn->lists sbn->lists
|
||||
)
|
||||
|
||||
(: apply-tbf-to-state (-> TBF (State (U Zero One)) (U Zero One)))
|
||||
(define (apply-tbf-to-state tbf st)
|
||||
(apply-tbf tbf (list->vector
|
||||
(hash-map st (λ (_ [val : (U Zero One)]) val) #t))))
|
||||
|
||||
(module+ test
|
||||
(test-case "apply-tbf-to-state"
|
||||
(define st (hash 'x1 0 'x2 1))
|
||||
(define f (tbf #(1 1) 1))
|
||||
(check-equal? (apply-tbf-to-state f st) 0)))
|
||||
|
||||
(struct tbf/state ([weights : (VariableMapping Real)]
|
||||
[threshold : Real])
|
||||
#:transparent
|
||||
#:type-name TBF/State)
|
||||
(define tbf/state-w tbf/state-weights)
|
||||
(define tbf/state-θ tbf/state-threshold)
|
||||
|
||||
(: make-tbf/state (-> (Listof (Pairof Variable Real)) Real TBF/State))
|
||||
(define (make-tbf/state pairs threshold)
|
||||
(tbf/state (make-immutable-hash pairs) threshold))
|
||||
|
||||
(module+ test
|
||||
(test-case "tbf/state"
|
||||
(define f (make-tbf/state '((x1 . 1) (x2 . 1)) 1))
|
||||
(check-equal? (tbf/state-w f) #hash((x1 . 1) (x2 . 1)))
|
||||
(check-equal? (tbf/state-θ f) 1)))
|
||||
|
||||
(: sbf/state? (-> TBF/State Boolean))
|
||||
(define (sbf/state? tbfs) (zero? (tbf/state-θ tbfs)))
|
||||
|
||||
(module+ test
|
||||
(test-case "sbf/state?"
|
||||
(check-true (sbf/state? (tbf/state (hash 'a -1 'b 1) 0)))
|
||||
(check-false (sbf/state? (tbf/state (hash 'a -1 'b 1) 1)))))
|
||||
|
||||
(: apply-tbf/state (-> TBF/State (State (U Zero One)) (U Zero One)))
|
||||
(define (apply-tbf/state tbfs st)
|
||||
(any->01
|
||||
(> (apply + (hash-values (hash-intersect (tbf/state-w tbfs) st #:combine *)))
|
||||
(tbf/state-θ tbfs))))
|
||||
|
||||
(module+ test
|
||||
(test-case "apply-tbf/state"
|
||||
(define st1 (hash 'a 1 'b 0 'c 1))
|
||||
(define st2 (hash 'a 1 'b 1 'c 0))
|
||||
(define tbf (make-tbf/state '((a . 2) (b . -2)) 1))
|
||||
(check-equal? (apply-tbf/state tbf st1) 1)
|
||||
(check-equal? (apply-tbf/state tbf st2) 0)))
|
||||
|
||||
(: compact-tbf (-> TBF/State TBF/State))
|
||||
(define (compact-tbf tbf)
|
||||
(tbf/state
|
||||
(for/hash : (VariableMapping Real)
|
||||
([(k v) (in-hash (tbf/state-w tbf))]
|
||||
#:unless (zero? v))
|
||||
(values k v))
|
||||
(tbf/state-θ tbf)))
|
||||
|
||||
(module+ test
|
||||
(test-case "compact-tbf"
|
||||
(check-equal? (compact-tbf (tbf/state (hash 'a 0 'b 1 'c 2 'd 0) 2))
|
||||
(tbf/state '#hash((b . 1) (c . 2)) 2))))
|
||||
|
||||
(: lists+vars->tbfs/state (-> (Listof Variable) (Listof (Listof Real))
|
||||
(Listof TBF/State)))
|
||||
(define (lists+vars->tbfs/state vars lsts)
|
||||
(for/list ([lst (in-list lsts)])
|
||||
(define-values (ws θ) (split-at-right lst 1))
|
||||
(make-tbf/state (for/list ([x (in-list vars)]
|
||||
[w (in-list ws)])
|
||||
(cons x w))
|
||||
(car θ))))
|
||||
|
||||
(module+ test
|
||||
(test-case "lists+vars->tbfs/state"
|
||||
(check-equal? (lists+vars->tbfs/state '(x y) '((1 2 3) (1 1 2)))
|
||||
(list (tbf/state '#hash((x . 1) (y . 2)) 3)
|
||||
(tbf/state '#hash((x . 1) (y . 1)) 2)))))
|
||||
|
||||
(: lists+headers->tbfs/state (-> (Pairof (Listof Variable) (Listof (Listof Real)))
|
||||
(Listof TBF/State)))
|
||||
(define (lists+headers->tbfs/state lsts+headers)
|
||||
(lists+vars->tbfs/state (drop-right (car lsts+headers) 1)
|
||||
(cdr lsts+headers)))
|
||||
|
||||
(module+ test
|
||||
(test-case "lists+headers->tbfs/state"
|
||||
(check-equal? (lists+headers->tbfs/state '((x y f) (1 2 3) (1 1 2)))
|
||||
(list (tbf/state '#hash((x . 1) (y . 2)) 3)
|
||||
(tbf/state '#hash((x . 1) (y . 1)) 2)))))
|
||||
|
||||
(: lists->tbfs/state (-> (Listof (Listof Real)) (Listof TBF/State)))
|
||||
(define (lists->tbfs/state lsts)
|
||||
(lists+vars->tbfs/state
|
||||
(for/list ([i (in-range (length (car lsts)))])
|
||||
(string->symbol (format "x~a" i)))
|
||||
lsts))
|
||||
|
||||
(module+ test
|
||||
(test-case "lists->tbfs/state"
|
||||
(check-equal? (lists->tbfs/state '((1 2 3) (1 1 2)))
|
||||
(list (tbf/state '#hash((x0 . 1) (x1 . 2)) 3)
|
||||
(tbf/state '#hash((x0 . 1) (x1 . 1)) 2)))))
|
||||
|
||||
(: lists->tbfs/state/opt-headers (-> (Listof (Listof (U Variable Real)))
|
||||
#:headers Boolean
|
||||
(Listof TBF/State)))
|
||||
(define (lists->tbfs/state/opt-headers lsts #:headers hdr)
|
||||
(if hdr
|
||||
(lists+headers->tbfs/state
|
||||
(assert-type lsts (Pairof (Listof Variable) (Listof (Listof Real)))))
|
||||
(lists->tbfs/state
|
||||
(assert-type lsts (Listof (Listof Real))))))
|
||||
|
||||
(module+ test
|
||||
(test-case "lists->tbfs/state/opt-headers"
|
||||
(check-equal?
|
||||
(lists->tbfs/state/opt-headers '((1 2 3) (1 1 2)) #:headers #f)
|
||||
(list (tbf/state '#hash((x0 . 1) (x1 . 2)) 3)
|
||||
(tbf/state '#hash((x0 . 1) (x1 . 1)) 2)))
|
||||
(check-equal?
|
||||
(lists->tbfs/state/opt-headers '((x y f) (1 2 3) (1 1 2)) #:headers #t)
|
||||
(list (tbf/state '#hash((x . 1) (y . 2)) 3)
|
||||
(tbf/state '#hash((x . 1) (y . 1)) 2)))))
|
||||
|
||||
(: lists+vars->sbfs/state (-> (Listof Variable) (Listof (Listof Real))
|
||||
(Listof TBF/State)))
|
||||
(define (lists+vars->sbfs/state vars lsts)
|
||||
(for/list ([lst (in-list lsts)])
|
||||
(make-tbf/state (map (inst cons Variable Real) vars lst) 0)))
|
||||
|
||||
(module+ test
|
||||
(test-case "lists+vars->sbfs/state"
|
||||
(check-equal? (lists+vars->sbfs/state '(x y) '((1 2) (1 1)))
|
||||
(list (tbf/state '#hash((x . 1) (y . 2)) 0)
|
||||
(tbf/state '#hash((x . 1) (y . 1)) 0)))))
|
||||
|
||||
(: lists+headers->sbfs/state (-> (Pairof (Listof Variable) (Listof (Listof Real)))
|
||||
(Listof TBF/State)))
|
||||
(define (lists+headers->sbfs/state lsts)
|
||||
(lists+vars->sbfs/state (car lsts) (cdr lsts)))
|
||||
|
||||
(module+ test
|
||||
(test-case "lists+headers->sbfs/state"
|
||||
(check-equal? (lists+headers->sbfs/state '((x y) (1 2) (1 1)))
|
||||
(list (tbf/state '#hash((x . 1) (y . 2)) 0)
|
||||
(tbf/state '#hash((x . 1) (y . 1)) 0)))))
|
||||
|
||||
(: lists->sbfs/state (-> (Listof (Listof Real)) (Listof TBF/State)))
|
||||
(define (lists->sbfs/state lsts)
|
||||
(lists+vars->sbfs/state
|
||||
(for/list ([i (in-range (length (car lsts)))])
|
||||
(string->symbol (format "x~a" i)))
|
||||
lsts))
|
||||
|
||||
(module+ test
|
||||
(test-case "lists->sbfs/state"
|
||||
(check-equal? (lists->sbfs/state '((1 2) (1 1)))
|
||||
(list
|
||||
(tbf/state '#hash((x0 . 1) (x1 . 2)) 0)
|
||||
(tbf/state '#hash((x0 . 1) (x1 . 1)) 0)))))
|
||||
|
||||
(: read-org-tbfs/state (-> String (Listof TBF/State)))
|
||||
(define (read-org-tbfs/state str)
|
||||
(lists->tbfs/state
|
||||
(assert-type (read-org-sexp str)
|
||||
(Listof (Listof Real)))))
|
||||
|
||||
(module+ test
|
||||
(test-case "read-org-tbfs/state"
|
||||
(check-equal? (read-org-tbfs/state "((1 2 3) (1 1 2))")
|
||||
(list (tbf/state '#hash((x0 . 1) (x1 . 2)) 3)
|
||||
(tbf/state '#hash((x0 . 1) (x1 . 1)) 2)))))
|
||||
|
||||
(: read-org-tbfs/state+headers (-> String (Listof TBF/State)))
|
||||
(define (read-org-tbfs/state+headers str)
|
||||
(lists+headers->tbfs/state
|
||||
(assert-type (read-org-sexp str)
|
||||
(Pairof (Listof Variable) (Listof (Listof Real))))))
|
||||
|
||||
(module+ test
|
||||
(test-case "read-org-tbfs/state+headers"
|
||||
(check-equal? (read-org-tbfs/state+headers "((a b f) (1 2 3) (1 1 2))")
|
||||
(list (tbf/state '#hash((a . 1) (b . 2)) 3)
|
||||
(tbf/state '#hash((a . 1) (b . 1)) 2)))))
|
||||
|
||||
(: tbfs/state->lists (-> (Listof TBF/State) (Listof (Listof Real))))
|
||||
(define (tbfs/state->lists tbfs)
|
||||
(for/list ([tbf (in-list tbfs)])
|
||||
(append (hash-map (tbf/state-w tbf) (λ (_ [w : Real]) w) #t)
|
||||
(list (tbf/state-θ tbf)))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tbfs/state->lists"
|
||||
(check-equal?
|
||||
(tbfs/state->lists (list (tbf/state (hash 'a 1 'b 2) 3)
|
||||
(tbf/state (hash 'a -2 'b 1) 1)))
|
||||
'((1 2 3) (-2 1 1)))))
|
||||
|
||||
(: tbfs/state->lists+headers (-> (Listof TBF/State)
|
||||
(Pairof (Listof Variable)
|
||||
(Listof (Listof Real)))))
|
||||
(define (tbfs/state->lists+headers tbfs)
|
||||
(cons (append (hash-map (tbf/state-w (car tbfs))
|
||||
(λ ([x : Symbol] _) x) #t)
|
||||
'(θ))
|
||||
(tbfs/state->lists tbfs)))
|
||||
|
||||
(module+ test
|
||||
(test-case "tbfs/state->list+headers"
|
||||
(check-equal?
|
||||
(tbfs/state->lists+headers (list (tbf/state (hash 'a 1 'b 2) 3)
|
||||
(tbf/state (hash 'a -2 'b 1) 1)))
|
||||
'((a b θ)
|
||||
(1 2 3)
|
||||
(-2 1 1)))))
|
||||
|
||||
(: sbfs/state->lists (-> (Listof TBF/State) (Listof (Listof Real))))
|
||||
(define (sbfs/state->lists tbfs)
|
||||
(for/list ([tbf (in-list tbfs)])
|
||||
(hash-map (tbf/state-w tbf) (λ (_ [w : Real]) w) #t)))
|
||||
|
||||
(module+ test
|
||||
(test-case "sbfs/state->lists"
|
||||
(check-equal?
|
||||
(sbfs/state->lists (list (tbf/state (hash 'a 1 'b 2) 0)
|
||||
(tbf/state (hash 'a -2 'b 1) 0)))
|
||||
'((1 2) (-2 1)))))
|
||||
|
||||
(: sbfs/state->lists+headers (-> (Listof TBF/State)
|
||||
(Pairof (Listof Variable)
|
||||
(Listof (Listof Real)))))
|
||||
(define (sbfs/state->lists+headers tbfs)
|
||||
(cons (hash-map (tbf/state-w (car tbfs))
|
||||
(λ ([x : Symbol] _) x) #t)
|
||||
(sbfs/state->lists tbfs)))
|
||||
|
||||
(module+ test
|
||||
(test-case "sbfs/state->list+headers"
|
||||
(check-equal?
|
||||
(sbfs/state->lists+headers (list (tbf/state (hash 'a 1 'b 2) 0)
|
||||
(tbf/state (hash 'a -2 'b 1) 0)))
|
||||
'((a b)
|
||||
(1 2)
|
||||
(-2 1)))))
|
||||
|
||||
(: tabulate-tbfs/state (-> (Listof TBF/State) (Listof (Listof Real))))
|
||||
(define (tabulate-tbfs/state tbfs)
|
||||
(define vars (hash-map (tbf/state-w (car tbfs)) (λ ([x : Variable] _) x) #t))
|
||||
(tabulate-state* (map (curry apply-tbf/state) tbfs)
|
||||
(make-same-domains vars '(0 1))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate-tbfs/state"
|
||||
(check-equal? (tabulate-tbfs/state
|
||||
(list (tbf/state (hash 'a 1 'b 2) 2)
|
||||
(tbf/state (hash 'a -2 'b 2) 1)))
|
||||
'((0 0 0 0)
|
||||
(0 1 0 1)
|
||||
(1 0 0 0)
|
||||
(1 1 1 0)))))
|
||||
|
||||
(: tabulate-tbfs/state+headers (-> (Listof TBF/State) (Pairof (Listof Variable)
|
||||
(Listof (Listof Real)))))
|
||||
(define (tabulate-tbfs/state+headers tbfs)
|
||||
(define vars (hash-map (tbf/state-w (car tbfs)) (λ ([x : Variable] _) x) #t))
|
||||
(tabulate-state*+headers (map (curry apply-tbf/state) tbfs)
|
||||
(make-same-domains vars '(0 1))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate-tbfs/state+headers"
|
||||
(check-equal? (tabulate-tbfs/state+headers
|
||||
(list (tbf/state (hash 'a 1 'b 2) 2)
|
||||
(tbf/state (hash 'a -2 'b 2) 1)))
|
||||
'((a b f1 f2)
|
||||
(0 0 0 0)
|
||||
(0 1 0 1)
|
||||
(1 0 0 0)
|
||||
(1 1 1 0)))))
|
||||
|
||||
(: tabulate-tbf/state (-> TBF/State (Listof (Listof Real))))
|
||||
(define (tabulate-tbf/state tbf)
|
||||
(tabulate-tbfs/state (list tbf)))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate-tbf/state"
|
||||
(check-equal? (tabulate-tbf/state (tbf/state (hash 'a 1 'b 2) 2))
|
||||
'((0 0 0)
|
||||
(0 1 0)
|
||||
(1 0 0)
|
||||
(1 1 1)))))
|
||||
|
||||
(: tabulate-tbf/state+headers (-> TBF/State (Pairof (Listof Variable)
|
||||
(Listof (Listof Real)))))
|
||||
(define (tabulate-tbf/state+headers tbf)
|
||||
(tabulate-tbfs/state+headers (list tbf)))
|
||||
|
||||
(module+ test
|
||||
(test-case "tabulate-tbf/state+headers"
|
||||
(check-equal? (tabulate-tbf/state+headers (tbf/state (hash 'a 1 'b 2) 2))
|
||||
'((a b f1)
|
||||
(0 0 0)
|
||||
(0 1 0)
|
||||
(1 0 0)
|
||||
(1 1 1)))))
|
||||
|
||||
(: group-truth-table-by-nai (-> (Listof (Listof Integer))
|
||||
(Listof (Listof (Listof Integer)))))
|
||||
(define (group-truth-table-by-nai tt)
|
||||
(: sum (-> (Listof Integer) Integer))
|
||||
(define (sum xs) (foldl + 0 xs))
|
||||
(group-by (λ ([row : (Listof Integer)])
|
||||
(drop-right row 1))
|
||||
tt
|
||||
(λ ([in1 : (Listof Integer)] [in2 : (Listof Integer)])
|
||||
(= (sum in1) (sum in2)))))
|
||||
|
||||
(module+ test
|
||||
(test-case "group-truth-table-by-nai"
|
||||
(check-equal? (group-truth-table-by-nai '((0 0 0 1)
|
||||
(0 0 1 1)
|
||||
(0 1 0 0)
|
||||
(0 1 1 1)
|
||||
(1 0 0 0)
|
||||
(1 0 1 0)
|
||||
(1 1 0 1)
|
||||
(1 1 1 0)))
|
||||
'(((0 0 0 1))
|
||||
((0 0 1 1) (0 1 0 0) (1 0 0 0))
|
||||
((0 1 1 1) (1 0 1 0) (1 1 0 1))
|
||||
((1 1 1 0))))))
|
||||
|
||||
(define-type TBN (HashTable Variable TBF/State))
|
||||
|
||||
(: sbn? (-> TBN Boolean))
|
||||
(define (sbn? tbn) (andmap sbf/state? (hash-values tbn)))
|
||||
|
||||
(module+ test
|
||||
(test-case "sbn?"
|
||||
(define f1 (tbf/state (hash 'a -1 'b 1) 0))
|
||||
(define f2 (tbf/state (hash 'a -1 'b 1) 1))
|
||||
(check-true (sbn? (hash 'a f1 'b f1)))
|
||||
(check-false (sbn? (hash 'a f1 'b f2))))
|
||||
)
|
||||
|
||||
(: tbn->network (-> TBN (Network (U Zero One))))
|
||||
(define (tbn->network tbn)
|
||||
(make-01-network
|
||||
(for/hash : (VariableMapping (UpdateFunction (U Zero One)))
|
||||
([(x tbfx) (in-hash tbn)])
|
||||
(values x (λ ([s : (State (U Zero One))])
|
||||
(apply-tbf/state tbfx s))))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tbn->network"
|
||||
(define tbn-form (hash 'a (tbf/state (hash 'a -1 'b 1) 0)
|
||||
'b (tbf/state (hash 'a -1 'b 1) 1)))
|
||||
(define tbn (tbn->network tbn-form))
|
||||
(define s (hash 'a 0 'b 1))
|
||||
(check-equal? (update tbn s '(a b))
|
||||
#hash((a . 1) (b . 0)))
|
||||
(check-equal? (network-domains tbn)
|
||||
#hash((a . (0 1)) (b . (0 1))))))
|
||||
|
||||
(: build-tbn-state-graph (-> TBN Graph))
|
||||
(define (build-tbn-state-graph tbn)
|
||||
(pretty-print-state-graph
|
||||
((inst build-full-state-graph (U Zero One))
|
||||
((inst make-syn-dynamics (U Zero One))
|
||||
(tbn->network tbn)))))
|
||||
|
||||
(module+ test
|
||||
(test-case "build-tbn-state-graph"
|
||||
(check-equal? (graphviz
|
||||
(build-tbn-state-graph
|
||||
(hash 'a (tbf/state (hash 'a -1 'b 1) 0)
|
||||
'b (tbf/state (hash 'a -1 'b 1) 1))))
|
||||
"digraph G {\n\tnode0 [label=\"a:0 b:0\"];\n\tnode1 [label=\"a:1 b:1\"];\n\tnode2 [label=\"a:0 b:1\"];\n\tnode3 [label=\"a:1 b:0\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t\tnode0 -> node0 [];\n\t}\n\tsubgraph D {\n\t\tnode1 -> node0 [];\n\t\tnode2 -> node3 [];\n\t\tnode3 -> node0 [];\n\t}\n}\n")))
|
||||
|
||||
(: normalized-tbn? (-> TBN Boolean))
|
||||
(define (normalized-tbn? tbn)
|
||||
(define tbn-vars (hash-keys tbn))
|
||||
(for/and ([tbf (in-list (hash-values tbn))])
|
||||
(set=? tbn-vars (hash-keys (tbf/state-w tbf)))))
|
||||
|
||||
(module+ test
|
||||
(test-case "normalized-tbn?"
|
||||
(check-true (normalized-tbn?
|
||||
(hash 'x (tbf/state (hash 'x 0 'y -1) -1)
|
||||
'y (tbf/state (hash 'x -1 'y 0) -1))))
|
||||
(check-false (normalized-tbn?
|
||||
(hash 'x (tbf/state (hash 'x 0 ) -1)
|
||||
'y (tbf/state (hash 'y 0) -1))))))
|
||||
|
||||
(: normalize-tbn (-> TBN TBN))
|
||||
(define (normalize-tbn tbn)
|
||||
(define vars-0 (for/hash : (VariableMapping Real)
|
||||
([(x _) (in-hash tbn)])
|
||||
(values x 0)))
|
||||
(: normalize-tbf (-> TBF/State TBF/State))
|
||||
(define (normalize-tbf tbf)
|
||||
;; Only keep the inputs which are also the variables of tbn.
|
||||
(define w-pruned (hash-intersect/tbn-tbf
|
||||
tbn
|
||||
(tbf/state-w tbf)
|
||||
#:combine (λ (_ w) w)))
|
||||
;; Put in the missing inputs with weight 0.
|
||||
(define w-complete
|
||||
(assert-type (hash-union vars-0 w-pruned #:combine (λ (_ w) w))
|
||||
(VariableMapping Real)))
|
||||
(tbf/state w-complete (tbf/state-θ tbf)))
|
||||
(for/hash : TBN ([(x tbf) (in-hash tbn)])
|
||||
(values x (normalize-tbf tbf))))
|
||||
|
||||
(module+ test
|
||||
(test-case "normalize-tbn"
|
||||
(check-equal? (normalize-tbn (hash 'x (tbf/state (hash 'x 2) -1)
|
||||
'y (tbf/state (hash 'y 3) 1)))
|
||||
(hash 'x (tbf/state (hash 'x 2 'y 0) -1)
|
||||
'y (tbf/state (hash 'x 0 'y 3) 1)))))
|
||||
|
||||
(: compact-tbn (-> TBN TBN))
|
||||
(define (compact-tbn tbn)
|
||||
(: remove-0-non-var (-> TBF/State TBF/State))
|
||||
(define (remove-0-non-var tbf)
|
||||
(tbf/state (for/hash : (VariableMapping Real)
|
||||
([(x w) (in-hash (tbf/state-w tbf))]
|
||||
#:when (hash-has-key? tbn x)
|
||||
#:unless (zero? w))
|
||||
(values x w))
|
||||
(tbf/state-θ tbf)))
|
||||
(for/hash : TBN ([(x tbf) (in-hash tbn)])
|
||||
(values x (remove-0-non-var tbf))))
|
||||
|
||||
(module+ test
|
||||
(test-case "compact-tbn"
|
||||
(check-equal?
|
||||
(compact-tbn (hash 'a (tbf/state (hash 'a 0 'b 1 'c 3 'd 0) 0)
|
||||
'b (tbf/state (hash 'a -1 'b 1) -1)))
|
||||
(hash
|
||||
'a
|
||||
(tbf/state '#hash((b . 1)) 0)
|
||||
'b
|
||||
(tbf/state '#hash((a . -1) (b . 1)) -1)))))
|
||||
|
||||
(: tbn-interaction-graph (->* (TBN) (#:zero-edges Boolean) Graph))
|
||||
(define (tbn-interaction-graph tbn #:zero-edges [zero-edges #t])
|
||||
(define ntbn (normalize-tbn tbn))
|
||||
(define ig (weighted-graph/directed
|
||||
(if zero-edges
|
||||
(for*/list : (Listof (List Real Variable Variable))
|
||||
([(tar tbf) (in-hash ntbn)]
|
||||
[(src w) (in-hash (tbf/state-w tbf))])
|
||||
(list w src tar))
|
||||
(for*/list : (Listof (List Real Variable Variable))
|
||||
([(tar tbf) (in-hash ntbn)]
|
||||
[(src w) (in-hash (tbf/state-w tbf))]
|
||||
#:unless (zero? w))
|
||||
(list w src tar)))))
|
||||
(update-graph
|
||||
ig #:v-func (λ (x) (cons x (tbf/state-θ (hash-ref ntbn (assert-type x Variable)))))))
|
||||
|
||||
(module+ test
|
||||
(test-case "tbn-interaction-graph"
|
||||
(define tbn (hash 'a (tbf/state (hash 'b 1) 0)
|
||||
'b (tbf/state (hash 'a -1) -1)))
|
||||
(check-equal? (graphviz (tbn-interaction-graph tbn))
|
||||
"digraph G {\n\tnode0 [label=\"'(b . -1)\"];\n\tnode1 [label=\"'(a . 0)\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t\tnode0 -> node0 [label=\"0\"];\n\t\tnode1 -> node1 [label=\"0\"];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [label=\"1\"];\n\t\tnode1 -> node0 [label=\"-1\"];\n\t}\n}\n")
|
||||
(check-equal? (graphviz (tbn-interaction-graph tbn #:zero-edges #f))
|
||||
"digraph G {\n\tnode0 [label=\"'(b . -1)\"];\n\tnode1 [label=\"'(a . 0)\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [label=\"1\"];\n\t\tnode1 -> node0 [label=\"-1\"];\n\t}\n}\n")))
|
||||
|
||||
(: pretty-print-tbn-interaction-graph (-> Graph Graph))
|
||||
(define (pretty-print-tbn-interaction-graph ig)
|
||||
(update-graph ig #:v-func (match-lambda
|
||||
[(cons var weight) (~a var ":" weight)])))
|
||||
|
||||
(module+ test
|
||||
(test-case "pretty-print-tbn-interaction-graph"
|
||||
(define tbn (hash 'a (tbf/state (hash 'b 1) 0)
|
||||
'b (tbf/state (hash 'a -1) -1)))
|
||||
(check-equal? (graphviz (pretty-print-tbn-interaction-graph
|
||||
(tbn-interaction-graph tbn)))
|
||||
"digraph G {\n\tnode0 [label=\"a:0\"];\n\tnode1 [label=\"b:-1\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t\tnode0 -> node0 [label=\"0\"];\n\t\tnode1 -> node1 [label=\"0\"];\n\t}\n\tsubgraph D {\n\t\tnode0 -> node1 [label=\"-1\"];\n\t\tnode1 -> node0 [label=\"1\"];\n\t}\n}\n")))
|
||||
|
||||
(: sbn-interaction-graph (-> TBN Graph))
|
||||
(define (sbn-interaction-graph sbn #:zero-edges [zero-edges #t])
|
||||
(update-graph (tbn-interaction-graph sbn #:zero-edges zero-edges)
|
||||
#:v-func (match-lambda
|
||||
[(cons var _) var])))
|
||||
|
||||
(module+ test
|
||||
(test-case "sbn-interaction-graph"
|
||||
(define sbn (hash 'a (tbf/state (hash 'b 2) 0)
|
||||
'b (tbf/state (hash 'a 2) 0)))
|
||||
(check-equal? (graphviz (sbn-interaction-graph sbn))
|
||||
"digraph G {\n\tnode0 [label=\"a\"];\n\tnode1 [label=\"b\"];\n\tsubgraph U {\n\t\tedge [dir=none];\n\t\tnode0 -> node1 [label=\"2\"];\n\t\tnode0 -> node0 [label=\"0\"];\n\t\tnode1 -> node1 [label=\"0\"];\n\t}\n\tsubgraph D {\n\t}\n}\n")))
|
||||
|
||||
(: parse-org-tbn (->* ((Listof (Listof (U Symbol Real))))
|
||||
(#:headers Boolean
|
||||
#:func-names Boolean)
|
||||
TBN))
|
||||
(define (parse-org-tbn tab #:headers [headers #t] #:func-names [func-names #t])
|
||||
(cond [func-names
|
||||
(define-values (vars rows) (multi-split-at tab 1))
|
||||
(define tbfs (lists->tbfs/state/opt-headers rows #:headers headers))
|
||||
(for/hash : TBN
|
||||
([tbf (in-list tbfs)]
|
||||
[var (in-list (cdr vars))])
|
||||
(values (assert-type (car var) Variable) tbf))]
|
||||
[else
|
||||
(define tbfs (lists->tbfs/state/opt-headers tab #:headers headers))
|
||||
(define vars (hash-map (tbf/state-w (car tbfs)) (λ (x _) x) #t))
|
||||
(for/hash : TBN ([tbf (in-list tbfs)] [var (in-list vars)])
|
||||
(values (assert-type var Variable) tbf))]))
|
||||
|
||||
(module+ test
|
||||
(test-case "parse-org-tbn"
|
||||
(check-equal?
|
||||
(parse-org-tbn '((1 2 3) (3 2 1)) #:headers #f #:func-names #f)
|
||||
(hash 'x0 (tbf/state '#hash((x0 . 1) (x1 . 2)) 3)
|
||||
'x1 (tbf/state '#hash((x0 . 3) (x1 . 2)) 1)))
|
||||
(check-equal?
|
||||
(parse-org-tbn '((a b θ) (1 2 3) (3 2 1)) #:headers #t #:func-names #f)
|
||||
(hash
|
||||
'a
|
||||
(tbf/state '#hash((a . 1) (b . 2)) 3)
|
||||
'b
|
||||
(tbf/state '#hash((a . 3) (b . 2)) 1)))
|
||||
(check-equal?
|
||||
(parse-org-tbn '((dummy a b θ) (b 3 2 1) (a 1 2 3))
|
||||
#:headers #t
|
||||
#:func-names #t)
|
||||
(hash 'a (tbf/state '#hash((a . 1) (b . 2)) 3)
|
||||
'b (tbf/state '#hash((a . 3) (b . 2)) 1)))))
|
||||
|
||||
(: read-org-tbn (->* (String) (#:headers Boolean #:func-names Boolean) TBN))
|
||||
(define (read-org-tbn str
|
||||
#:headers [headers #t]
|
||||
#:func-names [func-names #t])
|
||||
(parse-org-tbn (assert-type (read-org-sexp str)
|
||||
(Listof (Listof (U Symbol Real))))
|
||||
#:headers headers
|
||||
#:func-names func-names))
|
||||
|
||||
(module+ test
|
||||
(test-case "read-org-tbn"
|
||||
(check-equal?
|
||||
(read-org-tbn "((\"-\" \"x\" \"y\" \"θ\") (\"y\" -1 0 -1) (\"x\" 0 -1 -1))")
|
||||
(hash
|
||||
'x
|
||||
(tbf/state '#hash((x . 0) (y . -1)) -1)
|
||||
'y
|
||||
(tbf/state '#hash((x . -1) (y . 0)) -1)))
|
||||
(check-equal?
|
||||
(read-org-tbn "((\"x\" \"y\" \"θ\") (-1 0 -1) (0 -1 -1))" #:func-names #f)
|
||||
(hash
|
||||
'x
|
||||
(tbf/state '#hash((x . -1) (y . 0)) -1)
|
||||
'y
|
||||
(tbf/state '#hash((x . 0) (y . -1)) -1)))
|
||||
(check-equal?
|
||||
(read-org-tbn "((-1 0 -1) (0 -1 -1))" #:headers #f #:func-names #f)
|
||||
(hash
|
||||
'x0
|
||||
(tbf/state '#hash((x0 . -1) (x1 . 0)) -1)
|
||||
'x1
|
||||
(tbf/state '#hash((x0 . 0) (x1 . -1)) -1)))))
|
||||
|
||||
(: read-org-sbn (->* (String) (#:headers Boolean #:func-names Boolean) TBN))
|
||||
(define (read-org-sbn str
|
||||
#:headers [headers #t]
|
||||
#:func-names [func-names #t])
|
||||
(define sexp (assert-type (read-org-sexp str)
|
||||
(Listof (Listof (U Symbol Real)))))
|
||||
;; Inject the 0 thresholds into the rows of the sexp we have just read.
|
||||
(: inject-0 (-> (Listof (Listof (U Symbol Real))) (Listof (Listof (U Symbol Real)))))
|
||||
(define (inject-0 rows)
|
||||
(for/list : (Listof (Listof (U Symbol Real)))
|
||||
([row (in-list rows)]) (append row '(0))))
|
||||
(define sexp-ready (if headers
|
||||
(cons (append (car sexp) '(θ)) (inject-0 (cdr sexp)))
|
||||
(inject-0 sexp)))
|
||||
(parse-org-tbn sexp-ready #:headers headers #:func-names func-names))
|
||||
|
||||
(module+ test
|
||||
(test-case "read-org-sbn"
|
||||
(check-equal? (read-org-sbn "((\"-\" \"x\" \"y\") (\"y\" -1 0) (\"x\" 0 -1))")
|
||||
(hash
|
||||
'x
|
||||
(tbf/state '#hash((x . 0) (y . -1)) 0)
|
||||
'y
|
||||
(tbf/state '#hash((x . -1) (y . 0)) 0)))
|
||||
(check-equal? (read-org-sbn "((\"x\" \"y\") (-1 0) (0 -1))" #:func-names #f)
|
||||
(hash
|
||||
'x
|
||||
(tbf/state '#hash((x . -1) (y . 0)) 0)
|
||||
'y
|
||||
(tbf/state '#hash((x . 0) (y . -1)) 0)))
|
||||
(check-equal? (read-org-sbn "((-1 0) (0 -1))" #:headers #f #:func-names #f)
|
||||
(hash
|
||||
'x0
|
||||
(tbf/state '#hash((x0 . -1) (x1 . 0)) 0)
|
||||
'x1
|
||||
(tbf/state '#hash((x0 . 0) (x1 . -1)) 0)))))
|
||||
|
||||
(: tbn->lists (->* (TBN) (#:headers Boolean
|
||||
#:func-names Boolean)
|
||||
(Listof (Listof (U Symbol Real)))))
|
||||
(define (tbn->lists tbn
|
||||
#:headers [headers #t]
|
||||
#:func-names [func-names #t])
|
||||
(define ntbn (normalize-tbn tbn))
|
||||
(define vars-tbfs (hash-map ntbn (λ ([x : Variable] [tbf : TBF/State])
|
||||
(cons x tbf)) #t))
|
||||
(define tbfs (map (inst cdr Variable TBF/State) vars-tbfs))
|
||||
(define tbfs-table ((if headers
|
||||
tbfs/state->lists+headers
|
||||
tbfs/state->lists) tbfs))
|
||||
(cond
|
||||
[(eq? func-names #t)
|
||||
(define vars (map (inst car Variable TBF/State) vars-tbfs))
|
||||
(define col-1 (if headers (cons '- vars) vars))
|
||||
(for/list ([var (in-list col-1)] [row (in-list tbfs-table)])
|
||||
(cons var row))]
|
||||
[else
|
||||
tbfs-table]))
|
||||
|
||||
(module+ test
|
||||
(test-case "tbn->lists"
|
||||
(define tbn (hash 'a (tbf/state (hash 'b 1) 0)
|
||||
'b (tbf/state (hash 'a -1) -1)))
|
||||
(check-equal? (tbn->lists tbn)
|
||||
'((- a b θ) (a 0 1 0) (b -1 0 -1)))
|
||||
(check-equal? (tbn->lists tbn #:headers #f)
|
||||
'((a 0 1 0) (b -1 0 -1)))
|
||||
(check-equal? (tbn->lists tbn #:func-names #f)
|
||||
'((a b θ) (0 1 0) (-1 0 -1)))
|
||||
(check-equal? (tbn->lists tbn #:headers #f #:func-names #f)
|
||||
'((0 1 0) (-1 0 -1)))))
|
||||
|
||||
(: sbn->lists (->* (TBN) (#:headers Boolean
|
||||
#:func-names Boolean)
|
||||
(Listof (Listof (U Symbol Real)))))
|
||||
(define (sbn->lists sbn
|
||||
#:headers [headers #t]
|
||||
#:func-names [func-names #t])
|
||||
(define tab (tbn->lists sbn #:headers headers #:func-names func-names))
|
||||
(define-values (tab-no-θ _)
|
||||
(multi-split-at tab (sub1 (length (car tab)))))
|
||||
tab-no-θ)
|
||||
|
||||
(module+ test
|
||||
(test-case "sbn->lists"
|
||||
(define sbn (hash 'a (tbf/state (hash 'b 2) 0)
|
||||
'b (tbf/state (hash 'a 2) 0)))
|
||||
(check-equal? (sbn->lists sbn)
|
||||
'((- a b) (a 0 2) (b 2 0)))
|
||||
(check-equal? (sbn->lists sbn #:headers #f)
|
||||
'((a 0 2) (b 2 0)))
|
||||
(check-equal? (sbn->lists sbn #:func-names #f)
|
||||
'((a b) (0 2) (2 0)))
|
||||
(check-equal? (sbn->lists sbn #:headers #f #:func-names #f)
|
||||
'((0 2) (2 0)))))
|
||||
|
Loading…
Reference in New Issue