Systems like cellular automata always update every element at every step in their evolution. But in string substitution systems (as well as in our hypergraph-based models), the presence of overlaps between possible updating events typically means that there is no single, consistent way to do this kind of parallel updating. Nevertheless, in studying our models in earlier sections, we often used “steps” of evolution in which we updated as many elements as we consistently could. And we can also apply this kind of “generation-based” updating to string substitution systems.
Consider the rule:
{"A" -> "AB", "B" -> "A"}
We can construct the multiway graph for this rule by considering how one state is produced from another by a single updating event, corresponding to a single application of one of the transformations in the rule:
ResourceFunction["MultiwaySystem"][{"A" -> "AB",
"B" -> "A"}, {"A"}, 6, "StatesGraph"]
But we can also consider producing a “generational multiway graph” in which we do as many non-overlapping updates as possible on any given string. For this particular rule, doing this is straightforward, since every A and every B in the string can be transformed separately.
But the result is now a radically simplified multiway graph, in which there is just a single path of evolution:
Graph[ResourceFunction["GenerationalMultiwaySystem"][{"A" -> "AB",
"B" -> "A"}, {"A"}, 5, "StatesGraph"],
EdgeStyle ->
Directive[{Hue[0.75, 0, 0.35], Dashing[None],
AbsoluteThickness[1]}]]
The “generational steps” here involve an increasing number of update events, as we can see from this rendering of the evolution:
ResourceFunction["SubstitutionSystemPlot"][{"A" -> {"A", "B"},
"B" -> {"A"}}, {"A"}, 6]
All the states obtained at generational steps do appear somewhere in the full multiway graph, but the full graph also contains many additional states—that, among other things, can be thought of as representing all possible intermediate stages of generational steps:
stripMetadata[expression_] :=
If[Head[expression] === Rule, Last[expression], expression]; Graph[
ResourceFunction["MultiwaySystem"][{"A" -> "AB", "B" -> "A"}, {"A"},
6, "StatesGraph"],
VertexShapeFunction -> {Alternatives @@
VertexList[
ResourceFunction["GenerationalMultiwaySystem"][{"A" -> "AB",
"B" -> "A"}, {"A"}, 5, "StatesGraph"]] -> (Text[
Framed[Style[stripMetadata[#2] , Hue[0, 1, 0.48]],
Background -> Directive[Opacity[.6], Hue[0, 0.45, 0.87]],
FrameMargins -> {{2, 2}, {0, 0}}, RoundingRadius -> 0,
FrameStyle ->
Directive[Opacity[0.5],
Hue[0, 0.52, 0.8200000000000001]]], #1, {0, 0}] &)}]
For a rule like
the generational steps
ResourceFunction[
"GenerationalMultiwaySystem"][{"A" ->
"AB"}, {"AA"}, 4, "StatesGraph"]
correspond to a particularly simple trajectory through the full multiway graph:
stripMetadata[expression_] :=
If[Head[expression] === Rule, Last[expression], expression]; Graph[
ResourceFunction["MultiwaySystem"][{"A" -> "AB"}, {"AA"}, 6,
"StatesGraph"],
VertexShapeFunction -> {Alternatives @@
VertexList[
ResourceFunction[
"GenerationalMultiwaySystem"][{"A" -> "AB"}, {"AA"}, 5,
"StatesGraph"]] -> (Text[
Framed[Style[stripMetadata[#2] , Hue[0, 1, 0.48]],
Background -> Directive[Opacity[.6], Hue[0, 0.45, 0.87]],
FrameMargins -> {{2, 2}, {0, 0}}, RoundingRadius -> 0,
FrameStyle ->
Directive[Opacity[0.5],
Hue[0, 0.52, 0.8200000000000001]]], #1, {0, 0}] &)},
VertexCoordinates -> (Thread[
VertexList[#] -> GraphEmbedding[#, Automatic, 2]] &[
ResourceFunction["MultiwaySystem"][{"A" -> "AB"}, {"AA"}, 8,
"StatesGraph"]])]
It is not always the case that the generational multiway graph involves just a single path. Consider the rule:
{"A" -> "AB", "A" -> "B"}
The ordinary multiway graph for this rule starting from AA is:
ResourceFunction["MultiwaySystem"][{"A" -> "AB",
"A" -> "B"}, {"AA"}, 4, "StatesGraph"]
And the generational multiway graph is now:
ResourceFunction["GenerationalMultiwaySystem"][{"A" -> "AB",
"A" -> "B"}, {"AA"}, 3, "StatesGraph"]
The generational multiway graph is always in a sense a compression of the full multiway graph. And one way to think of it is as being derived from the full multiway graph by combining sequences of edges when they correspond to updating events that do not overlap on the string.
But there is also another view of generational evolution. Consider a branchial graph from the full multiway graph above (this branchial graph is derived from the layered foliation shown):
HighlightGraph[
ResourceFunction["MultiwaySystem"][{"A" -> "AB", "A" -> "B"}, {"AA"},
2, "BranchialGraph"],
Style[UndirectedEdge @@@ {{"ABBA", "ABAB"}, {"ABBA", "ABB"}, {"BBA",
"ABAB"}, {"BBA", "ABB"}, {"ABAB", "ABB"}, {"ABAB",
"AABB"}, {"ABB", "BAB"}}, Red, Thickness[.007]]]
The branch pairs in this branchial graph (shown as adjacent nodes) can be thought of as being of two kinds. The first are produced by applying different rules to a single part of a string (e.g. ABA {ABBA, BBA}). And the second (highlighted in the graph above) by applying rules to different parts of a string (e.g. ABA {ABBA, ABAB}).
In the full multiway graph, no distinction is made between these two kinds of branch pairs, and the graph includes both of them. But in a generational multiway system, strings in the second kind of branch pairs can be combined.
And indeed this provides another way to construct a generational multiway system: look at branchial graphs and take pairs of strings corresponding to “spatially” disjoint updating events, and then knit these together to form generational steps. And if there is only one way to do this for each branchial graph, one will get a single path of generational evolution. But if there are multiple ways, then the generational multiway graph will be more complicated.
For the rule
{"A" -> "AB", "B" -> "A"}
that we discussed above, the sequence of branchial graphs is
MapThread[
HighlightGraph[#1,
Style[UndirectedEdge @@@ Partition[#2, 2, 1], Red,
Thickness[.03]]] &, {Table[
ResourceFunction["MultiwaySystem"][{"A" -> "AB",
"B" -> "A"}, {"A"}, t, "BranchialGraph"], {t, 2, 5}], {{"ABB",
"AA"}, {"ABBB", "AAB"}, {"ABBBB", "AABB", "ABAB",
"ABBA"}, {"ABBBBB", "AABBB", "ABABB", "ABBAB", "ABBBA"}}}]
and it is readily possible to assemble “string fragments” (such as those highlighted) to produce the states at successive generational steps:
ResourceFunction["GenerationalMultiwaySystem"][{"A" -> "AB"}, {"AA"},
3]
The branchial graphs are determined by the foliation of the multiway graph that one uses. But given a foliation, one can then assemble strings corresponding to generational steps using the procedure above.
There is always an alternative, however: instead of combining strings from a branchial graph to produce the state for a generational step, one can always in a sense just wait, and eventually the full multiway system will have done the necessary sequence of updates to produce the complete state for the generational step.
Whenever there are no possible overlaps in the application of rules, the generational multiway graph must always yield a single path of history. But there is also another feature of such rules: they are guaranteed to be causal invariant. There are, however, plenty of rules that are causal invariant even though they allow overlaps—in a sense because their right-hand sides also appropriately agree. And for such rules, the generational multiway graph may have multiple paths of history.
A simple example is the rule:
{"A" -> "AB", "A" -> "BA"}
This rule is causal invariant, and starting from A yields the full multiway graph:
ResourceFunction["MultiwaySystem"][{"A" -> "AB",
"A" -> "BA"}, {"A"}, 4, "StatesGraph"]
Its generational multiway graph is actually identical in this case—because all the rule ever does is to apply one transformation or the other to the single A that appears:
ResourceFunction["GenerationalMultiwaySystem"][{"A" -> "AB",
"A" -> "BA"}, {"A"}, 4, "StatesGraph"]
Starting from AA, however, the rule yields the full multiway graph
ResourceFunction["MultiwaySystem"][{"A" -> "AB",
"A" -> "BA"}, {"AA"}, 3, "StatesGraph"]
and the generational multiway graph:
ResourceFunction["GenerationalMultiwaySystem"][{"A" -> "AB",
"A" -> "BA"}, {"AA"}, 2, "StatesGraph"]
In an alternative rendering, these graphs after a few more steps become, respectively:
{ResourceFunction["MultiwaySystem"][{"A" -> "AB",
"A" -> "BA"}, {"AA"}, 6, "StatesGraphStructure"],
ResourceFunction["GenerationalMultiwaySystem"][{"A" -> "AB",
"A" -> "BA"}, {"AA"}, 5, "StatesGraphStructure"]}
Note that in both cases, the number of states reached after t steps grows like t2 (in the first case it is ; in the second case exactly t2).
In the case of a rule like
{"A" -> "AB", "A" -> "BB"}
the presence of many potential overlaps in where updates can be applied makes many of the possible states in the full multiway graph also appear in the generational multiway graph (in the limit about 64% of all possible states are generational results):
LayeredGraphPlot[
HighlightGraph[
ResourceFunction["MultiwaySystem"][{"A" -> "AB",
"A" -> "BB"}, {"AA"}, 8, "StatesGraphStructure"],
VertexList[
ResourceFunction["GenerationalMultiwaySystem"][{"A" -> "AB",
"A" -> "BB"}, {"AA"}, 8, "StatesGraph"]]], VertexSize -> .25,
VertexStyle -> Opacity[.6], AspectRatio -> 1/2]
Generational multiway graphs share many features with full multiway graphs. For example, generational multiway graphs can also show causal invariance—and indeed unless strings grow too fast, any deviation from causal invariance must also appear in the generational multiway graph.
The basic construction of ordinary multiway graphs ensures that the number of states Mt after t steps can grow at most exponentially with t. In a generational multiway graph, there can be faster growth.
Consider for example the rule:
{"A" -> "AA", "A" -> "B"}
Its full multiway graph grows in a Fibonacci sequence ≈ϕt:
ResourceFunction["MultiwaySystem"][{"A" -> "AA",
"A" -> "B"}, {"A"}, 5, "StatesGraph"]
But its generational multiway graph grows much faster. After 3 generational steps it has the form
Graph[ResourceFunction["GenerationalMultiwaySystem"][{"A" -> "AA",
"A" -> "B"}, {"A"}, 3, "StatesGraphStructure"], AspectRatio -> 1/2]
while after 4 steps (in a different rendering) it is:
Graph[ResourceFunction["GenerationalMultiwaySystem"][{"A" -> "AA",
"A" -> "B"}, {"A"}, 4, "StatesGraphStructure"], AspectRatio -> 1/2,
VertexSize -> 0.5]
The number of states reached in successive steps is:
{1, 2, 5, 24, 455, 128702}
Although there is a distribution of lengths for the strings, say, at steps 4 and 5
stage5lengths = CompressedData["
1:eJztnGtu5DYQhB3ksX7b8/D6be9eKUfYC+SEuVZ+BwRBaMUZqsfuYGvjryAI
EqenVSxWd5OSxv767a8/v/1ydHT09z+/HP362+/Hn+r26Y+z09OT05OT4/5Y
9pPj40/nZ2U7O63b6cm+9nRej+0aV5dXl5cXZbs4r2cX52Uv2/lZbbfzeix7
veLYd8mz+PXfndv7dju2vq9Xq+vV9fVV2ct2dVnPri7LXnu1+0k9m46tF+Nv
Tux2P2nePZd53/qe9f2KerNr79s9ftY/skfabdbrVRmD/tjGZXRs/a7t5e80
+6T+KBIuLyK80XGu1/J3mn2Kn1EsXZwfOi79eOwfhUNZH8pyP7eIy3Lfx7mQ
zRV1rm03ddus27ZezT+p7em8HqcYaJa2tc/m7el8Pt67WMtIh1916Tpjv/67
c3sULdnRmL71fQX5/pPaHuXTLtYy0uFXXbrO2C+XW2+Pw/9DdLwlEn5+fVTj
qOGb7XcOXz1X3X4u2+ebut1s23k7u9mW/Wa73bTP61ba87PtpuxlPKcr7r/K
2K//7tzet9uxaXh4D9vnrRq18/3XXLrK2G+5r327j8OlERz15709VTLMqJ3D
z/pH9qiyfKRoPbyHh68Nl/q7NEvldRmP4Kg/7+2pkqEu8rP+2TlbOT+50moq
raoqOr8V+U2Pd7b+dP5q/Kx/ZI/ueR/u7+/u7+5u27Hsd7e3n/tj2UutqO19
ltpq5/XYqsvoav2xfX98paX1yIjDvO+76H0vl3v1fV/m+Fn/yB7V6h8zyqOr
Lff6retH9Vhm/bPz6vvy7L2K5iIhG69UjVXjrrqnU4+3eq3r+vyRcvdHjPLo
asu93mdZur9Wj6U6L9+XZ+9V1LVXobGfY/7Y8c76R/bovtn3guz5mc5fja+u
P9afrT+dvxpfPf87/9nxT+evxlfnv/Vn60/nr8bP+kf2vt2/5358KNvDfd3u
76ZWOa/H6d13bdettqfz+XOE6Dpjv/67c3v0vOItfWyfT28Qx28To+uM/XLP
V5TjqOGb7XcOX/087KPF71v62D6f3jKM3zhE1xn75fVRjaOGL/v5sOsvr/46
v0n5TY93tv50/mr8rH9kj+7vvd72evvn4kuvRx8rft/Sx/Z53X7W36LrxlHD
1/Oj6y+r/jq/SflNj3e2/nT+avysf2Tv2/37Yj/fpj3v83qONL853tn60/mr
8dXrC9d7Wrz7/p2V3/R4Z+tP56/Gz/pH9vj+3et7cv2j81fjq+uP9WfrT+ev
xlfP/85/dvzT+avx1flv/dn60/mr8bP+kb1v93/P/fL88vz8VLanx3r29Fj2
p8fHh9pq5/VY9vLMYPe7c3vfbsf2vGHp2rU18lu+bt/un2/k+53Dz/pH9uh5
jmrcl6699BuSPF/teGf9s8/nHO9s/en81fjq5+uu97R414y76neg6vHW5zc9
3tn60/mr8bP+kT16PuP1Pbv+0fmr8dX1x/qz9afzV+Or53/nPzv+6fzV+Or8
t/5s/en81fhZ/8jet/vfC/j5Pm294+f7pPnN8c7Wn85fja9eX7je0+JdM+5L
164t57fv363/f60/nb8aP+sf2eP7d6/vyfWPzl+Nr64/1p+tP52/Gl89/zv/
2fFP56/GV+e/9WfrT+evxs/6R/a+3f+9vn+/z37+QeevxlfPv9afrT+dvxpf
vf5y/rPjn85fja/Of+vP1p/OX42f9Y/s0fMBz//s+kfnr8ZX1x/rz9afzl+N
r57/nf/s+KfzV+Or89/6s/Wn81fjZ/0je9/u3//7/p+9/qHzV+Or66/1Z+tP
56/GV68/nP/s+KfzV+Or89/6s/Wn81fjZ/0je3T/7/mfXf/o/NX46vpj/dn6
0/mr8dXzv/OfHf90/mp8df5bf7b+dP5q/Kx/ZO/b7Vj29Wp1/fXLl9cvr68v
ZX99eXlux7KX/w3Qt9ux/d+AyN63+/83kPWP7NH/N1DjZ/2z/7+Bzl+Nr/7/
G9afrT+dvxpf/fs75z87/un81fjq/Lf+bP3p/NX4Wf/IHj3f8/zPrn90/mp8
df2x/mz96fzV+Or53/nPjn86fzW+Ov+tP1t/On81ftY/svft/vcOvv9nr3/o
/NX46vpr/dn60/mr8dXrD+c/O/7p/NX46vy3/mz96fzV+Fn/yB7d/3v+Z9c/
On81vrr+WH+2/nT+anz1/O/8Z8c/nb8aX53/1p+tP52/Gj/rH9n7dv//Bvz+
n/38g85fja+ef60/W386fzW+ev3l/GfHP52/Gl+d/9afrT+dvxo/6x/Zo+cD
nv/Z9Y/OX42vrj/Wn60/nb8aXz3/O//Z8U/nr8ZX57/1Z+tP56/Gz/pH9r7d
v//3/T97/UPnr8ZX11/rz9afzl+Nr15/OP/Z8U/nr8ZX57/1Z+tP56/Gz/pH
9uj+3/M/u/7R+avx1fXH+rP1p/NX46vnf+c/O/7p/NX46vy3/mz96fzV+Fn/
yN6327Hs281m7b//Z//+gc5fja9ef1t/tv50/mp89frb+c+Ofzp/Nb46/60/
W386fzV+1j+yR88XPP+z6x+dvxpfXX+sP1t/On81vnr+d/6z45/OX42vzn/r
z9afzl+Nn/WP7H27/z2A7//Z6x86fzW+uv5af7b+dP5qfPX6w/nPjn86fzW+
Ov+tP1t/On81ftY/skf3/57/2fWPzl+Nr64/1p+tP52/Gl89/zv/2fFP56/G
V+e/9WfrT+evxs/6R/a+3f/9v9//s59/0Pmr8dXzr/Vn60/nr8ZXr7+c/+z4
p/NX46vz3/qz9afzV+Nn/SN79HzA8z+7/tH5q/HV9cf6s/Wn81fjq+d/5z87
/un81fjq/Lf+bP3p/NX4Wf/I3rf79/++/2evf+j81fjq+mv92frT+avx1esP
5z87/un81fjq/Lf+bP3p/NX4Wf/IHt3/e/5n1z86fzW+uv5Yf7b+dP5qfPX8
7/xnxz+dvxpfnf/Wn60/nb8aP+sf2ft2O5Z9s16v/gXyDEUf
"];
Histogram[#, Frame -> True,
FrameTicks -> {Automatic, None}] & /@ {StringLength[
Last[ResourceFunction["GenerationalMultiwaySystem"][{"A" -> "AA",
"A" -> "B"}, {"A"}, 4]]], stage5lengths}
the fact that the maximum string length at generational step t is 2t–1—combined with the lack of causal invariance for this rule—allows for double exponential growth in the number of possible states with generational steps. In fact, with this particular rule, by step t almost all sequences of up to 2t–1 Bs and AAs have appeared (the missing fractions on steps 3, 4, 5 are 0.13, 0.078, 0.017) so at step t the total number of states approaches
2^2^(t - 1) // TraditionalForm