11#=
2- Algae growth
2+ # Algae growth
33
4- Alejandro Morales - May 2023
4+ Alejandro Morales
5+
6+ Centre for Crop Systems Analysis - Wageningen University
57
68In this first example, we learn how to create a `Graph` and update it
79dynamically with rewriting rules.
@@ -10,12 +12,13 @@ The model described here is based on the non-branching model of [algae
1012growth](https://en.wikipedia.org/wiki/L-system#Example_1:_Algae) proposed by
1113Lindermayer as one of the first L-systems.
1214
13- First, we need to load the VirtualPlantLab metapackage, which will automatically load all
14- the packages in the VirtualPlantLab ecosystem.
15+ First, we need to load the VPL metapackage, which will automatically load all
16+ the packages in the VPL ecosystem.
17+
1518=#
1619using VirtualPlantLab
17-
1820#=
21+
1922The rewriting rules of the L-system are as follows:
2023
2124**axiom**: A
@@ -24,115 +27,127 @@ The rewriting rules of the L-system are as follows:
2427
2528**rule 2**: B $\rightarrow$ A
2629
27- In VirtualPlantLab , this L-system would be implemented as a graph where the nodes can be of
30+ In VPL , this L-system would be implemented as a graph where the nodes can be of
2831type `A` or `B` and inherit from the abstract type `Node`. It is advised to
2932include type definitions in a module to avoid having to restart the Julia
3033session whenever we want to redefine them. Because each module is an independent
31- namespace, we need to import `Node` from the VirtualPlantLab package inside the module:
34+ namespace, we need to import `Node` from the VPL package inside the module:
35+
3236=#
3337module algae
34- import VirtualPlantLab: Node
35- struct A <: Node end
36- struct B <: Node end
38+ import VirtualPlantLab: Node
39+ struct A <: Node end
40+ struct B <: Node end
3741end
3842import . algae
43+ #=
3944
40- let
41- #=
42- Note that in this very example we do not need to store any data or state inside
43- the nodes, so types `A` and `B` do not require fields.
44-
45- The axiom is simply defined as an instance of type of `A`:
46- =#
47- axiom = algae. A ()
48-
49- #=
50- The rewriting rules are implemented in VirtualPlantLab as objects of type `Rule`. In VirtualPlantLab, a
51- rewriting rule substitutes a node in a graph with a new node or subgraph and is
52- therefore composed of two parts:
53-
54- 1. A condition that is tested against each node in a graph to choose which nodes
55- to rewrite.
56- 2. A subgraph that will replace each node selected by the condition above.
57-
58- In VirtualPlantLab, the condition is split into two components:
59-
60- 1. The type of node to be selected (in this example that would be `A` or `B`).
61- 2. A function that is applied to each node in the graph (of the specified type)
62- to indicate whether the node should be selected or not. This function is
63- optional (the default is to select every node of the specified type).
64-
65- The replacement subgraph is specified by a function that takes as input the node
66- selected and returns a subgraph defined as a combination of node objects.
67- Subgraphs (which can also be used as axioms) are created by linearly combining
68- objects that inherit from `Node`. The operation `+` implies a linear
69- relationship between two nodes and `[]` indicates branching.
70-
71- The implementation of the two rules of algae growth model in VirtualPlantLab is as follows:
72- =#
73- rule1 = Rule (algae. A, rhs = x -> algae. A () + algae. B ())
74- rule2 = Rule (algae. B, rhs = x -> algae. A ())
75-
76- #=
77- Note that in each case, the argument `rhs` is being assigned an anonymous (aka
78- *lambda*) function. This is a function without a name that is defined directly
79- in the assigment to the argument. That is, the Julia expression `x -> A() + B()`
80- is equivalent to the following function definition:
81- =#
82- function rule_1 (x)
83- algae. A () + algae. B ()
84- end
85-
86- #=
87- For simple rules (especially if the right hand side is just a line of code) it
88- is easier to just define the right hand side of the rule with an anonymous
89- function rather than creating a standalone function with a meaningful name.
90- However, standalone functions are easier to debug as you can call them directly
91- from the REPL.
92-
93- With the axiom and rules we can now create a `Graph` object that represents the
94- algae organism. The first argument is the axiom and the second is a tuple with
95- all the rewriting rules:
96- =#
97- organism = Graph (axiom = axiom, rules = (rule1, rule2))
98-
99- #=
100- If we apply the rewriting rules iteratively, the graph will grow, in this case
101- representing the growth of the algae organism. The rewriting rules are applied
102- on the graph with the function `rewrite!()`:
103- =#
104- rewrite! (organism)
45+ Note that in this very example we do not need to store any data or state inside
46+ the nodes, so types `A` and `B` do not require fields.
47+
48+ The axiom is simply defined as an instance of type of `A`:
49+
50+ =#
51+ axiom = algae. A ()
52+ #=
53+
54+ The rewriting rules are implemented in VPL as objects of type `Rule`. In VPL, a
55+ rewriting rule substitutes a node in a graph with a new node or subgraph and is
56+ therefore composed of two parts:
57+
58+ 1. A condition that is tested against each node in a graph to choose which nodes
59+ to rewrite.
60+ 2. A subgraph that will replace each node selected by the condition above.
10561
106- #=
107- Since there was only one node of type `A`, the only rule that was applied was
108- `rule1`, so the graph should now have two nodes of types `A` and `B`,
109- respectively. We can confirm this by drawing the graph. We do this with the
110- function `draw()` which will always generate the same representation of the
111- graph, but different options are available depending on the context where the
112- code is executed. By default, `draw()` will create a new window where an
113- interactive version of the graph will be drawn and one can zoom and pan with the
114- mouse.
115- =#
116- import GLMakie
117- draw (organism)
118-
119- #=
120- Notice that each node in the network representation is labelled with the type of
121- node (`A` or `B` in this case) and a number in parenthesis. This number is a
122- unique identifier associated to each node and it is useful for debugging
123- purposes (this will be explained in more advanced examples).
124-
125- Applying multiple iterations of rewriting can be achieved with a simple loop:
126- =#
127- for i = 1 : 4
128- rewrite! (organism)
129- end
130-
131- # And we can verify that the graph grew as expected:
132- draw (organism)
133-
134- # The network is rather boring as the system is growing linearly (no branching)
135- # but it already illustrates how graphs can grow rapidly in just a few
136- # iterations. Remember that the interactive visualization allows adjusting the
137- # zoom, which is handy when graphs become large.
62+ In VPL, the condition is split into two components:
63+
64+ 1. The type of node to be selected (in this example that would be `A` or `B`).
65+ 2. A function that is applied to each node in the graph (of the specified type)
66+ to indicate whether the node should be selected or not. This function is
67+ optional (the default is to select every node of the specified type).
68+
69+ The replacement subgraph is specified by a function that takes as input the node
70+ selected and returns a subgraph defined as a combination of node objects.
71+ Subgraphs (which can also be used as axioms) are created by linearly combining
72+ objects that inherit from `Node`. The operation `+` implies a linear
73+ relationship between two nodes and `[]` indicates branching.
74+
75+ The implementation of the two rules of algae growth model in VPL is as follows:
76+
77+ =#
78+ rule1 = Rule (algae. A, rhs = x -> algae. A () + algae. B ())
79+ rule2 = Rule (algae. B, rhs = x -> algae. A ())
80+ #=
81+
82+ Note that in each case, the argument `rhs` is being assigned an anonymous (aka
83+ *lambda*) function. This is a function without a name that is defined directly
84+ in the assigment to the argument. That is, the Julia expression `x -> A() + B()`
85+ is equivalent to the following function definition:
86+
87+ =#
88+ function rule_1 (x)
89+ algae. A () + algae. B ()
13890end
91+ #=
92+
93+ For simple rules (especially if the right hand side is just a line of code) it
94+ is easier to just define the right hand side of the rule with an anonymous
95+ function rather than creating a standalone function with a meaningful name.
96+ However, standalone functions are easier to debug as you can call them directly
97+ from the REPL.
98+
99+ With the axiom and rules we can now create a `Graph` object that represents the
100+ algae organism. The first argument is the axiom and the second is a tuple with
101+ all the rewriting rules:
102+
103+ =#
104+ organism = Graph (axiom = axiom, rules = (rule1, rule2))
105+ #=
106+
107+ If we apply the rewriting rules iteratively, the graph will grow, in this case
108+ representing the growth of the algae organism. The rewriting rules are applied
109+ on the graph with the function `rewrite!()`:
110+
111+ =#
112+ rewrite! (organism)
113+ #=
114+
115+ Since there was only one node of type `A`, the only rule that was applied was
116+ `rule1`, so the graph should now have two nodes of types `A` and `B`,
117+ respectively. We can confirm this by drawing the graph. We do this with the
118+ function `draw()` which will always generate the same representation of the
119+ graph, but different options are available depending on the context where the
120+ code is executed. By default, `draw()` will create a new window where an
121+ interactive version of the graph will be drawn and one can zoom and pan with the
122+ mouse (in this online document a static version is shown, see
123+ [Backends](../manual/Visualization.md) for details):
124+
125+ =#
126+ import GLMakie
127+ draw (organism)
128+ #=
129+
130+ Notice that each node in the network representation is labelled with the type of
131+ node (`A` or `B` in this case) and a number in parenthesis. This number is a
132+ unique identifier associated to each node and it is useful for debugging
133+ purposes (this will be explained in more advanced examples).
134+
135+ Applying multiple iterations of rewriting can be achieved with a simple loop:
136+
137+ =#
138+ for i in 1 : 4
139+ rewrite! (organism)
140+ end
141+ #=
142+
143+ And we can verify that the graph grew as expected:
144+
145+ =#
146+ draw (organism)
147+ #=
148+
149+ The network is rather boring as the system is growing linearly (no branching)
150+ but it already illustrates how graphs can grow rapidly in just a few iterations.
151+ Remember that the interactive visualization allows adjusting the zoom, which is
152+ handy when graphs become large.
153+ =#
0 commit comments