A. Model distribution - dwweiss/grayboxes GitHub Wiki
- 1. Recursive call of control loop
- 2. Followers vs cooperators
- 3. Object connection
- Example 1
- Example 2
White box and gray box models employ theoretical submodels. The building of complex submodels can be supported by distribution on logical or physical subdomains. Distributed theoretical models are organized in overlapping trees with unique keys.
The concept of conservative leader-follower relationships (authoritarian) is extended by leader-cooperator relationships (partnership). The differentiation into leader-follower and leader-cooperator relationships allows the creation of complex object structures which can coexist in space, time or abstract contexts, Fig. 1. The preprocesses, tasks and postprocesses on all subdomains are called recursively starting with the root() subdomain. The code execution sequence is shown in Fig. 2.
root
| other leader
| |
+------followers------+........... +-----+ ...
| | | : |
| | | : |
v v v v v
follower1 follower2 follower3 cooperator1
transient or
---iterative----
---> pre() | ^
| v |
---+---> control() ----o---> task() ----o---> stop
|
---> post()
This structure supports:
-
Distributed development of objects derived from the connecting Base class
-
Object specialization in "vertical" and "horizontal" direction
- leader-type objects implementing alternative control of tasks
- peer-type opjects implementing empirical models (data-driven), theoretical models and knowledge objects (e.g. properties of matter)
-
Uniform interface to code execution of objects derived from the Base class via public functions
- pre-process: pre() which calls load()
- main task: task() which is called by control()
- post-process: post() which calls save()
-
Recursive modification of local settings on sub-domains, for instance file path or silent mode of all followers.
The __call__() function calls pre(), control() and post() of followers and cooperators recursively. The iterative or transient repetition of the task() method is controlled by method control() of the Loop class, see Fig. 3.
leader object
^
|
--|--------- transient or
| leader() | ---iterative----
|------------| ---> pre() | ^
| | | v |
| self.__call__() ---+---> control() ----o---> task() ----o---> stop
| | |
| | ---> post()
| |
|------------|
| follower() |
--|---------
|-> follower/cooperator.__call__()
|-> follower/cooperator.__call__()
:
Leader objects have limited permissions when interacting with cooperators:
- A follower has a reference to its direct leader and this leader owns an array of references to all of its followers. A leader can destroy its followers.
- In an authoritarian relationship communication can be initiated by both the potential leader calling set_follower() or the potential follower calling set_leader().
- A cooperator keeps its direct leader if invited to the array of followers of another leader. The invitation is initiated by the foreign leader calling set_cooperator(). If the leader of the future cooperator is None, a call of set_follower() is identical to set_cooperator().
- A leader is not able to destruct a cooperator, but it can call its pre-process, task and post-process, it can assign a new file path or modify basic settings such as silent mode.
- A typical cooperator is an object providing properties of matter. It has only a pre-process (e.g. loading data) and getter-methods.
The concept is comparable to relationships in or between human groups with hard "leader-follower" character. Beside such relationships, human group members may initiate softer "leader-cooperator" associations via engagement, employment or neighborhood, see Example 1.
During code execution, the connectivity structure can be modified as in case of a marriage where a new family object is created to which the spouses are connected as followers. Due to husband's and wife's origin from two family objects, their follower relationship to the originating families will be converted to cooperator relationships, see Example 2.
Jensen family Christensen family
| |
| |
+------followers------+ +--followers--+
| | | | |
| | | | |
v v v v v
Henrik Jytte Jens Sanne Pia
(father) (mother) (son) (daughter) (mother)
Jensen family Christensen family
| |
| |
+------followers------+ .......cooperator......... +--followers--+
| | | : | |
| | | : | |
v v v v v v
Henrik Jytte Jens <......cooperator...... Sanne Pia
(father) (mother) (son) (daughter) (mother)
# initial situation (figure 4)
JensenFamily = Base('jensenFamily')
Henrik = Base('henrik')
Jytte = Base('jytte')
Jens = Base('jens')
JensenFamily.set_follower([Henrik, Jytte, Jens])
ChristensenFamily = Base('christensenFamily')
Sanne = Base('sanne')
Pia = Base('pia')
ChristensenFamily.set_follower([Sanne, Pia])
# engagement of Jens with Sanne (figure 5)
JensenFamily.set_cooperator(Sanne) # Sanne becomes cooperator ofJens'family
Sanne.set_cooperator(Jens) # Jens becomes cooperator of Sanne
# Note: Here set_cooperator() is
# identical to set_follower()
# because object has already leader
print(JensenFamily)
print(ChristensenFamily)
JensenFamily() # calls Sanne's pre(), task(), post() functions once
# because Sanne is cooperator of the Jensen family. Jens' pre(), task()
# and post() functions are executed twice because Jens is both
# (1) follower of Jensen family who calls him as follower
# (2) cooperator of Sanne who is self cooperator of Jensen family
# (Jens is called as cooperator of a cooperator)
ChristensenFamily() # call Jens' pre(), task(), post() once
# relationship checks
b = JensenFamily.is_cooperator('jens') # b: False
b = JensenFamily.is_cooperator('sanne') # b: True
b = id(JensenFamily) == id(Jens.leader()) # b: True
b = id(JensenFamily) == id(Sanne.leader()) # b: False
p = JensenFamily['jens'] # p: not None
p = JensenFamily['sanne'] # p: None
b = ChristensenFamily.is_cooperator('jens') # b: True
b = ChristensenFamily.is_cooperator('sanne') # b: False
b = id(ChristensenFamily) == id(Jens.leader()) # b: False
b = id(ChristensenFamily) == id(Sanne.leader()) # b: True
p = ChristensenFamily['jens'] # p: None
p = ChristensenFamily['sanne'] # p: not None
Creation of a new leader (Jensen Junior family) with followership to Jensen family (solid line) and cooperation to Christensen family (dotted line). Jens and Sanne are followers to Jensen Junior family as husband and wife (solid line). Also Jens is cooperator to Jensen family as son (dotted line) and Sanne is cooperator to Christensen family as daughter (dotted line).
Jensen family Christensen family
| |
+----------+----------+-------+ .........+-----+------+
| | | | : | |
| | x | : x |
| | : v v : |
| | : Jensen Jr. family : |
| | : | | : |
v v v v v v v
Henrik Jytte Jens Sanne Pia
(father) (mother) (son & husband) (wife & daughter) (mother)
# same initial situation as in example 1 (figure 4)
JensenFamily = Base('jensenFamily')
Henrik = Base('henrik')
Jytte = Base('jytte')
Jens = Base('jens')
JensenFamily.set_follower([Henrik, Jytte, Jens])
ChristensenFamily = Base('christensenFamily')
Sanne = Base('sanne')
Pia = Base('pia')
ChristensenFamily.set_follower([Sanne, Pia])
# now Jensen and Christensen families release Jens and Sanne from their
# followerships, Jens and Sanne become followers of the new Jensen Junior
# family and cooperators of Jensen and Christensen families (figure 7)
JensenJuniorFamily = Base('jensenJuniorFamily')
JensenFamily.release(Jens)
ChristensenFamily.release(Sanne)
JensenJuniorFamily.set_follower([Jens, Sanne])
JensenFamily.set_cooperator(Jens)
ChristensenFamily.set_cooperator(Sanne)
# finally, Jensen Junior family becomes follower of Jensen family and
# cooperator of Christensen family
JensenFamily.set_follower(JensenJuniorFamily)
ChristensenFamily.set_cooperator(JensenJuniorFamily)
# has_leader = JensenJuniorFamily.leader() is not None # has_leader: True
n_level = JensenJuniorFamily.tree_level() # n_level: 1, directly below root
# call both leaders
JensenFamily()
ChristensenFamily()
# alternatively, Jensen Jr. family could become a cooperator of both
# originating families because the Jensen Jr. family can be an independent
# object without leader
JensenFamily.release(JensenJuniorFamily)
JensenFamily.set_cooperator(JensenJuniorFamily)
# relationship checks
has_leader = JensenJuniorFamily.leader() is not None # has_leader: False
n_level = JensenJuniorFamily.tree_level() # n_level: 0, top leader
# call first leader
JensenFamily()