|
| 1 | +#= |
| 2 | +Algae growth |
| 3 | +
|
| 4 | +Alejandro Morales - May 2023 |
| 5 | +
|
| 6 | +In this first example, we learn how to create a `Graph` and update it |
| 7 | +dynamically with rewriting rules. |
| 8 | +
|
| 9 | +The model described here is based on the non-branching model of [algae |
| 10 | +growth](https://en.wikipedia.org/wiki/L-system#Example_1:_Algae) proposed by |
| 11 | +Lindermayer as one of the first L-systems. |
| 12 | +
|
| 13 | +First, we need to load the VPL metapackage, which will automatically load all |
| 14 | +the packages in the VPL ecosystem. |
| 15 | +=# |
| 16 | +using VPL |
| 17 | + |
| 18 | +#= |
| 19 | +The rewriting rules of the L-system are as follows: |
| 20 | +
|
| 21 | +**axiom**: A |
| 22 | +
|
| 23 | +**rule 1**: A $\rightarrow$ AB |
| 24 | +
|
| 25 | +**rule 2**: B $\rightarrow$ A |
| 26 | +
|
| 27 | +In VPL, this L-system would be implemented as a graph where the nodes can be of |
| 28 | +type `A` or `B` and inherit from the abstract type `Node`. It is advised to |
| 29 | +include type definitions in a module to avoid having to restart the Julia |
| 30 | +session whenever we want to redefine them. Because each module is an independent |
| 31 | +namespace, we need to import `Node` from the VPL package inside the module: |
| 32 | +=# |
| 33 | +module algae |
| 34 | +import VPL: Node |
| 35 | +struct A <: Node end |
| 36 | +struct B <: Node end |
| 37 | +end |
| 38 | +import .algae |
| 39 | + |
| 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 VPL as objects of type `Rule`. In VPL, 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 VPL, 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 VPL 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) |
| 105 | + |
| 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. |
| 138 | +end |
0 commit comments