2020-02-22 20:34:24 +01:00
|
|
|
#+TITLE: Examples of usage of =dds=
|
|
|
|
|
2020-05-28 00:30:27 +02:00
|
|
|
#+PROPERTY: header-args:racket :prologue "#lang racket\n(require graph dds/networks dds/utils dds/functions)"
|
2020-02-22 20:34:24 +01:00
|
|
|
|
|
|
|
* Introduction
|
|
|
|
This document shows some examples of usage of the modules in =dds=
|
|
|
|
with Org-mode. It relies on [[https://github.com/hasu/emacs-ob-racket][emacs-ob-racket]].
|
|
|
|
|
|
|
|
The [[intro][following section]] describes how Org-mode can interact with
|
|
|
|
Racket, and how this interaction can be used for a fluid workflow
|
2020-03-02 18:36:26 +01:00
|
|
|
with =dds=. In particular, the code block =munch-sexp= is [[tabread][defined]]
|
2020-02-22 20:34:24 +01:00
|
|
|
in this section.
|
|
|
|
|
|
|
|
The subsequent sections show off some the functionalities of the
|
|
|
|
submodules of =dds=.
|
|
|
|
|
|
|
|
* Org-mode, Racket, and =dds= <<intro>>
|
2020-05-16 00:01:36 +02:00
|
|
|
** Installing =dds= locally
|
|
|
|
To install =dds= locally, you can simply run the following command
|
|
|
|
in [[/home/scolobb/Candies/prj/racket/dds/][=dds=]].
|
|
|
|
|
|
|
|
#+BEGIN_SRC shell
|
|
|
|
raco pkg install
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
After this installation, you can import =dds= modules by simply
|
|
|
|
doing the following:
|
|
|
|
#+BEGIN_SRC racket :results output drawer
|
|
|
|
#lang racket
|
|
|
|
(file dds/networks)
|
|
|
|
(file dds/utils)
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
** Importing a module from file :old:
|
2020-02-22 20:34:24 +01:00
|
|
|
:PROPERTIES:
|
|
|
|
:header-args:racket: :prologue "#lang racket\n(require (file \"~/Candies/prj/racket/dds/networks.rkt\"))"
|
|
|
|
:END:
|
|
|
|
|
|
|
|
To require the modules from the files of =dds=, you can use the
|
|
|
|
following code (I only reset the prelude here because I set at the
|
|
|
|
top of this file):
|
|
|
|
|
|
|
|
#+BEGIN_SRC racket :results output :prologue ""
|
|
|
|
#lang racket
|
|
|
|
(require (file "~/Candies/prj/racket/dds/networks.rkt"))
|
|
|
|
(require (file "~/Candies/prj/racket/dds/utils.rkt"))
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
|
|
|
|
Note that this code will not work with =:results value=. I think it
|
|
|
|
is because in this case the code is not really evaluated at top
|
|
|
|
level.
|
|
|
|
|
|
|
|
These initialisation lines can be put into the prologue of every
|
|
|
|
code block in a subtree by setting =:prologue= via
|
|
|
|
=:header-args:racket:= in the properties drawer. Check out the
|
|
|
|
properties drawer of this section for an example.
|
|
|
|
|
|
|
|
Alternatively, this property can be set via a =#+PROPERTY= line at
|
|
|
|
the top the file. For example, this file has such a line. Whenever
|
|
|
|
this property line changes, refresh the setup of the file by hitting
|
|
|
|
=C-c C-c= on the property line. This will update the prologue for
|
|
|
|
all racket code blocks.
|
|
|
|
|
|
|
|
Finally, you can also set =:prologue= (and other properties with
|
|
|
|
long values) in the following way:
|
|
|
|
|
|
|
|
#+HEADER: :prologue "#lang racket\n(require (file \"~/Candies/prj/racket/dds/networks.rkt\"))"
|
|
|
|
#+BEGIN_SRC racket :results output drawer
|
2020-03-25 00:31:23 +01:00
|
|
|
(make-state '((a . 1)))
|
2020-02-22 20:34:24 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
'#hash((a . 1))
|
|
|
|
:END:
|
|
|
|
|
|
|
|
** Output formats for results of evaluation of code blocks
|
|
|
|
[[https://orgmode.org/manual/Results-of-Evaluation.html#Results-of-Evaluation][This section]] of the Org manual describes various different formats
|
|
|
|
for presenting the results of code blocks. I find the following
|
|
|
|
three particularly useful as of [2020-02-22 Sat]: =output=, =list=,
|
|
|
|
and =table=.
|
|
|
|
|
|
|
|
The =output= result format is the simplest and the most natural
|
|
|
|
ones. It works as if the code block were inserted into a module
|
|
|
|
which would then be evaluated.
|
|
|
|
|
|
|
|
#+BEGIN_SRC racket :results output drawer
|
|
|
|
(println "This is the first line of output.")
|
|
|
|
(println (+ 1 2))
|
|
|
|
(println "This the third line of output.")
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
"This is the first line of output."
|
|
|
|
3
|
|
|
|
"This the third line of output."
|
|
|
|
:END:
|
|
|
|
|
|
|
|
The =list= result format typesets the result of the last line in the
|
|
|
|
code block as a list:
|
|
|
|
#+BEGIN_SRC racket :results list
|
|
|
|
'(1 "hello" (and x y))
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
- 1
|
|
|
|
- "hello"
|
|
|
|
- (and x y)
|
|
|
|
|
|
|
|
Note how nested lists are not recursively shown as nested Org-mode
|
|
|
|
lists.
|
|
|
|
|
|
|
|
For some reason, the =list= output format does not work with the
|
|
|
|
result drawer:
|
|
|
|
#+BEGIN_SRC racket :results list drawer
|
|
|
|
'(1 "hello" (and x y))
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
- (1 "\"hello\"" (and x y))
|
|
|
|
:END:
|
|
|
|
|
|
|
|
Finally, the =table= result format typesets the output as a table:
|
|
|
|
#+BEGIN_SRC racket :results table drawer
|
|
|
|
'((a . #t) (b . #f))
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
| a | #t |
|
|
|
|
| b | #f |
|
|
|
|
:END:
|
|
|
|
|
|
|
|
This is clearly very useful for printing states (and hash tables,
|
|
|
|
more generally):
|
|
|
|
|
|
|
|
#+BEGIN_SRC racket :results table drawer
|
2020-03-25 00:31:23 +01:00
|
|
|
(make-state '((a . 1) (b . #f) (c . "hello")))
|
2020-02-22 20:34:24 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
| a | 1 |
|
|
|
|
| b | #f |
|
|
|
|
| c | "hello" |
|
|
|
|
:END:
|
|
|
|
|
|
|
|
*** A note about printing update function forms
|
|
|
|
Automatic table typesetting may go in the way of readability for
|
|
|
|
hash tables whose values are lists, as the following example shows:
|
|
|
|
|
|
|
|
#+BEGIN_SRC racket :results table drawer
|
|
|
|
#hash((a . (and a b)) (b . (not b)))
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
| a | and | a | b |
|
|
|
|
| b | not | b | |
|
|
|
|
:END:
|
|
|
|
|
|
|
|
To tackle this issue, [[../utils.rkt][=dds/utils=]] provides
|
|
|
|
=stringify-variable-mapping= (with the shortcut =sgfy=) which
|
|
|
|
converts all the values of a given variable mapping to strings:
|
|
|
|
|
|
|
|
#+BEGIN_SRC racket :results table drawer
|
2020-03-24 23:47:20 +01:00
|
|
|
(stringify-variable-mapping #hash((a . (and a b)) (b . (not b))))
|
2020-02-22 20:34:24 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
| a | "(and a b)" |
|
|
|
|
| b | "(not b)" |
|
|
|
|
:END:
|
|
|
|
|
|
|
|
** Reading Org-mode tables<<tabread>>
|
|
|
|
Org-mode allows supplying tables as arguments for code blocks.
|
|
|
|
|
|
|
|
#+NAME: test-table
|
|
|
|
| a | "(and a b)" |
|
|
|
|
| b | (or b (not a)) |
|
|
|
|
|
|
|
|
#+BEGIN_SRC elisp :var tab=test-table :results output drawer
|
|
|
|
tab
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
:END:
|
|
|
|
((a (and a b)) (b (or b (not a))))
|
|
|
|
|
|
|
|
Unfortunately, the same trick does not work with Racket directly,
|
|
|
|
because Racket interprets the first elements in the parentheses as
|
|
|
|
function applications:
|
|
|
|
|
|
|
|
#+BEGIN_SRC racket :results output drawer :var tab=test-table
|
|
|
|
tab
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
application: not a procedure;
|
|
|
|
expected a procedure that can be applied to arguments
|
|
|
|
given: "a"
|
|
|
|
arguments...:
|
|
|
|
"(and a b)"
|
|
|
|
context...:
|
|
|
|
"/tmp/babel-qkvrRR/org-babel-c4wuju.rkt": [running body]
|
|
|
|
temp37_0
|
|
|
|
for-loop
|
|
|
|
run-module-instance!125
|
|
|
|
perform-require!78
|
|
|
|
:END:
|
|
|
|
|
|
|
|
Fortunately, we can easily remedy this problem by creating a named
|
|
|
|
parameterised Elisp source block which will explicitly convert the
|
|
|
|
table to a string:
|
|
|
|
|
2020-03-02 18:36:26 +01:00
|
|
|
#+NAME: munch-sexp
|
2020-03-02 18:38:39 +01:00
|
|
|
#+BEGIN_SRC elisp :results output drawer :var sexp=test-table
|
|
|
|
(prin1 sexp)
|
2020-02-22 20:34:24 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
2020-03-02 18:36:26 +01:00
|
|
|
#+RESULTS: munch-sexp
|
2020-02-22 20:34:24 +01:00
|
|
|
:RESULTS:
|
|
|
|
(("a" "(and a b)") ("b" "(or b (not a))"))
|
|
|
|
:END:
|
|
|
|
|
|
|
|
We can now correctly receive this table in a Racket source code
|
2020-03-02 18:36:26 +01:00
|
|
|
block by threading it through =munch-sexp=:
|
2020-03-02 18:38:39 +01:00
|
|
|
#+BEGIN_SRC racket :results output drawer :var tab=munch-sexp(sexp=test-table)
|
2020-02-22 20:34:24 +01:00
|
|
|
(println tab)
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
"((\"a\" \"(and a b)\") (\"b\" \"(or b (not a))\"))"
|
|
|
|
:END:
|
|
|
|
|
|
|
|
[[../utils.rkt][=dds/utils=]] has several functions for parsing such strings, and
|
2020-03-25 00:16:29 +01:00
|
|
|
notably =read-org-variable-mapping=, with the shortcut =unorgv=:
|
2020-03-02 18:38:39 +01:00
|
|
|
#+BEGIN_SRC racket :results output drawer :var tab=munch-sexp(sexp=test-table)
|
2020-03-25 00:16:29 +01:00
|
|
|
(unorgv tab)
|
2020-02-22 20:34:24 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
'#hash((a . (and a b)) (b . (or b (not a))))
|
|
|
|
:END:
|
|
|
|
|
2020-03-02 18:36:26 +01:00
|
|
|
Of course, we can use =munch-sexp= to prepare any other table than
|
2020-02-22 20:34:24 +01:00
|
|
|
=test-table= for use with Racket:
|
|
|
|
|
|
|
|
#+NAME: another-test-table
|
|
|
|
| a | (not a) |
|
|
|
|
| b | (and a c) |
|
|
|
|
| c | (and a (not b)) |
|
|
|
|
|
2020-03-02 18:38:39 +01:00
|
|
|
#+BEGIN_SRC racket :results output drawer :var tab=munch-sexp(sexp=another-test-table)
|
2020-03-25 00:16:29 +01:00
|
|
|
(unorgv tab)
|
2020-02-22 20:34:24 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
'#hash((a . (not a)) (b . (and a c)) (c . (and a (not b))))
|
|
|
|
:END:
|
|
|
|
|
|
|
|
** Inline graph visualisation with Graphviz
|
|
|
|
Some functions in =dds= build graphs:
|
|
|
|
|
2020-03-02 18:36:26 +01:00
|
|
|
#+BEGIN_SRC racket :results output drawer :var bf=munch-sexp(another-test-table)
|
2020-03-25 00:16:29 +01:00
|
|
|
(build-interaction-graph (unorgv bf))
|
2020-02-22 20:34:24 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
#<unweighted-graph>
|
|
|
|
:END:
|
|
|
|
|
|
|
|
The =graph= library allows building a Graphviz description of the
|
|
|
|
constructed graph. (Note that you have to install the =graph=
|
|
|
|
library by running =raco pkg install graph= and require it. The
|
|
|
|
long property line at the top of this file defining the prologue for
|
|
|
|
racket source code blocks takes care of requiring =graph=.)
|
|
|
|
|
|
|
|
#+NAME: igraph
|
2020-03-02 18:36:26 +01:00
|
|
|
#+BEGIN_SRC racket :results output drawer :var bf=munch-sexp(another-test-table)
|
2020-03-25 00:16:29 +01:00
|
|
|
(display (graphviz (build-interaction-graph (unorgv bf))))
|
2020-02-22 20:34:24 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS: igraph
|
|
|
|
:RESULTS:
|
|
|
|
digraph G {
|
|
|
|
node0 [label="c"];
|
|
|
|
node1 [label="b"];
|
|
|
|
node2 [label="a"];
|
|
|
|
subgraph U {
|
|
|
|
edge [dir=none];
|
|
|
|
node0 -> node1;
|
|
|
|
node2 -> node2;
|
|
|
|
}
|
|
|
|
subgraph D {
|
|
|
|
node2 -> node0;
|
|
|
|
node2 -> node1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
:END:
|
|
|
|
|
|
|
|
You can have an inline drawing of this graph by calling the previous
|
|
|
|
code block (=igraph=) via a noweb reference in Graphviz/DOT source
|
|
|
|
block:
|
|
|
|
|
|
|
|
#+BEGIN_SRC dot :file dots/exampleBQNp7Z.svg :results raw drawer :cmd sfdp :noweb yes
|
|
|
|
<<igraph()>>
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
[[file:dots/exampleBQNp7Z.svg]]
|
|
|
|
:END:
|
|
|
|
|
|
|
|
Note that the =graph= library draws self-loops as undirected edges.
|
|
|
|
It also draws double-sided edges as undirected edges (e.g., in the
|
|
|
|
preceding graph, b depends on c and c depends on b).
|
|
|
|
|
2020-05-28 00:30:27 +02:00
|
|
|
* =dds/networks= and =dds/functions=
|
2020-02-22 21:01:06 +01:00
|
|
|
The [[../networks.rkt][=dds/networks=]] is a module for working with different network
|
|
|
|
models. A network 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.
|
|
|
|
|
2020-05-28 00:30:27 +02:00
|
|
|
[[file:../functions.rkt][=dds/functions=]] is a module for working with the functions
|
|
|
|
underlying the network models. Similarly to =dds/networks=, it
|
|
|
|
provides primitives for tabulating functions, reconstructing
|
|
|
|
functions from tables, generating random functions, etc.
|
|
|
|
|
2020-02-22 21:01:06 +01:00
|
|
|
** Boolean networks
|
|
|
|
Consider the following Boolean network:
|
|
|
|
#+NAME: simple-bn
|
|
|
|
| a | b |
|
|
|
|
| b | (and (not a) c) |
|
|
|
|
| c | (not c) |
|
|
|
|
|
2020-03-04 23:51:11 +01:00
|
|
|
Note that if you define the formula of a as 0, it will set a to 1,
|
|
|
|
because 0 is not #f. For example, =(if 0 1 2)= evaluates to 1, and
|
|
|
|
not to 2.
|
|
|
|
|
2020-02-22 21:01:06 +01:00
|
|
|
Here's the unsigned interaction graph of this network:
|
|
|
|
#+NAME: simple-bn-ig
|
2020-03-02 18:36:26 +01:00
|
|
|
#+BEGIN_SRC racket :results silent :var simple-bn=munch-sexp(simple-bn)
|
2020-03-25 00:16:29 +01:00
|
|
|
(dotit (build-interaction-graph (unorgv simple-bn)))
|
2020-02-22 21:01:06 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+BEGIN_SRC dot :file dots/examplejTo8XT.svg :results raw drawer :cmd sfdp :noweb yes
|
|
|
|
<<simple-bn-ig()>>
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
[[file:dots/examplejTo8XT.svg]]
|
|
|
|
:END:
|
|
|
|
|
2020-02-23 10:14:32 +01:00
|
|
|
Here's the signed interaction graph of this network:
|
|
|
|
|
|
|
|
#+NAME: simple-bn-sig
|
2020-03-02 18:36:26 +01:00
|
|
|
#+BEGIN_SRC racket :results silent :var simple-bn=munch-sexp(simple-bn)
|
2020-03-25 00:16:29 +01:00
|
|
|
(dotit (build-boolean-signed-interaction-graph/form (unorgv simple-bn)))
|
2020-02-23 10:14:32 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+BEGIN_SRC dot :file dots/exampledpQygl.svg :results raw drawer :cmd sfdp :noweb yes
|
|
|
|
<<simple-bn-sig()>>
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
[[file:dots/exampledpQygl.svg]]
|
|
|
|
:END:
|
|
|
|
|
|
|
|
For the interaction a \to b, note indeed that when c is #f, b is
|
|
|
|
always #f (positive interaction). On the other hand, when c is #t,
|
|
|
|
b becomes (not a) (negative interaction). Therefore, the influence
|
|
|
|
of a on b is neither activating nor inhibiting.
|
2020-02-22 21:01:06 +01:00
|
|
|
|
2020-02-26 21:17:11 +01:00
|
|
|
Here is the full state graph of this network under the asynchronous
|
|
|
|
dynamics:
|
|
|
|
#+NAME: simple-bn-sg
|
2020-03-02 18:36:26 +01:00
|
|
|
#+BEGIN_SRC racket :results silent :var simple-bn=munch-sexp(simple-bn)
|
2020-03-25 23:05:28 +01:00
|
|
|
(let* ([bn (network-form->network (unorgv simple-bn))]
|
2020-02-26 21:17:11 +01:00
|
|
|
[bn-asyn (make-asyn-dynamics bn)])
|
2020-03-25 23:15:37 +01:00
|
|
|
(dotit (pretty-print-state-graph (build-full-boolean-state-graph bn-asyn))))
|
2020-02-26 21:17:11 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
2020-03-01 18:01:53 +01:00
|
|
|
|
2020-02-26 21:17:11 +01:00
|
|
|
#+BEGIN_SRC dot :file dots/examplem7LpTs.svg :results raw drawer :cmd sfdp :noweb yes
|
|
|
|
<<simple-bn-sg()>>
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
[[file:dots/examplem7LpTs.svg]]
|
|
|
|
:END:
|
|
|
|
|
|
|
|
Alternatively, you may prefer a slighty more compact representation
|
|
|
|
of Boolean values as 0 and 1:
|
|
|
|
#+NAME: simple-bn-sg-bool
|
2020-03-02 18:36:26 +01:00
|
|
|
#+BEGIN_SRC racket :results silent :var simple-bn=munch-sexp(simple-bn)
|
2020-03-25 23:05:28 +01:00
|
|
|
(let* ([bn (network-form->network (unorgv simple-bn))]
|
2020-02-26 21:17:11 +01:00
|
|
|
[bn-asyn (make-asyn-dynamics bn)])
|
2020-03-25 23:15:37 +01:00
|
|
|
(dotit (pretty-print-boolean-state-graph (build-full-boolean-state-graph bn-asyn))))
|
2020-02-26 21:17:11 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+BEGIN_SRC dot :file dots/examplex1Irnk.svg :results raw drawer :cmd sfdp :noweb yes
|
|
|
|
<<simple-bn-sg-bool()>>
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
[[file:dots/examplex1Irnk.svg]]
|
|
|
|
:END:
|
|
|
|
|
|
|
|
Consider the following state (appearing in the upper left corner of
|
|
|
|
the state graph):
|
|
|
|
|
|
|
|
#+NAME: some-state
|
|
|
|
| a | 0 |
|
|
|
|
| c | 1 |
|
|
|
|
| b | 1 |
|
|
|
|
|
|
|
|
These are the states which can be reached from it in at most 2
|
|
|
|
steps:
|
|
|
|
#+NAME: simple-bn-some-state
|
2020-03-02 18:36:26 +01:00
|
|
|
#+HEADER: :var simple-bn=munch-sexp(simple-bn)
|
|
|
|
#+HEADER: :var some-state=munch-sexp(some-state)
|
2020-02-26 21:17:11 +01:00
|
|
|
#+BEGIN_SRC racket :results silent
|
2020-03-25 23:05:28 +01:00
|
|
|
(let* ([bn (network-form->network (unorgv simple-bn))]
|
2020-02-26 21:17:11 +01:00
|
|
|
[bn-asyn (make-asyn-dynamics bn)]
|
2020-03-25 23:15:37 +01:00
|
|
|
[s0 (booleanize-state (unorgv some-state))])
|
|
|
|
(dotit (pretty-print-boolean-state-graph (dds-build-n-step-state-graph bn-asyn (set s0) 2))))
|
2020-02-26 21:17:11 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+BEGIN_SRC dot :file dots/examplecHA6gL.svg :results raw drawer :cmd sfdp :noweb yes
|
|
|
|
<<simple-bn-some-state()>>
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
[[file:dots/examplecHA6gL.svg]]
|
|
|
|
:END:
|
|
|
|
|
2020-03-01 15:22:11 +01:00
|
|
|
Here is the complete state graph with edges annotated with the
|
|
|
|
modality leading to the update.
|
|
|
|
#+NAME: simple-bn-sg-bool-ann
|
2020-03-02 18:36:26 +01:00
|
|
|
#+BEGIN_SRC racket :results silent :var simple-bn=munch-sexp(simple-bn)
|
2020-03-25 23:05:28 +01:00
|
|
|
(let* ([bn (network-form->network (unorgv simple-bn))]
|
2020-03-01 15:22:11 +01:00
|
|
|
[bn-asyn (make-asyn-dynamics bn)])
|
2020-03-25 23:15:37 +01:00
|
|
|
(dotit (pretty-print-boolean-state-graph (build-full-boolean-state-graph-annotated bn-asyn))))
|
2020-03-01 15:22:11 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+BEGIN_SRC dot :file dots/examplei4we6j.svg :results raw drawer :cmd sfdp :noweb yes
|
|
|
|
<<simple-bn-sg-bool-ann()>>
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
[[file:dots/examplei4we6j.svg]]
|
|
|
|
:END:
|
|
|
|
|
2020-03-01 18:01:53 +01:00
|
|
|
For some networks, a single transition between two states may be
|
|
|
|
due to different modalities. Consider the following network:
|
|
|
|
#+NAME: bn2
|
|
|
|
| a | (not b) |
|
|
|
|
| b | b |
|
|
|
|
|
|
|
|
#+NAME: bn2-sgr
|
2020-03-02 18:36:26 +01:00
|
|
|
#+BEGIN_SRC racket :results silent :var input-bn=munch-sexp(bn2)
|
2020-03-25 23:05:28 +01:00
|
|
|
(let* ([bn (network-form->network (unorgv input-bn))]
|
2020-03-01 18:01:53 +01:00
|
|
|
[bn-asyn (make-asyn-dynamics bn)])
|
2020-03-25 23:15:37 +01:00
|
|
|
(dotit (pretty-print-boolean-state-graph (build-full-boolean-state-graph-annotated bn-asyn))))
|
2020-03-01 18:01:53 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+BEGIN_SRC dot :file dots/examplehsuRqc.svg :results raw drawer :cmd dot :noweb yes
|
|
|
|
<<bn2-sgr()>>
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
[[file:dots/examplehsuRqc.svg]]
|
|
|
|
:END:
|
|
|
|
|
2020-03-22 19:36:07 +01:00
|
|
|
** Tabulating functions and networks
|
2020-03-15 16:12:35 +01:00
|
|
|
Here's how you can tabulate a function. The domain of x is {1, 2},
|
|
|
|
and the domain of y is {0, 2, 4}. The first column in the output
|
|
|
|
corresponds to x, the second to y, and the third corresponds to the
|
|
|
|
value of the function.
|
|
|
|
|
|
|
|
#+BEGIN_SRC racket :results table drawer
|
2020-05-31 23:41:56 +02:00
|
|
|
(tabulate (λ (x y) (+ x y)) '((1 2) (0 2 4)))
|
2020-03-15 16:12:35 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
| 1 | 0 | 1 |
|
|
|
|
| 1 | 2 | 3 |
|
|
|
|
| 1 | 4 | 5 |
|
|
|
|
| 2 | 0 | 2 |
|
|
|
|
| 2 | 2 | 4 |
|
|
|
|
| 2 | 4 | 6 |
|
|
|
|
:END:
|
|
|
|
|
|
|
|
Here's how you tabulate a Boolean function:
|
|
|
|
#+BEGIN_SRC racket :results table drawer
|
|
|
|
(tabulate/boolean (λ (x y) (and x y)))
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
| #f | #f | #f |
|
|
|
|
| #f | #t | #f |
|
|
|
|
| #t | #f | #f |
|
|
|
|
| #t | #t | #t |
|
|
|
|
:END:
|
|
|
|
|
2020-05-31 23:42:16 +02:00
|
|
|
You can tabulate multiple functions taking the same arguments over
|
|
|
|
the same domains together.
|
|
|
|
#+BEGIN_SRC racket :results table drawer
|
|
|
|
(tabulate*/boolean `(,(λ (x y) (and x y)) ,(λ (x y) (or x y))))
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
| #f | #f | #f | #f |
|
|
|
|
| #f | #t | #f | #t |
|
|
|
|
| #t | #f | #f | #t |
|
|
|
|
| #t | #t | #t | #t |
|
|
|
|
:END:
|
|
|
|
|
2020-03-22 20:22:23 +01:00
|
|
|
Here's how to tabulate the network =simple-bn=, defined at the top
|
|
|
|
of this section:
|
|
|
|
#+BEGIN_SRC racket :results table drawer :var in-bn=munch-sexp(simple-bn)
|
2020-03-25 23:05:28 +01:00
|
|
|
(tabulate-boolean-network (network-form->network (unorgv in-bn)))
|
2020-03-22 20:22:23 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
| a | b | c | f-a | f-b | f-c |
|
|
|
|
| #f | #f | #f | #f | #f | #t |
|
|
|
|
| #f | #f | #t | #f | #t | #f |
|
|
|
|
| #f | #t | #f | #t | #f | #t |
|
|
|
|
| #f | #t | #t | #t | #t | #f |
|
|
|
|
| #t | #f | #f | #f | #f | #t |
|
|
|
|
| #t | #f | #t | #f | #f | #f |
|
|
|
|
| #t | #t | #f | #t | #f | #t |
|
|
|
|
| #t | #t | #t | #t | #f | #f |
|
|
|
|
:END:
|
|
|
|
|
2020-03-22 20:38:32 +01:00
|
|
|
** Random functions and networks
|
|
|
|
To avoid having different results every time a code block in this
|
|
|
|
section is run, every code block seeds the random number generator
|
|
|
|
to 0.
|
|
|
|
|
|
|
|
=dds/networks= can generate random functions, given a domain for
|
|
|
|
each of its arguments and for the function itself. Consider the
|
|
|
|
following domains:
|
|
|
|
#+NAME: simple-domains
|
|
|
|
| a | (#f #t) |
|
|
|
|
| b | (1 2) |
|
|
|
|
| c | (cold hot) |
|
|
|
|
|
|
|
|
Here's a random function taking values in the codomain =(4 5 6)=:
|
|
|
|
#+BEGIN_SRC racket :results table drawer :var simple-domains=munch-sexp(simple-domains)
|
|
|
|
(random-seed 0)
|
2020-03-25 00:16:29 +01:00
|
|
|
(define rnd-func (random-function/state (unorgv simple-domains) '(4 5 6)))
|
|
|
|
(tabulate-state rnd-func (unorgv simple-domains))
|
2020-03-22 20:38:32 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
| a | b | c | f |
|
|
|
|
| #f | 1 | cold | 4 |
|
|
|
|
| #f | 1 | hot | 5 |
|
|
|
|
| #f | 2 | cold | 4 |
|
|
|
|
| #f | 2 | hot | 4 |
|
|
|
|
| #t | 1 | cold | 5 |
|
|
|
|
| #t | 1 | hot | 6 |
|
|
|
|
| #t | 2 | cold | 4 |
|
|
|
|
| #t | 2 | hot | 5 |
|
|
|
|
:END:
|
|
|
|
|
|
|
|
We can build an entire random network over these domains:
|
|
|
|
#+BEGIN_SRC racket :results table drawer :var simple-domains=munch-sexp(simple-domains)
|
|
|
|
(random-seed 0)
|
2020-03-25 23:29:01 +01:00
|
|
|
(define n (random-network (unorgv simple-domains)))
|
|
|
|
(tabulate-network n (unorgv simple-domains))
|
2020-03-22 20:38:32 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
| a | b | c | f-a | f-b | f-c |
|
2020-03-22 21:03:20 +01:00
|
|
|
| #f | 1 | cold | #f | 2 | hot |
|
|
|
|
| #f | 1 | hot | #f | 2 | cold |
|
|
|
|
| #f | 2 | cold | #t | 1 | cold |
|
2020-03-22 20:38:32 +01:00
|
|
|
| #f | 2 | hot | #t | 2 | hot |
|
2020-03-22 21:03:20 +01:00
|
|
|
| #t | 1 | cold | #f | 2 | cold |
|
|
|
|
| #t | 1 | hot | #t | 1 | cold |
|
|
|
|
| #t | 2 | cold | #f | 2 | hot |
|
|
|
|
| #t | 2 | hot | #t | 1 | cold |
|
2020-03-22 20:38:32 +01:00
|
|
|
:END:
|
|
|
|
|
2020-05-08 23:33:43 +02:00
|
|
|
Let's snapshot this random network and give it a name.
|
|
|
|
#+NAME: rnd-network
|
|
|
|
| a | b | c | f-a | f-b | f-c |
|
|
|
|
| #f | 1 | cold | #f | 2 | hot |
|
|
|
|
| #f | 1 | hot | #f | 2 | cold |
|
|
|
|
| #f | 2 | cold | #t | 1 | cold |
|
|
|
|
| #f | 2 | hot | #t | 2 | hot |
|
|
|
|
| #t | 1 | cold | #f | 2 | cold |
|
|
|
|
| #t | 1 | hot | #t | 1 | cold |
|
|
|
|
| #t | 2 | cold | #f | 2 | hot |
|
|
|
|
| #t | 2 | hot | #t | 1 | cold |
|
|
|
|
|
|
|
|
Here's how we can read back this table as a Boolean network:
|
|
|
|
#+HEADER: :var rnd-network=munch-sexp(rnd-network)
|
|
|
|
#+BEGIN_SRC racket :results output drawer
|
|
|
|
(string->any rnd-network)
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
'(("a" "b" "c" "f-a" "f-b" "f-c") ("#f" 1 "cold" "#f" 2 "hot") ("#f" 1 "hot" "#f" 2 "cold") ("#f" 2 "cold" "#t" 1 "cold") ("#f" 2 "hot" "#t" 2 "hot") ("#t" 1 "cold" "#f" 2 "cold") ("#t" 1 "hot" "#t" 1 "cold") ("#t" 2 "cold" "#f" 2 "hot") ("#t" 2 "hot" "#t" 1 "cold"))
|
|
|
|
:END:
|
|
|
|
|
|
|
|
You can use =table->network= to convert a table such as [[rnd-network][rnd-network]]
|
|
|
|
to a network.
|
|
|
|
#+HEADER: :var rnd-network=munch-sexp(rnd-network)
|
|
|
|
#+BEGIN_SRC racket :results output drawer
|
|
|
|
(table->network (unorg rnd-network))
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
'#hash((a . #<procedure:...dds/networks.rkt:518:4>) (b . #<procedure:...dds/networks.rkt:518:4>) (c . #<procedure:...dds/networks.rkt:518:4>))
|
|
|
|
:END:
|
|
|
|
|
|
|
|
Here's the state graph of [[rnd-network][rnd-network]].
|
|
|
|
#+NAME: rnd-network-sg
|
|
|
|
#+HEADER: :var rnd-network=munch-sexp(rnd-network)
|
|
|
|
#+HEADER: :var simple-domains=munch-sexp(simple-domains)
|
|
|
|
#+BEGIN_SRC racket :results silent drawer
|
|
|
|
(define n (table->network (unorg rnd-network)))
|
|
|
|
(define rnd-asyn (make-asyn-dynamics n))
|
|
|
|
(define states (list->set (build-all-states (unorgv simple-domains))))
|
|
|
|
(dotit (pretty-print-state-graph (dds-build-state-graph-annotated rnd-asyn states)))
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+BEGIN_SRC dot :file dots/exampleHc023j.svg :results raw drawer :cmd sfdp :noweb yes
|
|
|
|
<<rnd-network-sg()>>
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
[[file:dots/exampleHc023j.svg]]
|
|
|
|
:END:
|
|
|
|
|
|
|
|
Here's the signed interaction graph of [[rnd-network][rnd-network]].
|
|
|
|
#+NAME: rnd-network-ig
|
|
|
|
#+HEADER: :var rnd-network=munch-sexp(rnd-network)
|
|
|
|
#+HEADER: :var simple-domains=munch-sexp(simple-domains)
|
|
|
|
#+BEGIN_SRC racket :results silent drawer
|
|
|
|
(define n (table->network (unorg rnd-network)))
|
|
|
|
(dotit (build-signed-interaction-graph n (unorgv simple-domains)))
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+BEGIN_SRC dot :file dots/examplePIN5ac.svg :results raw drawer :cmd sfdp :noweb yes
|
|
|
|
<<rnd-network-ig()>>
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
[[file:dots/examplePIN5ac.svg]]
|
|
|
|
:END:
|
|
|
|
|
|
|
|
Note that =build-signed-interaction-graph= only includes the + and
|
|
|
|
the - arcs in the graph, as it does not have access to the symbolic
|
|
|
|
description of the function.
|
2020-03-22 20:22:23 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
* Reaction systems
|
|
|
|
:PROPERTIES:
|
2020-05-16 00:01:36 +02:00
|
|
|
:header-args:racket: :prologue "#lang racket\n(require graph dds/rs dds/utils)"
|
2020-03-04 18:21:20 +01:00
|
|
|
:END:
|
|
|
|
Consider the following reaction system:
|
|
|
|
#+NAME: rs1
|
|
|
|
| a | x t | y | z |
|
|
|
|
| b | x | q | z |
|
|
|
|
|
|
|
|
Here is how we read this reaction into Racket code:
|
|
|
|
|
|
|
|
#+BEGIN_SRC racket :results output drawer :var input-rs=munch-sexp(rs1)
|
2020-03-24 23:22:20 +01:00
|
|
|
(read-org-rs input-rs)
|
2020-03-04 18:21:20 +01:00
|
|
|
#+END_SRC
|
2020-03-01 20:26:16 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
(hash 'a (reaction (set 'x 't) (set 'y) (set 'z)) 'b (reaction (set 'x) (set 'q) (set 'z)))
|
|
|
|
:END:
|
2020-03-01 20:26:16 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
Here is how we can put it back into an Org-mode table:
|
|
|
|
#+BEGIN_SRC racket :results table drawer :var input-rs=munch-sexp(rs1)
|
2020-03-24 23:22:20 +01:00
|
|
|
(rs->ht-str-triples (read-org-rs input-rs))
|
2020-03-04 18:21:20 +01:00
|
|
|
#+END_SRC
|
2020-03-01 21:10:01 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
| a | "t x" | "y" | "z" |
|
|
|
|
| b | "x" | "q" | "z" |
|
|
|
|
:END:
|
2020-03-01 21:10:01 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
Here is how we can apply this reaction system to a state:
|
|
|
|
#+BEGIN_SRC racket :results output drawer :var input-rs=munch-sexp(rs1)
|
2020-03-24 23:22:20 +01:00
|
|
|
(let ([rs (read-org-rs input-rs)])
|
2020-03-01 21:14:18 +01:00
|
|
|
(apply-rs rs (set 'x 't)))
|
2020-03-04 18:21:20 +01:00
|
|
|
#+END_SRC
|
2020-03-01 21:14:18 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
(set 'z)
|
|
|
|
:END:
|
2020-03-01 21:14:18 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
Let's see which reactions got applied:
|
|
|
|
#+BEGIN_SRC racket :results list :var input-rs=munch-sexp(rs1)
|
2020-03-24 23:22:20 +01:00
|
|
|
(let ([rs (read-org-rs input-rs)])
|
2020-03-01 21:14:18 +01:00
|
|
|
(list-enabled rs (set 'x 't)))
|
2020-03-04 18:21:20 +01:00
|
|
|
#+END_SRC
|
2020-03-01 21:14:18 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
#+RESULTS:
|
|
|
|
- a
|
|
|
|
- b
|
2020-03-01 21:14:18 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
You can also give a name to a list and read it with =munch-sexp=:
|
2020-03-02 23:11:26 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
#+NAME: ctx1
|
|
|
|
- x y
|
|
|
|
- z
|
|
|
|
-
|
|
|
|
- t
|
2020-03-02 23:11:26 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
#+BEGIN_SRC racket :results output drawer :var input-ctx=munch-sexp(ctx1)
|
2020-03-24 23:22:20 +01:00
|
|
|
(read-context-sequence input-ctx)
|
2020-03-04 18:21:20 +01:00
|
|
|
#+END_SRC
|
2020-03-02 23:11:26 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
(list (set 'x 'y) (set 'z) (set) (set 't))
|
|
|
|
:END:
|
2020-02-26 21:17:11 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
Let's see what the evolution of =rs1= looks like with the context
|
|
|
|
sequence =ctx1=.
|
2020-03-03 00:25:26 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
#+NAME: rs1-sgr
|
|
|
|
#+HEADER: :var input-rs=munch-sexp(rs1) :var input-ctx=munch-sexp(ctx1)
|
|
|
|
#+BEGIN_SRC racket :results output drawer
|
2020-03-24 23:22:20 +01:00
|
|
|
(dotit (pretty-print-state-graph (build-interactive-process-graph (read-org-rs input-rs) (read-context-sequence input-ctx))))
|
2020-03-04 18:21:20 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS: rs1-sgr
|
|
|
|
:RESULTS:
|
|
|
|
digraph G {
|
2020-05-16 00:18:36 +02:00
|
|
|
node0 [label="C:{z}{}{t}\nD:{z}"];
|
|
|
|
node1 [label="C:{}{t}\nD:{}"];
|
|
|
|
node2 [label="C:{x y}{z}{}{t}\nD:{}"];
|
|
|
|
node3 [label="C:{t}\nD:{}"];
|
2020-03-04 18:21:20 +01:00
|
|
|
node4 [label="C:\nD:{}"];
|
|
|
|
subgraph U {
|
|
|
|
edge [dir=none];
|
|
|
|
node4 -> node4 [label="{}"];
|
|
|
|
}
|
|
|
|
subgraph D {
|
2020-05-16 00:18:36 +02:00
|
|
|
node0 -> node1 [label="{}"];
|
|
|
|
node1 -> node3 [label="{}"];
|
|
|
|
node2 -> node0 [label="{b}"];
|
|
|
|
node3 -> node4 [label="{}"];
|
2020-03-04 18:21:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
:END:
|
|
|
|
|
|
|
|
#+BEGIN_SRC dot :file dots/examplevvXFaI.svg :results raw drawer :cmd circo :noweb yes
|
2020-03-03 00:25:26 +01:00
|
|
|
<<rs1-sgr()>>
|
2020-03-04 18:21:20 +01:00
|
|
|
#+END_SRC
|
2020-03-03 00:25:26 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
#+RESULTS:
|
|
|
|
:RESULTS:
|
|
|
|
[[file:dots/examplevvXFaI.svg]]
|
|
|
|
:END:
|
2020-03-03 00:25:26 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
Note that we need to keep the full context sequence in the name of
|
|
|
|
each state to avoid merging states with the same result and
|
|
|
|
contexts, but which occur at different steps of the evolution.
|
2020-03-03 00:25:26 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
The graphical presentation for interactive processes is arguably
|
|
|
|
less readable than just listing the contexts and the results
|
|
|
|
explicitly. Here is how you can do it.
|
2020-03-03 01:01:21 +01:00
|
|
|
|
2020-03-04 18:21:20 +01:00
|
|
|
#+NAME: rs1-ip
|
|
|
|
#+HEADER: :var input-rs=munch-sexp(rs1) :var input-ctx=munch-sexp(ctx1)
|
|
|
|
#+BEGIN_SRC racket :results table drawer
|
2020-03-24 23:22:20 +01:00
|
|
|
(build-interactive-process (read-org-rs input-rs) (read-context-sequence input-ctx))
|
2020-03-04 18:21:20 +01:00
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
#+RESULTS: rs1-ip
|
|
|
|
:RESULTS:
|
|
|
|
| (y x) | nil |
|
|
|
|
| (z) | (z) |
|
|
|
|
| nil | nil |
|
|
|
|
| (t) | nil |
|
|
|
|
| nil | nil |
|
|
|
|
:END:
|
|
|
|
|
|
|
|
The first column of this table shows the current context. The
|
|
|
|
second column shows the result of application of the reactions to
|
|
|
|
the previous state. The interactive process contains one more step
|
|
|
|
with respect to the context sequence. This is to show the effect
|
|
|
|
of the last context.
|
|
|
|
|
|
|
|
Note that empty sets are printed as =nil=.
|
2020-03-03 01:01:21 +01:00
|
|
|
|
|
|
|
|
2020-02-22 20:34:24 +01:00
|
|
|
* Local Variables :noexport:
|
|
|
|
# Local Variables:
|
|
|
|
# eval: (auto-fill-mode)
|
|
|
|
# ispell-local-dictionary: "en"
|
|
|
|
# org-link-file-path-type: relative
|
|
|
|
# End:
|