Compare commits

...

316 Commits

Author SHA1 Message Date
Sergiu Ivanov 56301357ca Update the Roadmap. 2023-08-25 23:09:42 +02:00
Sergiu Ivanov 3ba0cca66d Remove variable-mapping?. 2023-08-25 23:08:54 +02:00
Sergiu Ivanov 0e3cc3c9fd Update some tests in networks. 2023-08-25 23:08:48 +02:00
Sergiu Ivanov e01f99457d Adjust the roadmap. 2023-08-24 23:39:07 +02:00
Sergiu Ivanov 09b62feb46 Remove the TODO item about converting to Typed Racket.
Yay, done!  2 (?) years of work.
2023-08-24 23:38:37 +02:00
Sergiu Ivanov 51c033b29c Make rs fully Typed Racket. 2023-08-24 23:37:17 +02:00
Sergiu Ivanov 5a2307ed58 Type pretty-print-state-graph. 2023-08-24 13:53:42 +02:00
Sergiu Ivanov 1e9d906b34 Type pretty-print-state. 2023-08-23 16:37:54 +02:00
Sergiu Ivanov a2e5f9d091 Add build-interactive-process/org. 2023-08-19 16:47:49 +02:00
Sergiu Ivanov 11c736b04b Type build-interactive-process. 2023-08-19 16:33:57 +02:00
Sergiu Ivanov a18620e694 Add pretty-print-state-graph/simple-states.
Was pretty-print-reduced-graph.
2023-08-17 17:43:50 +02:00
Sergiu Ivanov 0e364eb52d Generalize the type of pretty-print-set. 2023-08-17 16:58:21 +02:00
Sergiu Ivanov 5bc062af54 Generalize the type of pretty-print-set-sets. 2023-08-17 11:33:47 +02:00
Sergiu Ivanov 99d50c8505 Add build-interactive-process-graph/simple-states.
Was build-reduced-state-graph.
2023-08-17 10:26:05 +02:00
Sergiu Ivanov 6a3bd9e7a6 Type build-interactive-process-graph. 2023-08-16 14:01:17 +02:00
Sergiu Ivanov c667f75c0e Type dynamics%. 2023-08-16 10:06:46 +02:00
Sergiu Ivanov b2bc06646e rs->ht-str-triples: Add an actual test case. 2023-08-15 10:46:26 +02:00
Sergiu Ivanov 78e88840cc Add state and State. 2023-08-14 14:47:09 +02:00
Sergiu Ivanov f398d10d15 Type rs->ht-str-triples. 2023-08-14 13:32:27 +02:00
Sergiu Ivanov a469eae764 Type reaction->str-triple. 2023-08-13 21:17:32 +02:00
Sergiu Ivanov c1723066a7 Type read-context-sequence. 2023-08-11 15:55:09 +02:00
Sergiu Ivanov 6a9adf5e07 Type read-org-rs. 2023-08-10 18:05:22 +02:00
Sergiu Ivanov aae2e1f964 Type ht-str-triples->rs. 2023-08-10 16:46:32 +02:00
Sergiu Ivanov e884a2ee07 Type str-triple->reaction. 2023-08-10 16:30:02 +02:00
Sergiu Ivanov a51bba870e Type apply-rs. 2023-08-10 16:19:58 +02:00
Sergiu Ivanov b29f98105f Type union-products. 2023-08-10 01:07:41 +02:00
Sergiu Ivanov c944841dc6 Add NonemptyListof. 2023-08-09 23:15:33 +02:00
Sergiu Ivanov 064169f0b6 Add and use ReactionName. 2023-08-09 11:29:09 +02:00
Sergiu Ivanov d779c52cc8 Type list-enabled. 2023-08-09 11:15:43 +02:00
Sergiu Ivanov cf9a68ae6b Add ReactionSystem. 2023-08-08 18:34:40 +02:00
Sergiu Ivanov 9efca22f7b Add enabled?. 2023-08-08 18:28:25 +02:00
Sergiu Ivanov 6518ae2fdd Add make-reaction. 2023-08-08 18:09:24 +02:00
Sergiu Ivanov 6cf1505a61 Add reaction and Reaction. 2023-08-08 17:40:44 +02:00
Sergiu Ivanov 3821a11d38 Start converting rs to Typed Racket. 2023-08-08 17:22:03 +02:00
Sergiu Ivanov 4e90afc638 Remove a done TODO. 2023-08-08 16:50:32 +02:00
Sergiu Ivanov ceb8e64a35 Promote the TODO of submitting update-graph to Stephen Chang. 2023-08-08 16:50:15 +02:00
Sergiu Ivanov ddc6b3d10c Update the roadmap. 2023-08-08 16:49:22 +02:00
Sergiu Ivanov 17b6ac7fb6 sbn-interaction-graph: Update the test case. 2023-08-08 16:39:26 +02:00
Sergiu Ivanov 0fa6ead5ec Drop the untyped part of dds/tbn. 2023-08-08 16:29:14 +02:00
Sergiu Ivanov dd7117c733 Type sbn-interaction-graph. 2023-08-08 16:10:35 +02:00
Sergiu Ivanov e9ecbd8a7c Type pretty-print-tbn-interaction-graph. 2023-08-08 09:21:12 +02:00
Sergiu Ivanov ac7f928737 Type tbn-interaction-graph. 2023-08-08 08:46:43 +02:00
Sergiu Ivanov aca3fb7868 Add sbn->lists. 2023-08-07 20:06:30 +02:00
Sergiu Ivanov 704221185b Type tbn->lists. 2023-08-07 19:16:16 +02:00
Sergiu Ivanov 927877b02f Start the section Reading and printing TBNs and SBNs.
… and move some functions around without modifying them.
2023-08-07 16:17:53 +02:00
Sergiu Ivanov d9641e7b5b Type compact-tbn. 2023-08-07 16:07:40 +02:00
Sergiu Ivanov cdb4602701 Type compact-tbf. 2023-08-07 15:52:15 +02:00
Sergiu Ivanov b7b4956fdc Type normalize-tbn. 2023-08-07 15:15:32 +02:00
Sergiu Ivanov 91b96463da in-random: Add some explicit type annotations.
Used to work without them before Racket 8.9, but hey that's
nothing special.
2023-08-07 15:11:22 +02:00
Sergiu Ivanov f0a20646ef Type normalized-tbn?. 2023-07-14 16:21:20 +02:00
Sergiu Ivanov f05024e61e Move the functions for reading to TBN and SBN to the section on TBN. 2023-07-12 20:18:29 +02:00
Sergiu Ivanov 78462d5083 Type build-tbn-state-graph. 2023-07-12 20:12:09 +02:00
Sergiu Ivanov c523c68037 Type read-org-sbn. 2023-07-07 12:29:36 +02:00
Sergiu Ivanov 8241bc4da5 Type read-org-tbn. 2023-07-07 11:44:06 +02:00
Sergiu Ivanov 0c91e6f6b2 Type parse-org-tbn. 2023-07-06 11:44:49 +02:00
Sergiu Ivanov afbc5426ce Add lists->tbfs/state/opt-headers. 2023-06-10 12:03:09 +02:00
Sergiu Ivanov cd8cada92e Add sbfs/state->lists+headers. 2023-05-25 15:19:25 +02:00
Sergiu Ivanov 76c6bb5745 Finish adding sbfs/state->lists. 2023-05-25 14:51:55 +02:00
Sergiu Ivanov 18c9828a5a Type tbn->network. 2023-05-25 14:34:58 +02:00
Sergiu Ivanov 738ad858ae Add sbn?. 2023-05-22 16:27:35 +02:00
Sergiu Ivanov ab56b64d38 Add the type TBN. 2023-05-22 15:55:36 +02:00
Sergiu Ivanov 001a12d166 Type group-truth-table-by-nai. 2023-05-22 15:34:27 +02:00
Sergiu Ivanov b9eb692091 Add tabulate-tbf/state and tabulate-tbf/state+headers. 2023-04-25 23:52:57 +02:00
Sergiu Ivanov 74347b5151 Add tabulate-tbfs/state and tabulate-tbfs/state+headers. 2023-04-24 00:04:10 +02:00
Sergiu Ivanov 08d41dd4ca print-org-tbfs/state → tbfs/state->lists
print-org-tbfs/state+headers → tbfs/state->lists+headers
2023-04-20 16:12:17 +02:00
Sergiu Ivanov b1b78917ce Add print-org-tbfs/state and print-org-tbfs/state+headers. 2023-04-16 23:49:25 +02:00
Sergiu Ivanov 495ea18bb5 Type read-org-tbfs/state and add read-org-tbfs/state+headers. 2023-04-13 00:40:34 +02:00
Sergiu Ivanov b611115f8c Add lists+vars->sbfs/state, lists+headers->sbfs/state, lists->sbfs/state. 2023-04-13 00:19:11 +02:00
Sergiu Ivanov 43d782f149 Add lists->tbfs/state. 2023-04-03 16:36:08 +02:00
Sergiu Ivanov 3455b8aae1 Add lists+headers->tbfs/state. 2023-04-03 16:32:08 +02:00
Sergiu Ivanov 764b4612f1 Make the sandbox sizes 10 times bigger for all scribblings. 2023-04-03 16:19:57 +02:00
Sergiu Ivanov 72454c395c Add lists+vars->tbfs/state. 2023-04-03 16:00:37 +02:00
Sergiu Ivanov 7c5333555c Add sectioning to tbn.scrbl. 2023-04-03 15:35:12 +02:00
Sergiu Ivanov e8ebab58ca Type apply-tbf/state. 2023-04-03 00:00:06 +02:00
Sergiu Ivanov b8b9fee9ce Type sbf/state?. 2023-03-29 17:46:53 +02:00
Sergiu Ivanov ed67927803 Type make-tbf/state. 2023-03-29 01:21:29 +02:00
Sergiu Ivanov 490127593c Type tbf/state, tbf/state-w, and tbf/state-θ. 2023-03-29 01:16:12 +02:00
Sergiu Ivanov fa88c15454 Type apply-tbf-to-state. 2023-03-27 23:23:34 +02:00
Sergiu Ivanov d2e4ab854c Start tbn.scrbl. 2023-03-26 23:29:51 +02:00
Sergiu Ivanov 0396e558ce Update the roadmap. 2023-03-26 23:04:23 +02:00
Sergiu Ivanov aa278215ed Make networks fully Typed Racket, throwing away the TBN/SBN part. 2023-03-26 22:53:27 +02:00
Sergiu Ivanov 507f7a28f7 Move TBN/SBN-related code out to tbn.rkt. 2023-03-26 22:53:12 +02:00
Sergiu Ivanov e6764b2dd6 Type random-boolean-network/n. 2023-03-26 22:34:45 +02:00
Sergiu Ivanov 861665e205 Type random-boolean-network. 2023-03-25 16:29:33 +01:00
Sergiu Ivanov 5e155d0a61 Type random-network. 2023-03-24 16:20:23 +01:00
Sergiu Ivanov e2e0ee6903 Only set random-seed once in the whole test submodule. 2023-03-24 16:09:44 +01:00
Sergiu Ivanov 59b90d4c12 Add random-boolean-function/state. 2023-03-24 15:12:22 +01:00
Sergiu Ivanov f5349a3659 Add random-function/state. 2023-03-24 14:43:59 +01:00
Sergiu Ivanov 78c638a886 Add typed-racket-more to package dependencies.
For typed/racket/random.
2023-03-24 14:39:49 +01:00
Sergiu Ivanov abf8d4cf92 Add table+headers->network. 2023-03-23 16:16:02 +01:00
Sergiu Ivanov d3907556ba Add table->network. 2023-03-17 23:36:50 +01:00
Sergiu Ivanov a6321c932a table->network → table+vars->network 2023-03-17 23:35:33 +01:00
Sergiu Ivanov 9175a98a2a Add hash-replace-keys/ordered. 2023-03-12 21:15:51 +01:00
Sergiu Ivanov 1490792a19 Add table->network. 2023-03-10 23:52:39 +01:00
Sergiu Ivanov 6783b97add Use table->unary-function in table->function/list. 2023-03-03 10:21:31 +01:00
Sergiu Ivanov 09c14907ca Add table->unary-function. 2023-03-03 10:16:37 +01:00
Sergiu Ivanov b9b224fc6a Add tabulate-network and tabulate-network+headers. 2023-02-17 14:06:33 +01:00
Sergiu Ivanov 0e5334f5e1 Add tabulate-state, tabulate-state/boolean, tabulate-state+headers, tabulate-state+headers/boolean. 2023-02-07 00:26:02 +01:00
Sergiu Ivanov dd23de304f Expand the comment before Dynamics%. 2023-02-03 12:53:47 +01:00
Sergiu Ivanov 16626c70ec Update the definition of Dynamics%.
define-type doesn't seem to work with Instance any more in Racket 8.7,
so predefining the type synonym DynamicsClass does not help Dynamics%
shorter.  Defining DynamicsClass as a macros doesn't work either.
2023-02-03 12:50:08 +01:00
Sergiu Ivanov 53e4981845 Add tabulate-state*+headers, tabulate-state*+headers/boolean. 2022-11-11 11:52:27 +01:00
Sergiu Ivanov da368d2574 Use define/:. 2022-11-06 23:02:10 +01:00
Sergiu Ivanov d7d9274bc9 Add lambda/:, λ/:, and define/:. 2022-11-06 22:25:27 +01:00
Sergiu Ivanov 6dce7583ab Fix the syntactic context.
https://racket.discourse.group/t/wrap-a-macro-injecting-bindings-in-another-macro/1406/2
2022-10-29 23:13:44 +02:00
Sergiu Ivanov fabecbe0f9 Type tabulate-state*/boolean. 2022-09-28 00:44:31 +02:00
Sergiu Ivanov 3d58660e9c Type tabulate-state*. 2022-09-27 23:12:57 +02:00
Sergiu Ivanov b5fef760c5 Type build-full-state-graph/annotated. 2022-09-22 01:22:11 +02:00
Sergiu Ivanov f8a03659d1 Replace /boolean with /01. 2022-09-22 01:06:16 +02:00
Sergiu Ivanov d00056affb Add examples for pretty-print-state-graph and pretty-print-state-graph/boolean. 2022-09-22 00:56:07 +02:00
Sergiu Ivanov 990abee3db Add tests for pretty-print-state-graph and pretty-print-state-graph/boolean. 2022-09-22 00:38:15 +02:00
Sergiu Ivanov 47602a1785 Type build-full-state-graph. 2022-09-22 00:15:35 +02:00
Sergiu Ivanov fb56dc6589 Add and use relax-arg-type/any. 2022-09-21 00:53:12 +02:00
Sergiu Ivanov 4c3415f0ac Put boolean at the end of function names. 2022-09-21 00:28:52 +02:00
Sergiu Ivanov 2577d06dbc Type pretty-print-boolean-state-graph and ppsgb. 2022-09-21 00:07:10 +02:00
Sergiu Ivanov d5a351d6c1 Type pretty-print-state-graph and ppsg. 2022-09-20 11:48:45 +02:00
Sergiu Ivanov d5382eacda Type pretty-print-state-graph-with. 2022-09-20 11:40:21 +02:00
Sergiu Ivanov b1cc242c7b Type pretty-print-boolean-state. 2022-09-19 00:10:42 +02:00
Sergiu Ivanov 6d2034f9e5 Add pretty-print-state.
And start a new section in the docs.
2022-09-18 01:20:00 +02:00
Sergiu Ivanov 945626487c Add make-asyn-dynamics and make-syn-dynamics. 2022-09-18 01:03:16 +02:00
Sergiu Ivanov 4d05b9d9ee Fix the scope of the doc of dynamics%. 2022-09-18 00:56:09 +02:00
Sergiu Ivanov 46612dd3df Fix the example for Dynamics%. 2022-09-17 15:36:08 +02:00
Sergiu Ivanov d2ab44c79b Fix Dynamics%.
Dynamics% used to be the type of the class, rather than the type of
its instances.
2022-09-17 00:55:33 +02:00
Sergiu Ivanov cc121fc9e3 Type make-syn and make-asyn. 2022-09-16 17:57:15 +02:00
Sergiu Ivanov 60fd8b2a24 Add Dynamics%. 2022-09-15 20:40:07 +02:00
Sergiu Ivanov 567a721c8f Implement build-state-graph, build-state-graph/annotated, build-state-graph*. 2022-09-15 16:55:44 +02:00
Sergiu Ivanov 857b33ad71 Refine the types of build-state-graph* and build-state-graph*/annotated. 2022-09-15 01:11:11 +02:00
Sergiu Ivanov d7f4d2d732 Implement build-state-graph*/annotated. 2022-09-15 00:57:38 +02:00
Sergiu Ivanov ecc57a34fc Generalize the type of build-state-graph* and build-state-graph*/annotated. 2022-09-10 18:55:03 +02:00
Sergiu Ivanov 45410176b7 Implement step*. 2022-09-10 17:46:26 +02:00
Sergiu Ivanov 983380b063 Implement the method step. 2022-09-02 16:38:13 +02:00
Sergiu Ivanov bba44c2887 Generally skip expensive tests in networks. 2022-08-30 17:41:56 +02:00
Sergiu Ivanov 8c89bf810a Add dynamics%. 2022-08-30 17:41:44 +02:00
Sergiu Ivanov 811df5fe1e Add a TODO item. 2022-08-23 23:37:16 +02:00
Sergiu Ivanov 279ee68b91 Define Mode and Modality as lists instead of sets. 2022-08-23 11:09:06 +02:00
Sergiu Ivanov b0c084af37 Start dynamics. 2022-08-23 10:17:58 +02:00
Sergiu Ivanov 77f2fcb58f Add define/abstract/error. 2022-08-03 01:15:24 +02:00
Sergiu Ivanov 3691c42e67 Start dynamics. 2022-07-06 00:00:17 +02:00
Sergiu Ivanov 413f1798c4 Add Mode and Modality. 2022-07-05 23:53:14 +02:00
Sergiu Ivanov 25411043c1 Type build-signed-interaction-graph and build-signed-interaction-graph/form. 2022-07-05 23:43:41 +02:00
Sergiu Ivanov f2f0564f72 Type build-interaction-graph and build-interaction-graph/form. 2022-07-05 22:46:02 +02:00
Sergiu Ivanov 2424e155fd Type get-interaction-sign. 2022-07-03 23:03:48 +02:00
Sergiu Ivanov 3e1dca8d63 Type interaction?. 2022-07-02 07:56:10 +02:00
Sergiu Ivanov 59b3d5f6fe Add for/first/typed and for*/first/typed. 2022-06-21 00:42:56 +02:00
Sergiu Ivanov 1bfc44491e Type build-all-01-states. 2022-05-15 19:52:37 +02:00
Sergiu Ivanov d64b04a8f0 Type build-all-boolean-states. 2022-05-15 19:05:07 +02:00
Sergiu Ivanov eaabcd9a05 Type build-all-states. 2022-05-15 16:50:21 +02:00
Sergiu Ivanov 8722d63d3e Type build-syntactic-interaction-graph. 2022-05-15 01:57:49 +02:00
Sergiu Ivanov 0018c91fb6 Type list-syntactic-interactions. 2022-05-10 10:41:07 +02:00
Sergiu Ivanov 893b375b91 Improve the introduction to Inferring interaction graphs. 2022-05-09 10:33:34 +02:00
Sergiu Ivanov 0336526a84 Type forms->boolean-network. 2022-05-05 11:59:37 +02:00
Sergiu Ivanov 871a923842 Type make-boolean-network-form. 2022-05-05 11:25:10 +02:00
Sergiu Ivanov 7a3cbaa1af Add network-form->network/01. 2022-05-04 01:21:11 +02:00
Sergiu Ivanov 007baa5e41 Improve the doc of update-function-form->update-function/01. 2022-05-04 01:20:50 +02:00
Sergiu Ivanov 43e29f928b Add network-form->network/boolean. 2022-05-04 01:12:52 +02:00
Sergiu Ivanov b795be0a39 Type network-form->network as network-form->network/any. 2022-05-03 22:01:13 +02:00
Sergiu Ivanov 181b427cd8 Add update-function-form->update-function/boolean and update-function-form->update-function/01. 2022-05-03 21:41:50 +02:00
Sergiu Ivanov 2c2d8fbbdb update-function-form->update-function → update-function-form->update-function/any 2022-05-03 21:26:36 +02:00
Sergiu Ivanov 320ae55456 Type update-function-form->update-function. 2022-05-03 15:40:23 +02:00
Sergiu Ivanov b97bbfd972 Type network-form. 2022-05-02 00:27:55 +02:00
Sergiu Ivanov 84134340e5 Typos. 2022-05-02 00:22:47 +02:00
Sergiu Ivanov 901720d2f5 Add UpdateFunctionForm. 2022-05-02 00:16:16 +02:00
Sergiu Ivanov fc633c2e4c Move make-same-domains, make-boolean-domains, and make-01-domains to Utilities. 2022-05-01 01:07:33 +02:00
Sergiu Ivanov 458ba10ab5 Add 01->boolean/state (replacing booleanize-state). 2022-05-01 01:05:35 +02:00
Sergiu Ivanov aea472acb2 Remove make-state and make-state-booleanize.
make-state is just a longer word for hash, and I never really use
make-state-booleanize.
2022-05-01 00:55:42 +02:00
Sergiu Ivanov ba30e3dc5e Type update. 2022-05-01 00:33:43 +02:00
Sergiu Ivanov 9a2f1ff527 Type make-01-domains and make-01-network. 2022-04-30 23:21:42 +02:00
Sergiu Ivanov 4ea31d8f39 Typo. 2022-04-30 22:49:03 +02:00
Sergiu Ivanov 70be49b957 Start Common examples. 2022-04-30 00:23:38 +02:00
Sergiu Ivanov 5da523d297 Make the definitions for types the same as in Typed Racket docs. 2022-04-30 00:11:09 +02:00
Sergiu Ivanov d02742ba6d Somewhat improve the sectioning. 2022-04-29 17:31:36 +02:00
Sergiu Ivanov bed1ed24f3 Add and use deftype and defpolytype. 2022-04-29 16:32:22 +02:00
Sergiu Ivanov e867a86d4d Add the type Domain. 2022-04-29 16:10:36 +02:00
Sergiu Ivanov be729f6ca8 Type make-same-domains, make-boolean-domains, make-boolean-network. 2022-04-29 15:54:15 +02:00
Sergiu Ivanov 609de226a9 Basic definitions → Basic types 2022-04-29 15:33:46 +02:00
Sergiu Ivanov 883e845d9d Type network and add examples. 2022-04-28 23:47:37 +02:00
Sergiu Ivanov 8067f9e7f0 Make TBF appear as "type" in the docs. 2022-04-28 19:04:16 +02:00
Sergiu Ivanov 4927c0ec8c Give the instances of tbf the type TBF. 2022-04-27 19:00:13 +02:00
Sergiu Ivanov 86d52eed3b syntax → type as kinds of definitions in scribblings. 2022-04-27 18:45:09 +02:00
Sergiu Ivanov 5efe086d06 Add UpdateFunction and DomainMapping. 2022-04-27 00:15:03 +02:00
Sergiu Ivanov 85566d7479 Start migrating networks to Typed Racket. 2022-04-27 00:10:13 +02:00
Sergiu Ivanov cdcee66b7c Explain the new strategy for converting generics to Typed Racket. 2022-04-26 23:56:22 +02:00
Sergiu Ivanov ba8b9b4d98 Punctuation. 2022-04-26 23:56:17 +02:00
Sergiu Ivanov 9182ea9ecb Switch functions entirely to Typed Racket. 2022-04-25 23:55:32 +02:00
Sergiu Ivanov 0e2b91fdd1 Add one more example to read-org-tbfs. 2022-04-25 23:46:22 +02:00
Sergiu Ivanov 974bf193ed Type read-org-sbfs. 2022-04-25 23:43:25 +02:00
Sergiu Ivanov ef979d6dce Type list->sbf. 2022-04-25 23:29:48 +02:00
Sergiu Ivanov 945dfe1490 Type sbf. 2022-04-25 00:31:00 +02:00
Sergiu Ivanov 6d37f180ba Type sbf?. 2022-04-25 00:24:32 +02:00
Sergiu Ivanov 297d455207 Type tbf-tabulate*/boolean. 2022-04-25 00:17:03 +02:00
Sergiu Ivanov 9db0fcbdb4 Type tbf-tabulate. 2022-04-25 00:07:28 +02:00
Sergiu Ivanov da6f5acf4e functions.scrbl: Add shortcuts for @examples. 2022-04-24 23:59:55 +02:00
Sergiu Ivanov e1a97235c0 Add some shortcuts for the examples.
Inspired from the scribblings in Typed Racket.
2022-04-24 23:59:40 +02:00
Sergiu Ivanov 8ded018c05 Type tbf-tabulate*. 2022-04-24 23:25:10 +02:00
Sergiu Ivanov 70caf3bb7e functions: cast → assert-type. 2022-04-24 14:38:49 +02:00
Sergiu Ivanov 64daec5065 Add and use assert-type in utils. 2022-04-24 14:34:57 +02:00
Sergiu Ivanov 2e8373d037 utils: Replace all casts with asserts.
https://racket.discourse.group/t/managing-cast-performance-penalty/905
2022-04-24 14:09:04 +02:00
Sergiu Ivanov 0e9a974965 Add tabulate*/list/boolean tabulate/list/boolean tabulate*/list/01 tabulate/list/01. 2022-04-22 16:20:25 +02:00
Sergiu Ivanov 0196ab5800 Let Emacs align apply-tbf as it wants. 2022-04-22 15:38:20 +02:00
Sergiu Ivanov 47f7f70241 Add tabulate*/list tabulate/list. 2022-04-22 15:12:41 +02:00
Sergiu Ivanov cf49a6f087 Add apply-op as a parameter to make-tabulate*. 2022-04-22 14:39:57 +02:00
Sergiu Ivanov d958c5822d define-syntax-parse-rule: Give the syntax class for row-op. 2022-04-22 14:35:38 +02:00
Sergiu Ivanov 79a688a3e5 Type read-org-tbfs. 2022-04-21 16:56:58 +02:00
Sergiu Ivanov 1f01917f8a Type lists->tbfs. 2022-04-21 15:37:55 +02:00
Sergiu Ivanov d9734a5a35 Type list->tbf. 2022-04-21 15:00:50 +02:00
Sergiu Ivanov cbede999df Type apply-tbf/boolean. 2022-04-21 15:00:24 +02:00
Sergiu Ivanov 1863b0829c Type apply-tbf. 2022-04-21 14:20:22 +02:00
Sergiu Ivanov 027022524b vector-boolean->01 → boolean->01/vector 2022-04-21 14:06:23 +02:00
Sergiu Ivanov dc8a098234 Correctly export tbf-w and tbf-θ. 2022-04-21 14:02:35 +02:00
Sergiu Ivanov f3a8c65e9d Type vector-boolean->01. 2022-04-21 13:49:15 +02:00
Sergiu Ivanov e44d9c7748 Type tbf-w and tbf-θ. 2022-04-21 11:50:56 +02:00
Sergiu Ivanov cc137a5459 Type tbf. 2022-04-21 11:42:50 +02:00
Sergiu Ivanov 212440add1 Typed random-boolean-function/list. 2022-04-21 11:23:36 +02:00
Sergiu Ivanov ee487af157 Fix parens typesetting. 2022-04-21 11:13:58 +02:00
Sergiu Ivanov 9ad3a69c27 functions: Integer → Positive-Integer for arities. 2022-04-21 11:09:52 +02:00
Sergiu Ivanov 242ea9d31d Type random-boolean-function. 2022-04-21 11:03:57 +02:00
Sergiu Ivanov a7a25f92fe Type random-boolean-table. 2022-04-21 10:46:21 +02:00
Sergiu Ivanov 52bf1b2f58 Type enumerate-boolean-functions/list. 2022-04-19 23:31:28 +02:00
Sergiu Ivanov a201e537e1 Add enumerate-boolean-functions/pv. 2022-04-19 23:14:40 +02:00
Sergiu Ivanov a4a6604ecd Type enumerate-boolean-functions. 2022-04-18 23:26:52 +02:00
Sergiu Ivanov fa63875022 Type enumerate-boolean-tables. 2022-04-18 01:00:34 +02:00
Sergiu Ivanov 4272cd87a8 Add table->function/pv. 2022-04-16 00:17:44 +02:00
Sergiu Ivanov 1324be292e Add table->function. 2022-04-16 00:11:58 +02:00
Sergiu Ivanov 3b51a4ba51 Add table->function/list. 2022-04-13 00:50:53 +02:00
Sergiu Ivanov 1503434306 Add tabulate*/pv/01 and tabulate/pv/01. 2022-04-10 22:37:50 +02:00
Sergiu Ivanov 6102a2b8f3 Fix the labels and add explicit "(untyped)" to conflicts. 2022-04-10 19:55:28 +02:00
Sergiu Ivanov 81496a2ee7 Move tabulate/01 and tabulate*/01 to the untyped submodule. 2022-04-10 19:36:00 +02:00
Sergiu Ivanov 1b22ba4a7e Add tabulate/pv/boolean and tabulate*/pv/boolean. 2022-04-09 02:03:51 +02:00
Sergiu Ivanov c19d18122c Move tabulate/boolean and tabulate*/boolean to typed/untyped. 2022-04-09 01:32:54 +02:00
Sergiu Ivanov 096d536908 Start functions/untyped. 2022-04-09 01:12:17 +02:00
Sergiu Ivanov f2a9336d71 Untyped code → Untyped definitions 2022-04-07 00:41:35 +02:00
Sergiu Ivanov f29bb3956d untyped → utils/untyped
Otherwise there will be conflicts with other modules.
2022-04-07 00:40:46 +02:00
Sergiu Ivanov b1613ac1f7 utils: Explicitly document the untyped submodule. 2022-04-07 00:21:48 +02:00
Sergiu Ivanov 650801a6d2 Add the untyped submodule to utils. 2022-04-01 00:09:42 +02:00
Sergiu Ivanov 1e4f6d3fbc Use define-syntax-parse-rule in make-tabulate*. 2022-03-31 23:45:38 +02:00
Sergiu Ivanov 5aa816507d Use define-syntax-parser in auto-hash-ref/explicit. 2022-03-31 23:40:12 +02:00
Sergiu Ivanov b07cda477f Add tabulate*/pv and tabulate/pv. 2022-03-21 00:04:21 +01:00
Sergiu Ivanov 8fb5bab803 Add syntax for defining pseudovariadic functions. 2022-03-20 20:42:29 +01:00
Sergiu Ivanov 8b838c0b22 Introduce pseudovariadic functions. 2022-03-20 19:34:48 +01:00
Sergiu Ivanov 1f281d3851 Rename the module in the example for tabulate*/untyped. 2022-03-06 23:41:24 +01:00
Sergiu Ivanov e7616684f5 Add tabulate, tabulate/strict, and tabulate/untyped. 2022-03-06 23:39:51 +01:00
Sergiu Ivanov fcf21c51aa Remove tabulate* from functions.rkt.
This temporarily breaks networks, but all breakages are easy to fix
with tabulate*/untyped.
2022-03-06 22:58:16 +01:00
Sergiu Ivanov 31d6275229 Add an explain tabulate*/untyped. 2022-03-06 22:53:33 +01:00
Sergiu Ivanov 37dddb190f Add make-tabulate* to factor out the common parts. 2022-03-06 21:45:21 +01:00
Sergiu Ivanov 929bf09299 Add append-lists. 2022-03-06 20:52:33 +01:00
Sergiu Ivanov 66f1157200 Add typed versions of tabulate* and tabulate*/strict. 2022-03-06 19:54:05 +01:00
Sergiu Ivanov ccb70a5921 Prepare the Scribble infrastructure. 2022-03-05 21:37:24 +01:00
Sergiu Ivanov cd94326ea3 Start typing functions. 2022-03-05 21:37:17 +01:00
Sergiu Ivanov 3e16dbe3c8 Only require rackunit in the test submodule. 2022-03-05 21:33:04 +01:00
Sergiu Ivanov fa940eb0d3 Improve the explanation of hash->list/ordered. 2022-03-05 13:44:48 +01:00
Sergiu Ivanov 97d4c18305 Switch utils to Typed Racket. 2022-03-05 13:41:40 +01:00
Sergiu Ivanov f62d53ed8f Add an easy type to variable-mapping? and remove the untyped section. 2022-03-05 13:29:37 +01:00
Sergiu Ivanov bd6c62e3eb Update the roadmap. 2022-03-05 13:22:38 +01:00
Sergiu Ivanov b026f74f39 Type 01->boolean. 2022-03-05 13:07:44 +01:00
Sergiu Ivanov 687aea5337 Type any->01. 2022-03-05 12:55:23 +01:00
Sergiu Ivanov 357fcad89f Mention functions. 2022-03-05 12:34:15 +01:00
Sergiu Ivanov 77cc8e295a Import stream->list and stream-take for-label. 2022-03-05 00:24:02 +01:00
Sergiu Ivanov 83b293e3e4 Type boolean-power/stream. 2022-03-05 00:20:51 +01:00
Sergiu Ivanov 347e17158d Type boolean-power. 2022-03-05 00:14:38 +01:00
Sergiu Ivanov f5c762b6d8 Fix the sectioning. 2022-03-04 18:12:19 +01:00
Sergiu Ivanov 10f0e0ab0c Type cartesian-product/stream. 2022-03-04 18:08:49 +01:00
Sergiu Ivanov 8cd75b0fa3 utils: Add cartesian-product-2/stream. 2022-03-04 17:24:18 +01:00
Sergiu Ivanov 3918730e1a Streamline and simplify auto-hash-ref/:. 2022-02-20 18:27:36 +01:00
Sergiu Ivanov fa015870d0 Simplify quoting and unquoting in auto-hash-ref/explicit. 2022-02-20 17:30:33 +01:00
Sergiu Ivanov f6946d94ad Indentation. 2022-02-18 16:04:39 +01:00
Sergiu Ivanov 6679db9aa4 utils.rkt: Add a comment about an alternative implementation of lists-transpose. 2022-02-18 16:04:27 +01:00
Sergiu Ivanov 87c80bb6ef utils: Reimplement and type in-random. 2022-02-15 00:14:52 +01:00
Sergiu Ivanov b36d8adaa4 Remove procedure-fixed-arity?.
I don't really need this function.  I think I wrote it just to see how
I can count the arity of a procedure.
2022-02-13 20:09:50 +01:00
Sergiu Ivanov ec50395cb0 networks: Add a copy of lists-transpose.
I will remove this copy when I will have migrated networks to
Typed Racket.
2022-02-13 19:48:21 +01:00
Sergiu Ivanov 2af8d9276b utils: Typed lists-transpose. 2022-02-13 19:33:04 +01:00
Sergiu Ivanov 20cc0a27d0 utils: Type multi-split-at. 2022-02-11 00:01:07 +01:00
Sergiu Ivanov 912da811f2 utils: Type hash->list/ordered. 2022-02-10 23:37:40 +01:00
Sergiu Ivanov 9defe51ee6 utils: Make collect-by-keys and collect-by-keys/sets polymorphic. 2022-02-10 00:12:50 +01:00
Sergiu Ivanov e01ba07724 utils: Type ht-values/list->set. 2022-02-10 00:08:49 +01:00
Sergiu Ivanov 50fb3dab59 utils: Type collect-by-key/sets. 2022-02-09 23:55:20 +01:00
Sergiu Ivanov cf3f20097b utils: Type collect-by-key. 2022-02-09 01:07:37 +01:00
Sergiu Ivanov 002b1a8006 utils: Remove the old definition of pretty-print-set. 2022-02-08 00:11:58 +01:00
Sergiu Ivanov 67ae415064 utils.scrbl: Require typed/graph in a separate place. 2022-02-08 00:10:49 +01:00
Sergiu Ivanov 0179423be9 Utils: Type update-graph. 2022-02-08 00:08:42 +01:00
Sergiu Ivanov 45a60cd122 utils.scrbl: Improve the doc for dotit. 2022-02-06 23:55:21 +01:00
Sergiu Ivanov e1cf64a822 utils: Typed update-vertices/unweighted. 2022-02-06 23:44:03 +01:00
Sergiu Ivanov 25b4216faf utils.scrbl: Move Pretty printing before Additional graph utilities. 2022-02-04 00:09:39 +01:00
Sergiu Ivanov 8d5d41233e utils.scrbl: Move the pretty printing functions to their section. 2022-02-04 00:08:22 +01:00
Sergiu Ivanov e4f7c956d4 utils: Type pretty-print-set-sets. 2022-02-04 00:06:19 +01:00
Sergiu Ivanov 2997319f1f utils: Type pretty-print-set. 2022-02-03 23:58:23 +01:00
Sergiu Ivanov 211f39e91f utils: Type list-sets->list-strings. 2022-02-03 16:40:02 +01:00
Sergiu Ivanov 4703bfcce8 utils.scrbl: Include the whole Typed Racket for the examples. 2022-02-03 16:00:20 +01:00
Sergiu Ivanov 403401f085 utils: Type drop-first-last. 2022-02-03 10:55:34 +01:00
Sergiu Ivanov ec28541c46 utils: Type read-symbol-list. 2022-02-03 10:48:51 +01:00
Sergiu Ivanov caf9114ec5 Update package dependencies. 2022-02-03 00:13:46 +01:00
Sergiu Ivanov b2f3306731 utils: Add dotit. 2022-02-03 00:09:11 +01:00
Sergiu Ivanov ebd960de53 utils.scrbl: Import the whole of typed/racket/base for-label. 2022-01-27 21:10:59 +01:00
Sergiu Ivanov 746c586973 utils.rkt: Remove the left-over definitions of read-org-sexp and unorg. 2022-01-27 21:10:37 +01:00
Sergiu Ivanov 8b2bab4d9e utils: Type read-org-variable-mapping and unorgv. 2022-01-27 21:10:13 +01:00
Sergiu Ivanov 73afefdeb2 utils.scrbl: Import -> from typed/racket/base. 2022-01-26 16:35:03 +01:00
Sergiu Ivanov 44de84de10 utils: Type unstringify-pairs. 2022-01-25 00:46:12 +01:00
Sergiu Ivanov c1967816b4 utils/typed: Explicitly provide Variable and VariableMapping.
These are not useful outside of utils/typed as of yet, but the time
will come.
2022-01-23 16:01:09 +01:00
Sergiu Ivanov 69a7234665 utils: Type read-org-sexp and unorg. 2022-01-23 15:57:50 +01:00
Sergiu Ivanov ec84924608 utils: Type map-sexp. 2022-01-23 15:50:06 +01:00
Sergiu Ivanov b6417d2d07 utils: Type handle-org-booleans. 2022-01-23 15:10:58 +01:00
Sergiu Ivanov 9d569bd3ba utils: Type string->any. 2022-01-23 13:53:41 +01:00
Sergiu Ivanov 783000318b utils: Type stringify-variable-mapping. 2022-01-23 13:47:36 +01:00
Sergiu Ivanov f30d9b9aa1 utils: Type any->string. 2022-01-20 19:58:06 +01:00
Sergiu Ivanov 8ffd252fc2 utils: Type extract-symbols. 2022-01-19 00:40:41 +01:00
Sergiu Ivanov 85d60c6a9b utils: Move auto-hash-ref/explicit and auto-hash-ref/: to the typed section. 2022-01-19 00:34:04 +01:00
Sergiu Ivanov 752d173279 utils: Fix some transient test errors. 2022-01-16 23:18:14 +01:00
Sergiu Ivanov 75560658a0 networks: eval-with → eval1-with
This makes the type checker happy because it's more precise to use
eval1-with.
2022-01-16 23:15:33 +01:00
Sergiu Ivanov 556a4ae8bf info.rkt: Update the dependencies. 2022-01-16 23:12:53 +01:00
Sergiu Ivanov d64ecfb207 utils.scrbl: Add a TODO note about the broken hyperlinks. 2022-01-16 23:11:06 +01:00
Sergiu Ivanov 28fdc23324 utils: Type eval-with and eval1-with. 2022-01-16 23:11:00 +01:00
Sergiu Ivanov 906d339508 README: Update the strategy for conversion to Typed Racket. 2022-01-16 21:33:15 +01:00
Sergiu Ivanov 505487d3b6 info.rkt: List all package dependencies. 2022-01-16 21:23:44 +01:00
Sergiu Ivanov 29a9c57ebd utils.scrbl: Add the note about Typed Racket. 2022-01-16 21:09:50 +01:00
Sergiu Ivanov 4490cadf58 utils: Add Variable and VariableMapping. 2022-01-16 21:00:41 +01:00
Sergiu Ivanov 33ddd747c3 Start a typed section in utils. 2022-01-16 20:48:20 +01:00
Sergiu Ivanov e3e50c99ea dds.org: Add a code block for opening the documentation. 2022-01-16 00:44:37 +01:00
Sergiu Ivanov 00db8b651b dds.org: Minor update. 2022-01-15 23:33:35 +01:00
16 changed files with 6062 additions and 2415 deletions

View File

@ -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

View File

@ -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

68
dynamics.rkt Normal file
View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

525
rs.rkt
View File

@ -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")
))

View File

@ -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"]

117
scribblings/dynamics.scrbl Normal file
View File

@ -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].
}
}

View File

@ -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))))
]}

View File

@ -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))
]}

View File

@ -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))))
]}

602
scribblings/tbn.scrbl Normal file
View File

@ -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)))
]}

View File

@ -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)))
]}

715
tbn.rkt Normal file
View File

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

762
utils.rkt

File diff suppressed because it is too large Load Diff