dds/scribblings/networks.scrbl
2022-09-16 17:57:15 +02:00

604 lines
17 KiB
Racket

#lang scribble/manual
@(require scribble/example racket/sandbox
(for-label typed/racket/base
graph
(only-in typed/graph Graph)
(only-in racket/class send)
(submod "../networks.rkt" typed)
"../utils.rkt"
"../functions.rkt"
"../dynamics.rkt"))
@(define networks-evaluator
(parameterize ([sandbox-output 'string]
[sandbox-error-output 'string]
[sandbox-memory-limit 50])
(make-evaluator 'typed/racket #:requires '((submod "networks.rkt" typed)))))
@(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}
@defmodule[(submod dds/networks typed)]
This module provides definitions for and analysing 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.
@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}
@deftype[UpdateFunctionForm]{
An update function form is any form which can appear as a body of a function
and which can be evaluated with @racket[eval].
@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[
(ann (inst dynamics% Boolean) (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))]
}
}
@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}
@section{Constructing functions and networks}
@section{Random functions and networks}
@section{TBF/TBN and SBF/SBN}
This section defines threshold Boolean functions (TBF) and networks (TBN), as
well as sign Boolean functions (SBF) and networks (SBN).