Make rs fully Typed Racket.

This commit is contained in:
Sergiu Ivanov 2023-08-24 23:37:17 +02:00
parent 5a2307ed58
commit 51c033b29c
2 changed files with 275 additions and 608 deletions

877
rs.rkt
View file

@ -1,688 +1,355 @@
#lang racket
#lang typed/racket
(module typed typed/racket
(require typed/graph "utils.rkt" "dynamics.rkt")
(provide
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 typed/rackunit))
(define-type Species Symbol)
(struct reaction ([reactants : (Setof Species)]
[inhibitors : (Setof Species)]
[products : (Setof Species)])
#:transparent
#:type-name Reaction)
(define-type ReactionName Symbol)
(: 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)))))
(: enabled? (-> Reaction (Setof Species) Boolean))
(define/match (enabled? r s)
[((reaction r i _) s)
(and (subset? r s) (set-empty? (set-intersect i s)))])
(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)))))
(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))
(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))))
(: 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 "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))))
(: 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)))])
(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 : (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 (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))))))
(: 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 't 'x) (set 'y) (set 'z))
'b (reaction (set 'x) (set 'q) (set 'z))))))
(: read-context-sequence (-> String (Listof (Setof Species))))
(define (read-context-sequence 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)))))
(: reaction->str-triple (-> Reaction (Listof String)))
(define/match (reaction->str-triple r)
[((reaction r i p))
(for/list ([c (in-list (list r i p))])
(drop-first-last (any->string (set->list c))))])
(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 : (HashTable ReactionName (Listof String))
([(a r) (in-hash rs)])
(values a (reaction->str-triple r))))
(module+ test
(test-case "rs->ht-str-triples"
(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)
(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 _'()) '()]))))
(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)))))))
(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) '()))))))
(: build-interactive-process-graph (-> ReactionSystem (Listof (Setof Species)) Graph))
(define (build-interactive-process-graph rs contexts)
(send (new dynamics% [rs rs])
build-state-graph/annotated
(list (state (set) 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 (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-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")))
(: 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-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")
))
(: build-interactive-process (-> ReactionSystem (Listof (Setof Species))
(Listof (Pairof (Setof Species) (Setof Species)))))
(define (build-interactive-process rs contexts)
(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)))))
(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))])
(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 (λ (st) (pretty-print-state (assert-type st State)))
#:e-func (λ (e) (pretty-print-set (assert-type e (Listof ReactionName))))))
(module+ test
(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")
))
)
(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

@ -1,7 +1,7 @@
#lang scribble/manual
@(require scribble/example racket/sandbox
(for-label typed/racket/base
(submod "../rs.rkt" typed)
"../rs.rkt"
"../utils.rkt"
"../dynamics.rkt"))
@ -10,7 +10,7 @@
[sandbox-error-output 'string]
[sandbox-memory-limit 500])
(make-evaluator 'typed/racket
#:requires '((submod "rs.rkt" typed) "utils.rkt"))))
#:requires '("rs.rkt" "utils.rkt"))))
@(define-syntax-rule (ex . args)
(examples #:eval rs-evaluator . args))
@ -23,7 +23,7 @@
@title[#:tag "rs"]{dds/rs: Reaction Systems}
@defmodule[(submod dds/rs typed)]
@defmodule[dds/rs]
This module defines reaction systems and various tools for working with them.