Multi-way tag systems with symbolic rewriting, voxel transitions, and non-deterministic state graphs

In this post, instead of symbolic strings, @Max Niederman's approach builds structure iteratively. In this framework, ideas from Emil Post's deterministic tag systems are applicable to non-deterministic scenarios. Whereas, in the past, we examined the growth rates of these systems and then disregarded the iterative expansion of multi-way systems due to their heavy computational demands, it turns out right now that we can look at the state evolution of multi-way systems. Moreover, in the cinematic universe of deterministic tag systems, the initial state carries information inefficiently--each bit is "charged" with thousands of electrons. Recognizing this inefficiency prompts the exploration of alternative informational carriers, such as photons, hinting at the promising potential of photonic computing. ClearAll[$LEGOPrototile, discreteRotate, MultiwayTransforms, GrowBrick, IntersectingQ, IterateMultiwayLEGO, DisplayMultiwayLEGO]; Unprotect[IntersectingQ]; IntersectingQ[vox1_, vox2_] := Not@FreeQ[Flatten[Outer[Equal, vox1, vox2, 1], 1], True]; Protect[IntersectingQ]; $LEGOPrototile = <|"Voxels" -> {{0, 0, 0}}, "Connectors" -> {{{0, 0, 0}, {1, 0, 0}}, {{0, 0, 0}, {0, 1, 0}}, {{0, 0, 0}, {0, 0, 1}}}|>; discreteRotate[dir_] := RotateLeft[dir, 1]; MultiwayTransforms[match_, move_] := {match[[2]], discreteRotate[move[[2]]]}; GrowBrick[transformation_] := Module[{shift = transformation[[1]]}, <| "Voxels" -> (# + shift) & /@ $LEGOPrototile["Voxels"], "Connectors" -> Map[{#[[1]] + shift, discreteRotate[#[[2]]]} &, $LEGOPrototile[ "Connectors"]]|>]; IterateMultiwayLEGO[data_] := Module[{max = Max[Keys[data["Structures"]]] + 1, funs, candidates, newTile}, funs = MapApply[MultiwayTransforms, Tuples[{data["Connectors"], $LEGOPrototile["Connectors"]}]]; candidates = Map[GrowBrick, funs]; candidates = Select[candidates, Not@IntersectingQ[#, data["Voxels"]] &]; If[candidates === {} || candidates === Null, data, newTile = First[candidates]; <|"Voxels" -> Union[data["Voxels"], newTile["Voxels"]], "Structures" -> Append[data["Structures"], max -> newTile], "Connectors" -> Union[data["Connectors"], newTile["Connectors"]]|>]]; DisplayMultiwayLEGO[data_] := Graphics3D[{EdgeForm[Directive[Thin, Gray]], MapIndexed[{ColorData["Rainbow"][#2[[1]]/ Length[data["Structures"]]], Cuboid[# - {0.45, 0.45, 0.45}, # + {0.45, 0.45, 0.45}]} &, data["Voxels"]], {Red, Sphere[#, 0.25] & /@ (First /@ data["Connectors"])}, {Green, Arrow[Tube[#, 0.07]] & /@ data["Connectors"]}}, Boxed -> False, Lighting -> "Neutral", ImageSize -> 200]; MultiwayLEGOExplorerStatic[iterations_ : 10] := Module[{data, genList}, data = <|"Voxels" -> $LEGOPrototile["Voxels"], "Structures" -> <|1 -> $LEGOPrototile|>, "Connectors" -> $LEGOPrototile["Connectors"]|>; genList = NestList[IterateMultiwayLEGO, data, iterations]; Row[MapIndexed[ Row[{Style["Generation " <> ToString[#2[[1]] - 1], Bold, 14], DisplayMultiwayLEGO[#]}] &, genList], Alignment -> Center]]; MultiwayLEGOExplorerStatic[2] This iterative, LEGO-inspired construction is analogous to how symbolic string instantiation works in tag systems. Just as a tag system evolves by applying production rules to strings, here the structure evolves through discrete rotations and translations that sequentially match those operations. Each iteration examines the potential for non-deterministic growth by selecting valid "tiles" (or LEGO pieces) that can connect without overlapping existing parts. Niederman's work emphasizes the complexity and cyclic nature of state evolution from simple iterative rules, and this LEGO analogy cyclically reveals a similar progression--albeit in a geometric context. Although the focus isn't yet strictly on building physical LEGO blocks, the analogy helps to illustrate the principles behind a binary string multi-way system format and tag-like string evolution. So let's look at some tag-like string evolution instead. ruledRule1044 = {{0, 0} -> {0, 1}, {0, 0} -> {1, 1}, {0, 1} -> {0, 1}, {0, 1} -> {1, 1}, {1, 0} -> {1, 0}, {1, 0} -> {1, 1}, {1, 1} -> {1, 0}, {1, 1} -> {1, 1}}; colorAssociation = <|1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown, 5 -> Orange, 6 -> Red, 7 -> Magenta, 8 -> Purple|>; GenerateTransitions[state_] := Flatten[Table[ Module[{currentPair, matches}, currentPair = state[[i ;; i + 1]]; matches = Flatten[Position[ruledRule1044, currentPair -> _]]; Map[{state, ReplacePart[state, Thread[Range[i, i + 1] -> ruledRule1044[[#, 2]]]], #} &, matches]], {i, Length[state] - 1}], 1]; initialState = {0, 1, 1}; maxSteps = 3; edges = {}; states = {initialState}; currentStates = {initialState}; Do[newTransitions = Flatten[GenerateTransitions /@ currentStates, 1]; newStates = DeleteCases[DeleteDuplicates[newTransitions[[All, 2]]], Alternatives @@ states]; edges = Join[edges, newTransitions]; states = Union[states, newStates]; currentStates = newStates; If[Length[currentStates] == 0, Break[]], {maxSteps}]; edgeStyles = # -> colorAssociation[#2] & @@@ Transpose[{DirectedEdge @@@ edges[[All, ;; 2]], edges[[All, 3]]}]; formatState[s_List] := StringJoin[ToString /@ s]; stateLabels = Thread[states -> Map[Style[formatState[#], 12, Bold, Black] &, states]]; Graph[DirectedEdge @@@ edges[[All, ;; 2]], VertexLabels -> stateLabels, EdgeLabels -> Placed[Automatic, "Center"], EdgeStyle -> edgeStyles, VertexShapeFunction -> "Square", VertexSize -> Large, VertexStyle -> Directive[White, EdgeForm[Black]], GraphLayout -> {"LayeredDigraphEmbedding", "Orientation" -> Top}, PlotTheme -> "Detailed", PerformanceGoal -> "Quality", ImageSize -> 300] The concept of multi-way tag evolution as it stands, as strings of binary digits that evolve readily according to non-deterministic rules, means that we don't have to look at multi-way tag evolution as it exists right now. The computational overhead, makes it possible for us to transform binary states. So this is all so far the multi-way nature of transitions, the "rabbit system" and multi-way extensions that color-code edges based on applied rules (e.g. the "twin horn system"), where color-coding emphasizes particular transitions. With a nifty bit of data wrangling we can re-do the non-deterministic rules and look at the evolution of binary digit strings from the beginning. colorAssociation1D = <|0 -> Black, 1 -> Blue, 2 -> Yellow|>; updateRule[{a_, b_, c_}] := Switch[{a, b, c}, {2, 2, 2}, 0, {_, 1, _}, Mod[a + c, 3], _, Mod[a + b + c, 3]]; gridWidth = 101; initialState = ConstantArray[0, gridWidth]; initialState[[Ceiling[gridWidth/2]]] = 1; numSteps = 100; getCircularTriplet[list_, i_] := Module[{n = Length[list]}, {list[[Mod[i - 2, n, 1]]], list[[i]], list[[Mod[i, n, 1]]]}]; history = NestList[ Table[updateRule[getCircularTriplet[#, i]], {i, Length[#]}] &, initialState, numSteps]; Panel[DynamicModule[{frame = 1}, Column[{Row[{Button["⏪", If[frame > 1, frame--], ImageSize -> Small], Button["⏩", If[frame < numSteps, frame++], ImageSize -> Small], Button["\[FilledRightTriangle]", While[frame < numSteps, frame++; Pause[0.05]], ImageSize -> Small]}], Dynamic[ArrayPlot[history[[1 ;; frame]], ColorRules -> Normal[colorAssociation1D], PixelConstrained -> 2, Frame -> False, ImageSize -> 800]]}, Alignment -> Center]], ImageSize -> 250] However, the binary state transitions alone don't fully explain the meaning of complex system dynamics. They don't fully explain the story and that is why we need to recall the evolution of say, a simple cellular automaton state through iterative application of rules wherein each iteration's outcome depends solely on local state patterns--analogous to local string manipulations in tag systems. The generation history of states, introduces temporal evolution which, while it might not tell us what we're plotting--the growth and evolution histories of tag system states--while it doesn't tell us the "now" it does tell us the development of complexity in the context of cyclic pattern formation, which for now represents a 1-dimensional cellular automaton with circular conditions. ruledRule1044 = {"00" -> "01", "00" -> "11", "01" -> "01", "01" -> "11", "10" -> "10", "10" -> "11", "11" -> "10", "11" -> "11"}; rulesWithIndices = MapIndexed[#1 -> #2[[1]] &, ruledRule1044]; graph = ResourceFunction["MultiwaySystem"][ruledRule1044, "00", 4, "EvolutionCausalGraph", GraphLayout -> "LayeredDigraphEmbedding"]; edgeTags = EdgeTags[graph]; edgeStyles = If[MatchQ[edgeTags, _Association] && Length[Keys[edgeTags]] === Length[EdgeList[graph]], Thread[EdgeList[ graph] -> (colorAssociation /@ (Lookup[rulesWithIndices, #, 1] & /@ (Values[edgeTags]))), Directive[Black, Thickness[0.004]] ];] styledGraph = Graph[graph, EdgeStyle -> edgeStyles, VertexStyle -> Black, VertexSize -> 0.8, VertexLabels -> Placed["Name", Center], EdgeLabels -> Placed["EdgeTag", 0.5], VertexLabelStyle -> Directive[White, Bold, 12], EdgeLabelStyle -> Directive[Black, Italic, 10], ImageSize -> 300, Prolog -> {Transparent, Rectangle[Scaled[{0, 0}], Scaled[{1, 1}]]}] Wolfram's built-in multi-way system framework mirrors eight degrees of freedom, that is the exact functionality that Niederman employs in MultiwaySystem; the rule tracking of rule-labeled edges (causal connections), is how we Ruliologically clarify how multi-way transformations branch out non-deterministically, leading to complex system states. So when you look at the graph styling with vertex labels and edge-tags, you'll emphasize the colorized role of specific transformations or production rules. colorAssociation = <|0 -> Blue, 1 -> Cyan, 2 -> Green, 3 -> Brown, 4 -> Orange, 5 -> Red, 6 -> Magenta, 7 -> Purple|>; Stabilize1D[s_, zc_] := Module[{i, newList}, i = FirstPosition[s, _?(# >= zc &)]; If[MissingQ[i], Return[s]]; i = i[[1]]; newList = ReplacePart[s, i -> s[[i]] - 2]; If[i > 1, newList = ReplacePart[newList, i - 1 -> newList[[i - 1]] + 1]]; If[i < Length[s], newList = ReplacePart[newList, i + 1 -> newList[[i + 1]] + 1]]; Stabilize1D[newList, zc]]; PossibilityGenerator1D[state_, zc_] := DeleteDuplicates@ Table[Module[{newState = state}, newState[[pos]] += 1; Stabilize1D[newState, zc]], {pos, Length@state}]; size = 3; Manipulate[ Module[{graph, initialState = PadRight[ConstantArray[0, size], 3]}, graph = ResourceFunction["NestGraphTagged"][ PossibilityGenerator1D[#, zc] &, {initialState}, steps, VertexShapeFunction -> (Inset[ ArrayPlot[{#2}, ColorRules -> colorAssociation, ImageSize -> 30, Mesh -> True, PlotLabel -> #2], #1, Center] &), VertexSize -> 0.8, EdgeStyle -> Directive[Thick, Blue], GraphLayout -> "LayeredDigraphEmbedding", ImageSize -> 600]; HighlightGraph[graph, Style[PathGraph[FindShortestPath[graph, initialState, #]], Directive[Thick, Red]] & /@ VertexList[graph]]], {{zc, 3, "Toppling Threshold"}, 2, 5, 1}, {{steps, 3, "Evolution Steps"}, 1, 5, 1}, ControlPlacement -> Top] Multi-way tag systems can generate multiple states from single initial conditions. The "possibility generator" is a bottomless pit; it creates multiple outcomes for each state, via the use of NestGraphTagged to finitely enumerate multiple state evolutions, methods of finite reachable state spaces in tag systems. So if you really needed to enumerate the reachable state spaces, there you have the "toppling threshold" parameter which influences our investigation of how small parameter changes can drastically alter the kind of growth rates and open structural complexity of tag system state graphs. That's why we start out with the iterative evolution and visualization of complex states from simple local rules. We could create branching multi-way state graphs many times over and we will usually get non-deterministic expansions, which I suppose is an echo of the geometric and abstract symbolism "as analogs" to tag system operations, illustrating computational universality. That's why we need to make this clear. ruledRule1044 = {"00" -> "01", "00" -> "11", "01" -> "01", "01" -> "11", "10" -> "10", "10" -> "11", "11" -> "10", "11" -> "11"}; colorAssociation = <|1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown, 5 -> Orange, 6 -> Red, 7 -> Magenta, 8 -> Purple|>; depth = 4; Module[{mwGraph, styledEdges, myGraph, vertexCount, edgeCount, meanVertexDegree, meanBetweenness, infoPanel}, mwGraph = ResourceFunction["MultiwaySystem"][ruledRule1044, "00", depth, "StatesGraph", EdgeLabels -> "EdgeTag", GraphLayout -> "LayeredDigraphEmbedding"]; styledEdges = MapIndexed[Tooltip[#1, "Edge " <> ToString[First[#2]]] &, MapIndexed[ Style[#1, colorAssociation[ Mod[First[#2], Length[colorAssociation], 1]]] &, EdgeList[mwGraph]]]; myGraph = Graph[styledEdges, VertexLabels -> Placed["Name", Center], VertexStyle -> White, VertexSize -> 0.3, VertexLabelStyle -> Directive[Black, Bold, 12], EdgeStyle -> Thickness[0.008], GraphLayout -> "LayeredDigraphEmbedding", PlotTheme -> "Detailed", ImageSize -> 100]; vertexCount = VertexCount[myGraph]; edgeCount = EdgeCount[myGraph]; meanVertexDegree = N[Mean[VertexDegree[myGraph]]]; meanBetweenness = N[Mean[BetweennessCentrality[myGraph]]]; infoPanel = Grid[{{"Vertex Count", vertexCount}, {"Edge Count", edgeCount}, {"Avg. Degree", NumberForm[meanVertexDegree, 3]}, {"Avg. Betweenness", NumberForm[meanBetweenness, 3]}}, Frame -> All, Background -> {None, {{LightBlue, White, LightBlue, White}}}]; Column[{Labeled[myGraph, "Multiway State Transition Graph (depth = " <> ToString[depth] <> ")", Top], infoPanel}, Spacings -> 2]] Multiway system string-based transitions are a meta-demonstration of the multi-way tag systems, which let us non-deterministically transform binary strings, that is until all possible state evolutions are revealed from an initial binary state. And then, we can differentiate rules via color-coded state transitions that format the vertex & edge count(s), the average vertex degree as well as the average betweenness centrality--metrics that are the last spiritual step to "state growth rate" and "path growth rate," quantitatively verifying the elementary cellular automaton in the context of Rule 30. rule30 = Association[{1, 1, 1} -> 0, {1, 1, 0} -> 0, {1, 0, 1} -> 0, {1, 0, 0} -> 1, {0, 1, 1} -> 1, {0, 1, 0} -> 1, {0, 0, 1} -> 1, {0, 0, 0} -> 0]; triplets = Tuples[{0, 1}, 3]; stateAssociation = AssociationThread[triplets -> Range[8]]; edges = Table[ Module[{currentTriplet, nextBit, nextTriplet, currentState, nextState}, currentTriplet = triplets[[i]]; nextBit = rule30[currentTriplet]; nextTriplet = {currentTriplet[[2]], currentTriplet[[3]], nextBit}; currentState = stateAssociation[currentTriplet]; nextState = stateAssociation[nextTriplet]; Labeled[currentState -> nextState, nextBit]], {i, Length[triplets]}]; colorAssociation = <|0 -> Gray, 1 -> Blue|>; edgeStyles = edges /. Labeled[edge_, tag_] :> Style[edge, colorAssociation[tag]]; vertexLabels = Thread[Range[8] -> (StringJoin /@ (ToString /@ # & /@ triplets))]; stateTransitionGraph = EdgeTaggedGraph[edgeStyles, VertexLabels -> vertexLabels, VertexLabelStyle -> 12, VertexSize -> 0.3, VertexStyle -> Black, EdgeLabels -> "EdgeTag", EdgeLabelStyle -> Directive[Black, Bold, 12], GraphLayout -> "SpringEmbedding", ImageSize -> Medium]; initialRow = {0, 0, 0, 1, 0, 0, 0}; steps = 10; caEvolution = CellularAutomaton[30, initialRow, steps]; caPlot = ArrayPlot[caEvolution, Mesh -> All, ColorRules -> {0 -> White, 1 -> Black}, Frame -> False, ImageSize -> Small]; Row[{stateTransitionGraph, Spacer[20], caPlot}] There are discrete-rule-based systems that "cut off" the deterministic cellular automaton Rule 30 alongside a "discontinuous" state transition graph where we can inspect how each neighborhood state mapped deterministically to another--relational navigation of deterministic tag systems to their more manifold non-deterministic multi-way dual visualizations (graph plus cellular automaton evolution) pairs graphical state evolution with the "previous forward" visual patterns to get the latest complexity and structural nuances via the generalized, multiway cellular automaton explorer. extractRuleBits[r_Integer] := IntegerDigits[r, 2, 8] standardNeighborhoods = {{1, 1, 1}, {1, 1, 0}, {1, 0, 1}, {1, 0, 0}, {0, 1, 1}, {0, 1, 0}, {0, 0, 1}, {0, 0, 0}}; states = Tuples[{0, 1}, 3]; stateAssociation = AssociationThread[states -> Range[Length[states]]]; colorAssociation = AssociationThread[ Range[Length[states]] -> (Hue /@ Rescale[Range[Length[states]]])]; buildEdges[ruleAssoc_] := Module[{edgesList = {}}, Do[Module[{currentState, nextState}, currentState = states[[i]]; nextState = RotateLeft[{ruleAssoc[RotateRight[currentState, 1]], ruleAssoc[currentState], ruleAssoc[RotateLeft[currentState, 1]]}]; AppendTo[ edgesList, {stateAssociation[currentState] -> stateAssociation[nextState], stateAssociation[currentState] -> stateAssociation[RotateRight[nextState, 1]], stateAssociation[currentState] -> stateAssociation[RotateLeft[nextState, 1]]}];], {i, Length[states]}]; DeleteDuplicates[Flatten[edgesList]]]; Manipulate[ Module[{bits, ruleAssoc, edges, graph}, bits = extractRuleBits[r]; ruleAssoc = AssociationThread[standardNeighborhoods -> bits]; edges = buildEdges[ruleAssoc]; graph = Graph[edges, VertexStyle -> Normal[colorAssociation], VertexShapeFunction -> "Square", VertexSize -> 0.2, EdgeStyle -> Directive[Opacity[0.5], GrayLevel[0.3]], GraphLayout -> "SpringElectricalEmbedding", ImageSize -> 800, PlotLabel -> Style["Rule " <> ToString[r], 20, Bold]]; Show[graph, ImageSize -> 800]], {{r, 30, "Rule Number"}, 0, 255, 1, Appearance -> "Labeled"}, TrackedSymbols :> {r}] This generalized explorer allows users to select the, elementary cellular automaton rule (0-255). The multi-way behaviors interactively explore "race conditions" and Niederman's enumeration of state evolutions, allowing systematic exploration across parameter spaces, the point being that a slight modification in rules just drastically influences the structural complexity--as far as we're supposed to know, the enumeration and classification of dynamic and growth behaviors is all happening in the eyes of the user, in two dimensions. rule2D = <|{0, {0, 0, 0, 0}} -> 0, {0, {0, 0, 0, 1}} -> 1, {0, {0, 0, 1, 0}} -> 1, {0, {0, 0, 1, 1}} -> 0, {1, {1, 1, 1, 1}} -> 0|>; get4Neighbors[state_, i_, j_] := Module[{rows, cols}, {rows, cols} = Dimensions[state]; {If[i > 1, state[[i - 1, j]], 0], If[i < rows, state[[i + 1, j]], 0], If[j > 1, state[[i, j - 1]], 0], If[j < cols, state[[i, j + 1]], 0]}]; apply2DRule[state_, ruleAssoc_] := Module[{rows, cols, newState}, {rows, cols} = Dimensions[state]; newState = state; Do[Module[{c = state[[i, j]], n, key}, n = get4Neighbors[state, i, j]; key = {c, n}; If[KeyExistsQ[ruleAssoc, key], newState[[i, j]] = ruleAssoc[key], newState[[i, j]] = state[[i, j]]]], {i, rows}, {j, cols}]; newState]; initialState = RandomInteger[1, {10, 10}]; steps = 5; evolutionList = NestList[apply2DRule[#, rule2D] &, initialState, steps]; ArrayPlot[#, PlotRange -> {0, 1}, Mesh -> True, Frame -> False, ImageSize -> 200] & /@ evolutionList rule2Dmulti = <|{0, {0, 0, 0, 0}} -> {0, 1}, {0, {0, 0, 0, 1}} -> {1}, {1, {1, 1, 1, 1}} -> {0, 1}|>; apply2DMultirule[state_, ruleAssoc_] := Module[{rows, cols}, {rows, cols} = Dimensions[state]; FoldList[ Function[{acc, coords}, Flatten[acc /. {arr_ :> Table[ReplacePart[arr, coords -> possibility], {possibility, Lookup[ruleAssoc, {arr[[coords[[1]], coords[[2]]]], get4Neighbors[arr, coords[[1]], coords[[2]]]}, {arr[[ coords[[1]], coords[[2]]]]}]}]}, 1]], {state}, Flatten[Table[{i, j}, {i, rows}, {j, cols}], 1]]]; multiwayStep[state_] := apply2DMultirule[state, rule2Dmulti]; The most impressive aspect of the original conceptual framework is this generalization into two dimensions--the local neighborhood rules get "saved" for later much like here and now, the neighborhood-based transformations in tag systems we present via rule2Dmulti, enabling each state to evolve into multiple outcomes (non-deterministically), creating complex branching behaviors that transition between multiple ways from simple local rules, from tag systems to higher-dimensional lattice structures and back. The voxels show in black and white that 3-dimensional structure growth keeps on updating and when we return we will find, the headlamp prototile multiway system is based on the same voxel structure that we explored laterally through our lattice structures. $HeadlampPrototile = Association["Voxels" -> {{0, 0, 0}}, "Ins" -> <|1 -> {{0, 0, 0}, {1, 0, 0}}|>, "Outs" -> <|1 -> {{0, 0, 0}, {-1, 0, 0}}|>]; FourTransforms[match_, offset_] := Transpose[{Table[ TranslationTransform[match + offset + RandomInteger[{-1, 1}, 3]], 4], Table[ RotationTransform[RandomReal[{-Pi/4, Pi/4}], RandomChoice[{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}], match + offset], 4]}]; TransformOne[ptfun_, vecfun_] := Module[{res = <||>}, res["Voxels"] = Map[ptfun, $HeadlampPrototile["Voxels"]]; res["Ins"] = MapAt[vecfun, MapAt[ptfun, $HeadlampPrototile["Ins"], {All, 1}], {All, 2}]; res["Outs"] = MapAt[vecfun, MapAt[ptfun, $HeadlampPrototile["Outs"], {All, 1}], {All, 2}]; res]; MultiwayVoxelEvolve[data_, gens_] := Module[{newData = data}, Do[newData = Catenate[ ParallelMap[ With[{offset = RandomChoice[#["Outs"][[1]]], match = #["Voxels"][[1]]}, FourTransforms[match, offset] // Map[Function[transform, TransformOne[transform[[1]], transform[[2]] ]]]] &, newData]], {gen, gens}]; newData]; DisplayMultiwayGrowth[data_] := Graphics3D[{EdgeForm[Thin], MapIndexed[{Hue[#2[[1]]/10], Cuboid[# - 0.5 {1, 1, 1}, # + 0.5 {1, 1, 1}]} &, Catenate[#["Voxels"] & /@ data]]}, Boxed -> False, Lighting -> "Neutral", SphericalRegion -> True]; Manipulate[SeedRandom[seed]; initial = {$HeadlampPrototile}; evolved = MultiwayVoxelEvolve[initial, generations]; DisplayMultiwayGrowth[evolved], {{generations, 3, "Generations"}, 1, 5, 1}, {{seed, 1, "Random Seed"}, 1, 100, 1}, ControlPlacement -> Top] Most notably the Niederman approach makes it possible to alter not just the cyclic stage of generational structures but also the seed value which allows for "reproducibility" that is the first, implementation that we have got to write down if we note that the abstraction, the symbolism of the multi-way rules coincides with the 3D voxel-based evolution and the way that the analogy of Niederman's mention of extending multi-way transformations beyond simple symbolic representations to intuitive structures plays out, is that the voxel evolution is inherently multi-way: each generation's structure can branch into various configurations non-deterministically. Our three-dimensional graphical representation prospectively sources universality-like behavior reminiscent of universal computation through multi-way systems through the lens of Niederman just so that yes, we can approach multi-way tag systems through multi-way branching that works through state transition graphs arising from simple rules, subjectivizes complexity through interactive "explorations" and statistical metrics analogous to the growth rate measures that originally symbolized one-dimensional formulations but now, they reinforce the three-dimensional and two-dimensional versatility of our visual analogs, expressly powering our multi-way computational frameworks and effectively exemplify the tangible nature of theoretical and computational representations of conceptual ideas, in multi-way tag systems. Multiway3D[rules_, init_, steps_] := Module[{mw, states, edges, coords, stateToCoord, cuboids, edgeLines, centroid, baseLabelPositions, extraAdjustments, xyPositions, groups}, mw = ResourceFunction["MultiwaySystem"][rules, init, steps, "StatesGraph"]; states = VertexList[mw]; edges = EdgeList[mw]; coords = Table[With[{s = If[StringQ[state], state, ToString[state]]}, {StringCount[s, "A"], StringCount[s, "B"], GraphDistance[mw, init, state]}], {state, states}]; stateToCoord = Thread[states -> coords]; cuboids = {EdgeForm[Directive[Black, Thin]], FaceForm[Directive[Opacity[0.8], LightGray]], Map[Cuboid[# - {0.45, 0.45, 0.45}, # + {0.45, 0.45, 0.45}] &, coords]}; edgeLines = {Directive[Thick, Blue], Line /@ (edges /. stateToCoord /. DirectedEdge -> List)}; centroid = Mean[coords]; baseLabelPositions = Map[# + (Normalize[# - centroid]*0.5 + {0, 0, 0.6}) &, coords]; xyPositions = coords[[All, {1, 2}]]; groups = Gather[Range[ Length[xyPositions]], (xyPositions[[#1]] === xyPositions[[#2]]) &]; extraAdjustments = ConstantArray[{0, 0, 0}, Length[coords]]; Do[If[Length[group] > 1, Module[{base, rad, perp, n, offsets}, base = coords[[First[group]]]; rad = Normalize[{base[[1]] - centroid[[1]], base[[2]] - centroid[[2]], 0}]; perp = {-rad[[2]], rad[[1]], 0}; n = Length[group]; offsets = Table[(i - (n + 1)/2)*0.2*perp, {i, 1, n}]; Do[ extraAdjustments[[group[[j]]]] = offsets[[j]], {j, 1, n}]]], {group, groups}]; Graphics3D[{cuboids, edgeLines, MapThread[ Text[Style[#1, FontSize -> 12, FontFamily -> "Helvetica", Bold, Black], #2 + extraAdjustments[[#3]]] &, {states, baseLabelPositions, Range[Length[states]]}]}, Axes -> True, AxesLabel -> {Style["A Count", Bold, 14], Style["B Count", Bold, 14], Style["Generation", Bold, 14]}, BoxRatios -> {1, 1, 1}, ViewPoint -> {2, 2, 2}, Lighting -> "Neutral", ImageSize -> 600]]; Multiway3D[{"A" -> "AB", "B" -> "A"}, "A", 4] Three-dimensional space, if we look around we'll see how Niederman's concept of visualizing multi-way state graphs where each vertex represents system states after rule applications, can "be plotted" via Multiway3D in a three-dimensional coordinate system based on counts of symbols ("A" and "B") and generation steps, which can make us make the multi-way tag system ({"A"->"AB", "B"->"A"}) more precisely match Niederman's exploration of symbolic transformations as it becomes more 3-dimensional (symbol counts vs. generation number are fundamentally "different") in the sense of labeling & positioning, which come along with the "twin horn" or exponential-growth. But first, we should look at the quite animated, 3-dimensional rotating cuboid and quite possibly and interactively visualize some more abstract LEGO structures. Animate[Graphics3D[{EdgeForm[None], FaceForm[RGBColor[1, 0.5, 0.1, 0.5] ], Cuboid[{-1, -1, -1}, {1, 1, 1}] }, Boxed -> False, Lighting -> "Neutral", ViewPoint -> {4 Cos[t], 4 Sin[t], 2}, ImageSize -> 400 ], {t, 0, 2 Pi}, AnimationRunning -> True, AnimationRate -> 0.5] One might think the complexity of these iterative growth processes can be represented auditorially. Who knows, everybody just visualizes whatever they want to visualize and the auditory attributes of these multi-way tag systems, they echo the iterations of systems that might otherwise seem too abstract, too complex to explore. The thing to remember is that where binary strings evolve according to non-deterministic rules, the color association assigns specific colors to different transition indices. Via GenerateTransitions, we can generate transitions by producing new states by scanning through the current binary string, matching each two-digit segment against the rule set, and replacing it with the corresponding outcome. Starting from an initial state such as {0, 1, 1}, the system iteratively applies these rules for a set number of steps. As new states are generated, a graph is constructed showing the transitions. Each edge in the graph is color-coded--this is why we call it the "rabbit system" or the "twin horn system"--because the transitions result from different applied rules. It's a simultaneous keeping track of the non-deterministic evolution of the system while transforming binary states, that's the multi-way transition approach. $HeadlampPrototile = Association["Voxels" -> {{0, 0, 0}}, "Ins" -> <|1 -> {{0, 0, 0}, {1, 0, 0}}|>, "Outs" -> <|1 -> {{0, 0, 0}, {-1, 0, 0}}|>]; FourPointTransforms[match_, move_] := Translate[IdentityMatrix[3], move[[1]]]; VectorTransform[match_, move_] := Rotate[IdentityMatrix[3], Pi/2, move[[2]]]; FourTransforms[match_, move_] := Transpose[{FourPointTransforms[match, move], VectorTransform[match, move]}]; TransformOne[ptfun_, vecfun_] := Module[{res = <||>}, res["Voxels"] = Map[ptfun, $HeadlampPrototile["Voxels"]]; res["Ins"] = MapAt[vecfun, MapAt[ptfun, $HeadlampPrototile["Ins"], {All, 1}], {All, 2}]; res["Outs"] = MapAt[vecfun, MapAt[ptfun, $HeadlampPrototile["Outs"], {All, 1}], {All, 2}]; res]; rules = {"A" -> "AB", "B" -> "A"}; init = "A"; multiwayGraph = ResourceFunction["MultiwaySystem"][rules, init, 3, "StatesGraphStructure"]; TransformFromState[state_] := Module[{chars = Characters[state], ptfun = Identity, vecfun = Identity}, Do[Switch[ch, "A", ptfun = TranslationTransform[{1, 0, 0}]@*ptfun, "B", ptfun = RotationTransform[Pi/2, {0, 0, 1}]@*ptfun], {ch, chars}]; {ptfun, vecfun}]; states = VertexList[multiwayGraph]; tileData = <||>; Do[{pt, vec} = TransformFromState[state]; tileData[state] = TransformOne[pt, vec]["Voxels"], {state, states}]; DynamicModule[{currentState = init, path = {init}}, Column[{GraphPlot[multiwayGraph, VertexShapeFunction -> (Inset[Button[#2, currentState = #2; path = FindShortestPath[multiwayGraph, init, #2]], #1] &), ImageSize -> 200], Dynamic@Graphics3D[{EdgeForm[Opacity[0.1]], MapIndexed[{ColorData["Rainbow"][First[#2]/Length[path]], Cuboid[# - {0.5, 0.5, 0.5}, # + {0.5, 0.5, 0.5}]} &, Catenate[tileData /@ path]]}, Boxed -> False, Lighting -> "Neutral", SphericalRegion -> True, ViewAngle -> 0.5, PlotLabel -> currentState]}]] The voxel-based headlamp prototile system above shows that we are beginning to bridge the gap between symbolic multi-way transformations, as explored by Niederman, and their 3-dimensional geometric counterparts. Although we have not yet fully translated all of Niederman's transformations into a completely voxelized, iterative construction process, we now have a framework for interpreting symbolic transformations in a tangible, 3-dimensional voxel form. Certainly we're not out of the woods yet but here we know now that we can instantiate physical structures that are "being" built iteratively in the above voxel-based headlamp prototile multiway evolution, the kind that theoretically concretizes the symbolic growth rules into tangible 3-dimensional voxel structures. Multiway3D[rule_, init_, steps_] := Module[{g, g3d, coords, vertices, edges}, g = ResourceFunction["MultiwaySystem"][rule, init, steps, "StatesGraph"]; g3d = Graph3D[g, GraphLayout -> "SpringElectricalEmbedding", VertexSize -> 0.1, EdgeStyle -> Directive[Opacity[0.5], Gray]]; coords = Thread[VertexList[ g3d] -> (VertexCoordinates /. AbsoluteOptions[g3d, VertexCoordinates])]; vertices = VertexList[g3d] /. coords; edges = EdgeList[g3d] /. a_ \[DirectedEdge] b_ :> Line[{a /. coords, b /. coords}]; Graphics3D[{{RGBColor[0.027, 0.545, 0.788], Cuboid[# - {0.1, 0.1, 0.1}, # + {0.1, 0.1, 0.1}] & /@ vertices}, {Gray, Tube[#, 0.02] & /@ edges}}, Boxed -> True, Lighting -> "Neutral", ImageSize -> Medium, ViewPoint -> {1.3, -2.4, 2.0}]]; DynamicModule[{step = 3, system = {"A" -> "AB", "B" -> "A"}, init = {"A"}}, Column[{Row[{Control[{{step, 3, "Steps"}, 1, 5, 1}], Spacer[20], Button["Reset", step = 3, ImageSize -> Medium]}], Dynamic@Multiway3D[system, init, step]}, Alignment -> Center]] In this system, each symbolic rule that defines how a state evolves can be directly linked to a geometric transformation, thereby concretizing abstract growth rules into physically interpretable voxel structures. In the subsequent section, we take a well-known symbolic tag system--represented by the rules {"A" -> "AB", "B" -> "A"}--and show how it can be transformed into a series of physical voxel transformations. If there's one thing we do know, however, it's that it IS possible to convert symbolic multi-way evolutions; we need to be reminded, this might be another, "Here comes honey goo goo" moment or it might be nothing but in any case it's not just another symbolic representation of a tag system. It might even be a more spatialized physicalization of the state graph mapping paradigm; we don't know the specific "details" but now we can be certain that there is a clear way to demonstrate the mapping from symbolic states (strings) to spatially transformed states (voxels). When I saw Niederman's symbolic-to-structural approach, I knew that I had to go at least try out making a more clickable graph via the interactive visual tools said and provided, because then we can have a frame of reference for user-guided exploration of states and paths. So check this out; there's another way, a multi-way variant of graphs that can always get better uses for the 3-dimensional spring-electrical embedding on state connections and transitions, which was superior to our non-visual embedding variant. Multiway3D[rule_, init_, steps_] := Module[{g = ResourceFunction["MultiwaySystem"][rule, init, steps, "StatesGraph"], coords, voxels, edges3D, labels}, coords = AssociationThread[VertexList[g], MapIndexed[ RotateLeft[{#2[[1]], 0, #2[[1]]/10}, Mod[#2[[1]], 3]] &, VertexList[g]]]; voxels = Map[Cuboid[# - {0.1, 0.1, 0.1}, # + {0.1, 0.1, 0.1}] &, Values[coords]]; edges3D = Map[Tube[{coords[#[[1]]], coords[#[[2]]]}, 0.02] &, EdgeList[g]]; labels = MapThread[ Text[Style[#1, 8, Black, Background -> White], #2, {-1.5, 0}] &, {VertexList[g], Values[coords]}]; DynamicModule[{view = {2, 2, 2}}, Column[{Graphics3D[{{RGBColor[0.4, 0.6, 1], edges3D}, {RGBColor[1, 0.4, 0.4], voxels}, {Black, labels}}, Lighting -> "Neutral", ViewPoint -> Dynamic[view], ImageSize -> 400, Boxed -> False, SphericalRegion -> True]}]]] Multiway3D[{"A" -> "AB", "B" -> "A"}, "A", 4] The interesting thing about the 3-dimensional spring-electrical embedding is that it "implicitly contains" an alternative visual representation, which gives us another chance to do a lot of radial or layered layouts at once or maybe just clarify structural and relational complexity one step at a time within state graphs. Because I don't know, which graph variant raises the least questions. But I do know that having an interactive, clickable graph represents the multiway evolution of states. Each symbolic state (a string of letters) is transformed into a spatial configuration of voxels via a mapping function (here, through functions like TransformFromState and TransformOne). This visualization coupled with a 3-dimensional spring-electrical embedding generates many types of intuitively understandable state connections and transitions compared to any traditional non-visual embedding. MultiwayTagExplorer[] := DynamicModule[{rules, init = {1, 0, 1}, steps = 5, graph, path = {}, metrics}, rules = {{left___, 0, 0, s___} :> {left, s, 1, 0, 0}, {left___, 1, 0, s___} :> {left, s, 0, 1}, {left___, 0, 1, s___} :> {left, s, 1, 1, 0}, {left___, 1, 1, s___} :> {left, s, 0, 0, 0}}; graph = ResourceFunction["NestGraphTagged"][ReplaceList[rules], {init}, steps, VertexShapeFunction -> (Inset[ ArrayPlot[{#2}, ImageSize -> 40, ColorRules -> {0 -> Pink, 1 -> Darker@Cyan}], #1, Center] &), GraphLayout -> "LayeredDigraphEmbedding", PerformanceGoal -> "Quality"]; BFSMetrics[g_, start_, end_] := Module[{parent = <||>, queue = {start}, current, neighbors, metrics = <|"Visited" -> {}, "QueueSize" -> {}, "Density" -> {}|>}, While[Length[queue] > 0, current = First[queue]; queue = Rest[queue]; If[! KeyExistsQ[metrics["Visited"], current], AppendTo[metrics["Visited"], current]; AppendTo[metrics["QueueSize"], Length[queue]]; neighbors = AdjacencyList[g, current]; Do[If[! KeyExistsQ[parent, n], parent[n] = current; AppendTo[queue, n];], {n, neighbors}]; subgraph = Subgraph[g, metrics["Visited"]]; density = If[VertexCount[subgraph] > 1, N[EdgeCount[ subgraph]/(VertexCount[ subgraph] (VertexCount[subgraph] - 1)/2)], 0]; AppendTo[metrics["Density"], density];]; If[current === end, Break[]];]; metrics]; Column[{Grid[{{"Initial State:", InputField[ Dynamic[init, (init = #; graph = ResourceFunction["NestGraphTagged"][ ReplaceList[rules], {init}, steps]) &], Expression]}, {"Steps:", Slider[Dynamic[ steps, (steps = #; graph = ResourceFunction["NestGraphTagged"][ ReplaceList[rules], {init}, steps]) &], {1, 8, 1}]}}], Dynamic@ClickPane[ HighlightGraph[graph, {Style[path, Directive[Thick, Red]]}, ImageSize -> 800, VertexLabels -> Placed["Name", Center], EdgeLabels -> "EdgeTag", GraphHighlightStyle -> "Thick"], Function[coords, metrics = BFSMetrics[graph, init, VertexList[graph][[ First@Nearest[ GraphEmbedding[graph] -> Range[VertexCount[graph]], coords]]]]; path = FindShortestPath[graph, init, VertexList[graph][[ First@Nearest[ GraphEmbedding[graph] -> Range[VertexCount[graph]], coords]]]];]], Dynamic@If[Length[metrics["Visited"]] > 0, Column[{ListLinePlot[{metrics["QueueSize"], metrics["Density"]}, PlotLegends -> {"Queue Size", "Graph Density"}, PlotLabel -> "Exploration Metrics", ImageSize -> 400], Grid[{{"States Visited:", Length[metrics["Visited"]]}, {"Final Queue Size:", Last[metrics["QueueSize"]]}, {"Final Density:", Last[metrics["Density"]]}}, Frame -> All]}]]}]]; MultiwayTagExplorer[] I cannot advise in totality which method of demonstrating a multi-way tag system but I can fix the tag rules so that we know, what are the definitive Breadth-First Search metrics like queue size and graph density; how do we teach state / path growth rates to serve the purpose of complexity measurement in a practical, interactive way? How do we extend our, alteration of initial states and steps to the methodological and "systematic" enumeration of multi-way system behaviors? Binary tag rules with their state-to-state transitions will always cause the graph complexity metrics to be quantifiable, because that is how we measure complexity and growth. But that's not all. musicRules = {"A" -> "BC", "B" -> "ACD", "C" -> "DE", "D" -> "EF", "E" -> "FG", "F" -> "GH", "G" -> "HA", "H" -> "A"}; initState = "A"; steps = 6; evolution = NestList[StringReplace[#, musicRules] &, initState, steps]; finalState = Last[evolution]; stateSequence = Characters[finalState]; noteRules = <|"A" -> SoundNote["C4", 0.35], "B" -> SoundNote["D4", 0.35], "C" -> SoundNote["E4", 0.35], "D" -> SoundNote["F4", 0.35], "E" -> SoundNote["G4", 0.35], "F" -> SoundNote["A4", 0.35], "G" -> SoundNote["B4", 0.35], "H" -> SoundNote["C5", 0.35]|>; chordRules = {"A" -> SoundNote[{"C4", "E4", "G4"}, 0.35], "B" -> SoundNote[{"D4", "F4", "A4"}, 0.35], "C" -> SoundNote[{"E4", "G4", "B4"}, 0.35], "D" -> SoundNote[{"F4", "A4", "C5"}, 0.35], "E" -> SoundNote[{"G4", "B4", "D5"}, 0.35], "F" -> SoundNote[{"A4", "C5", "E5"}, 0.35], "G" -> SoundNote[{"B4", "D5", "F5"}, 0.35], "H" -> SoundNote[{"C5", "E5", "G5"}, 0.35]}; melody = Sound[RotateRight[stateSequence, 1] /. noteRules]; harmony = Sound[Partition[stateSequence, 2, 1] /. chordRules]; rhythm = Sound[Table[ SoundNote["C2", RandomChoice[{0.2, 0.25, 0.3, 0.35}]], {Length[ stateSequence]}]]; audioMelody = Audio[melody, SampleRate -> 44100]; audioHarmony = Audio[harmony, SampleRate -> 44100]; audioRhythm = Audio[rhythm, SampleRate -> 44100]; combinedAudio = AudioChannelCombine[{audioMelody, audioHarmony, audioRhythm}]; DynamicModule[{playState = "Stop"}, Column[{Labeled[ Column[{Style["Evolution String:", Bold, 14], finalState, Row[{Button[ Dynamic[If[playState === "Playing", "⏸", "\[FilledRightTriangle]"]], playState = If[playState === "Playing", "Paused", "Playing"]; If[playState === "Playing", AudioPlay[combinedAudio]], ImageSize -> {50, 50}], ProgressIndicator[ Dynamic[Clock[{1, Length[stateSequence], 1}]], {1, Length[stateSequence]}, ImageSize -> 300]}]}], Style["L-System Music Evolution", 16, Bold]]}]] The transformative creativity of symbolic multi-way rules doesn't matter with regard to multi-way rules so much as it "gives us" a greater perspective on multi-way system outputs; here, symbolic-to-spatial transformations become structured auditory experiences. The emergence of complexity from simple iterative rules is so much fun--that's the reason why this time we translate complexity into sound, harnessing the universality and expressive power of musical structures. ruledRule1044 = {{0, 0} -> {0, 1}, {0, 0} -> {1, 1}, {0, 1} -> {0, 1}, {0, 1} -> {1, 1}, {1, 0} -> {1, 0}, {1, 0} -> {1, 1}, {1, 1} -> {1, 0}, {1, 1} -> {1, 1}}; colorAssociation = <|1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown, 5 -> Orange, 6 -> Red, 7 -> Magenta, 8 -> Purple|>; transitions = {{{0, 1, 1} -> {0, 1, 1}, {0, 1} -> {0, 1}}, {{0, 1, 1} -> {1, 1, 1}, {0, 1} -> {1, 1}}, {{0, 1, 1} -> {0, 1, 0}, {1, 1} -> {1, 0}}, {{0, 1, 1} -> {0, 1, 1}, {1, 1} -> {1, 1}}, {{1, 1, 1} -> {1, 1, 0}, {1, 1} -> {1, 0}}, {{1, 1, 1} -> {1, 1, 1}, {1, 1} -> {1, 1}}, {{0, 1, 0} -> {0, 1, 0}, {0, 1} -> {0, 1}}, {{0, 1, 0} -> {1, 1, 0}, {0, 1} -> {1, 1}}, {{1, 1, 0} -> {1, 0, 0}, {1, 1} -> {1, 0}}, {{1, 1, 0} -> {1, 1, 0}, {1, 1} -> {1, 1}}, {{1, 0, 0} -> {1, 0, 1}, {0, 0} -> {0, 1}}, {{1, 0, 0} -> {1, 1, 1}, {0, 0} -> {1, 1}}, {{1, 0, 1} -> {1, 0, 1}, {0, 1} -> {0, 1}}}; stateAssociation = <|{0, 1, 1} -> 1, {1, 1, 1} -> 2, {0, 1, 0} -> 3, {1, 1, 0} -> 4, {1, 0, 0} -> 5, {1, 0, 1} -> 6|>; myGraph = EdgeTaggedGraph[ Flatten[Table[ Style[stateAssociation[Flatten[Keys[trans[[1]]]]] -> stateAssociation[Flatten[Values[trans[[1]]]]], Directive[ colorAssociation[ Flatten[Position[ruledRule1044, trans[[2]]]][[1]]], Small], Arrowheads[0.012]], {trans, transitions}]], VertexStyle -> Black, VertexSize -> Large, VertexShapeFunction -> "Square", GraphLayout -> "LayeredDigraphEmbedding"]; Animate[HighlightGraph[myGraph, Take[EdgeList[myGraph], k]], {k, 1, Length[EdgeList[myGraph]], 1}, AnimationRepetitions -> 1, Paneled -> False, DisplayAllSteps -> True] It turns out that we don't have to see the graph all at once; we can look at the map and cheat a little by animating state transitions to illustrate evolution dynamics that shows that it's just progressively revealing state complexity through dynamic visualization techniques that is, and the particular evolution paths highlighted in otherwise complex transition systems, that makes tracing paths possible as we apprehend these multiway transition graphs, and it shows Mathematica as this relentless animation powerhouse that maps out the corners of our multi-way transition graph. possibles = Flatten[Table[{{a, b}, {c, d}}, {a, {0, 1}}, {b, {0, 1}}, {c, {0, 1}}, {d, {0, 1}}], 3]; states = Union@Flatten[possibles, 1]; statesStr = ToString /@ states; colorAssociation = <|0 -> Gray, 1 -> Blue, 2 -> Green, 3 -> Red, 4 -> Orange, 5 -> Purple, 6 -> Brown, 7 -> Cyan, 8 -> Magenta, 9 -> Lime, 10 -> Pink, 11 -> Yellow, 12 -> Teal, 13 -> Coral, 14 -> Lavender, 15 -> Gold|>; dynamicRules = {{0, 0} -> {{0, 1}, {1, 0}}, {0, 1} -> {{1, 1}, {0, 0}}, {1, 0} -> {{1, 1}, {0, 1}}, {1, 1} -> {{0, 0}, {1, 0}}}; transitions = Flatten[Table[ DirectedEdge[before, after], {before, states}, {after, Replace[before, dynamicRules, {}]}], 1]; transitionsStr = transitions /. x_List :> ToString[x]; Graph[transitions, VertexStyle -> Thread[states -> (colorAssociation /@ Range[0, 15]~Take~Length[states])], EdgeStyle -> {e_ :> If[MemberQ[path, e], Directive[Thick, Red], Automatic]}, VertexLabels -> Placed["Name", Center], VertexSize -> 0.15, ImageSize -> 300] DynamicModule[{currentState = {0, 0}, path = {}, history = {{0, 0}}, gLocal}, gLocal = g; Column[{Button["Reset", currentState = {0, 0}; path = {}; history = {{0, 0}};], Grid[{{"Current State:", Framed[Style[Dynamic[currentState], Bold, 20], Background -> White]}, {"Possible Next States:", Dynamic@Column[Replace[currentState, dynamicRules, {}]]}}, Frame -> All], Button["Step Forward", Module[{nextStates = Replace[currentState, dynamicRules, {}]}, If[nextStates =!= {}, currentState = RandomChoice[nextStates]; AppendTo[path, DirectedEdge[ToString[history[[-1]]], ToString[currentState]]], currentState = {0, 0}; path = {}; history = {{0, 0}};]; AppendTo[history, currentState];]], Dynamic[Panel[ Grid[{{"Steps Taken:", Length[history]}, {"Current Path:", path}, {"Unique States Visited:", Length[Union[history]]}}, Frame -> All]]]}, Spacings -> 1]] And as we step forward, we sort of step outside of the evolution dynamics via path tracing and user-driven investigation of multi-way state evolution and transitions, by including metrics and state histories; otherwise how would we know whether this is from 2024 or 2016 type of a thing? Anyway, I think that the tracking and display of steps and unique states visited and paths, these are the metrics that strongly and scriptedly just the same connect Niederman's analytical approach, to complexity measurement, the graphical representations of "which" are a lot like pulling those abstract symbols apart, including complexity measurements that start out with a doll-like form of multi-way tag systems and transform them into something even more awesome--an exploration of the computational and expressive possibilities across all domains whether it's packing auditory domains full of lively auditory representations of our spatial and symbolic paradigm, all within these multi-way tag systems. ruledRule1044 = {{0, 0} -> {0, 1}, {0, 0} -> {1, 1}, {0, 1} -> {0, 1}, {0, 1} -> {1, 1}, {1, 0} -> {1, 0}, {1, 0} -> {1, 1}, {1, 1} -> {1, 0}, {1, 1} -> {1, 1}}; ruleAssoc = GroupBy[ruledRule1044, First -> Last]; evolve[state_] := Flatten[RandomChoice[ruleAssoc[#]] & /@ Partition[state, 2]]; Manipulate[SeedRandom[seed]; currentEvolution = NestList[evolve, initialState, steps]; ArrayPlot[Transpose[Reverse[currentEvolution]], Mesh -> True, ColorRules -> {0 -> White, 1 -> Black}, Frame -> True, FrameTicks -> {{Range[0, steps], Range[0, steps]}, {Range[Length[initialState]], None}}, PlotLabel -> "Non-deterministic Cellular Automaton Evolution (Rotated)", ImageSize -> 500] , {{steps, 10, "Generations"}, 1, 50, 1, Appearance -> "Labeled"}, {{seed, 42, "Random Seed"}, 1, 100, 1, Appearance -> "Labeled"}, {{initialState, {0, 1, 1, 0}}, ControlType -> None}, Button["New Random Seed", seed = RandomInteger[{1, 1000}], ImageSize -> Medium], Button["Reset Initial State", initialState = {0, 1, 1, 0}, ImageSize -> Medium], ControlPlacement -> Top, Paneled -> True, Initialization :> (initialState = {0, 1, 1, 0};)] And so you see multi-way tag systems allow for non-deterministic outcomes; it is these multiple possible outcomes that can result from a single state. That is how this multiple possible outcomes thing works; we don't have any non-deterministic outcomes to represent and allow in the first place, meaning multiple possible outcomes can result from a single state. So we use RandomChoice to non-deterministically produce different results on each run based on random selections, just as Niederman describes multi-way systems. In later parts of the description there is a hint of a contrast amongst the processes required to guide "non-deterministic outcomes" like photons (by changing refractive indices) with those used for electrons (doping semiconductors), an underinterpretation of the different physical phenomena (for example, the interference of light) that provide new computing possibilities. colorAssociation = <|0 -> Blue, 1 -> Cyan, 2 -> Green, 3 -> Brown, 4 -> Orange, 5 -> Red, 6 -> Magenta, 7 -> Purple, 8 -> Blue|>; IsStable2D[grid_, zc_] := AllTrue[Flatten[grid], # < zc &]; Stabilize2D[grid_, zc_] := Module[{dims = Dimensions[grid], newGrid = grid}, While[! IsStable2D[newGrid, zc], Do[If[newGrid[[i, j]] >= zc, newGrid[[i, j]] -= zc; If[i > 1, newGrid[[i - 1, j]] += 1]; If[i < dims[[1]], newGrid[[i + 1, j]] += 1]; If[j > 1, newGrid[[i, j - 1]] += 1]; If[j < dims[[2]], newGrid[[i, j + 1]] += 1];], {i, dims[[1]]}, {j, dims[[2]]}];]; newGrid]; DynamicModule[{grid = ConstantArray[0, {21, 21}]}, Column[{"Click cells to add sand grains (max 3 shown):", ClickPane[ Dynamic[ArrayPlot[Map[Mod[#, 8] &, grid, {2}], ColorRules -> {n_ :> colorAssociation[n]}, ImageSize -> 250, Mesh -> True, PlotRange -> {{0, 21}, {0, 21}}, Frame -> False]], Function[pt, Module[{x, y}, x = Clip[Floor[pt[[1]]] + 1, {1, 21}]; y = Clip[21 - Floor[pt[[2]]], {1, 21}]; grid = ReplacePart[grid, {x, y} -> grid[[x, y]] + 1]; grid = Stabilize2D[grid, 4];]]]}]] Isn't it a stabilizing thing? When in the open close case of sandpile models, the state stability involves state evolution and fixed points. Descriptively, evolution functions and the stability of states are a "sieve" that brings our local systems to an eventually "reachable" stable "formulation" of states or fixed points. Similarly, our sandpile model repeatedly applies a transformation until a stable state (where no more updates are required or attributable) is reached. Fixed-point graphs breed stability in multi-way tag systems. SandStep[s_] := s + ListConvolve[{{0, 1, 0}, {1, -4, 1}, {0, 1, 0}}, UnitStep[s - 4], 2, 0]; initialState = CenterArray[{{25}}, {11, 11}]; evolution = NestList[SandStep, initialState, 15]; StateHash[grid_] := Hash[Integer[Round[grid]]]; graph = TransitiveReductionGraph@ RelationGraph[ AnyTrue[Flatten@#2, # > 3] && ContainsExactly[Integer[Round[#2]], {0, 1, 2, 3, 4}] && StateHash[#1] == StateHash[SandStep[#1]] &, evolution, VertexShapeFunction -> (Inset[ ArrayPlot[#2, ColorRules -> {0 -> White, 1 -> LightBlue, 2 -> Cyan, 3 -> Blue, 4 -> Darker@Red, _?Negative -> Black}, PixelConstrained -> 2, ImageSize -> 30], #1, Center, Scaled[0.15]] &), GraphLayout -> {"LayeredDigraphEmbedding", "Orientation" -> Bottom}, EdgeStyle -> Directive[Orange, Thickness[0.003]], VertexSize -> 1.5, PerformanceGoal -> "Quality"]; GraphPlot3D[graph, VertexStyle -> Normal@AssociationThread[VertexList[graph], ColorData["Rainbow"] /@ Rescale[VertexDegree[graph]]], EdgeStyle -> Directive[Blue, Opacity[0.3]], PlotTheme -> "Detailed", Boxed -> False, ViewVertical -> {0, 0, 1}, VertexSize -> 0.2, ImageSize -> 800] Manipulate[ ArrayPlot[evolution[[step]], ColorRules -> {0 -> White, 1 -> LightBlue, 2 -> Cyan, 3 -> Blue, 4 -> Darker@Red, _?Negative -> Black}, Epilog -> {Red, PointSize[0.03], Point /@ Position[evolution[[step]], _?(# >= 4 &)]}, ImageSize -> 300], {{step, 1, "Generation"}, 1, Length@evolution, 1, Appearance -> "Labeled"}, ControlPlacement -> Top] The extensive employment of 2-dimensional and 3-dimensional remembrances finish the structure and dynamics of multi-way state evolution, such as the "Twin Horn System" which automatically interprets how states transition and evolve, reverting us to an understanding of system-atic graph-theoretical measurements as we use them, and as we "relate" them to Niederman's systematic analysis, of state graphs. states = Tuples[{0, 1}, 2]; ruledRule1044 = {{0, 0} -> {0, 1}, {0, 0} -> {1, 1}, {0, 1} -> {0, 1}, {0, 1} -> {1, 1}, {1, 0} -> {1, 0}, {1, 0} -> {1, 1}, {1, 1} -> {1, 0}, {1, 1} -> {1, 1}}; edges = Flatten[ Table[{DirectedEdge[#[[1]], #[[2]]], n} & /@ Take[ruledRule1044, {n}], {n, Length[ruledRule1044]}], 1]; colorAssociation = <|1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown, 5 -> Orange, 6 -> Red, 7 -> Magenta, 8 -> Purple|>; myGraph = Graph[edges[[All, 1]], EdgeStyle -> Thread[edges[[All, 1]] -> (edges[[All, 2]] /. colorAssociation)], EdgeLabels -> {e_ :> Placed["Index", Tooltip]}, VertexLabels -> Placed["Name", Center], VertexStyle -> Directive[Black, EdgeForm[White]], VertexSize -> 0.25, VertexLabelStyle -> Directive[White, Bold, 8], EdgeLabelStyle -> Directive[Red, Italic, 12], PerformanceGoal -> "Quality", GraphLayout -> "LayeredDigraphEmbedding", ImageSize -> 200]; metrics = {{"Vertex Count", VertexCount[myGraph]}, {"Edge Count", EdgeCount[myGraph]}, {"Mean Vertex Degree", N@Mean[VertexDegree[myGraph]]}, {"Mean In-Degree", N@Mean[VertexInDegree[myGraph]]}, {"Mean Betweenness", N@Mean[BetweennessCentrality[myGraph]]}}; Grid[Prepend[metrics, {"Metric", "Value"}], Frame -> All, Alignment -> Left, Background -> {None, {LightBlue, {LightOrange, LightGreen}}}] // Framed[#, RoundingRadius -> 10] &; Row[{myGraph, Spacer[20], %}, Alignment -> Center] While Niederman's work primarily focuses on including graph complexity examination and "exploratory" growth characteristics using measures like state growth rate and path growth rate, we here can significantly characterize system complexity and connectivity of intermediate stages by hovering over nodes, the tooltips that provide epilogical quantifications of the complexity of the evolution of Cellular Automata. stateAssociation = <|{0, 1, 1} -> 1, {1, 1, 1} -> 2, {0, 1, 0} -> 3, {1, 1, 0} -> 4, {1, 0, 0} -> 5, {1, 0, 1} -> 6|>; colorAssociation = <|1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Magenta, 5 -> Orange, 6 -> Red|>; rule = 30; width = 61; steps = 30; initialState = SparseArray[{Ceiling[width/2] -> 1}, width]; ca = CellularAutomaton[rule, initialState, steps]; getNeighborhoods[array_] := Module[{padded}, padded = PadLeft[PadRight[array, Length[array] + 2], Length[array] + 2]; Partition[padded, 3, 1]]; colorMatrix = Table[neighborhoods = getNeighborhoods[ca[[i]]]; Replace[neighborhoods, stateAssociation, {1}] /. _Missing -> 7, {i, 1, Length[ca]}]; coloredCA = Map[colorAssociation[#] &, colorMatrix, {2}]; ArrayPlot[coloredCA, ColorRules -> Join[Normal[colorAssociation], {7 -> None}], PixelConstrained -> 10, Frame -> False, AspectRatio -> 1/2] And these rule-based systems, they will not stop evolving iteratively from initial states according to simple local rules! They're going to iteratively and recursively detail the natural analogies intrinsic to these multi-way systems like the Cellular Automaton-based systems we've discussed. And so we restructure the rule enumeration and system specification structure effectively enumerating possible transformation rules and evaluating system behavior, by skipping over rules systematically and enumerating their pretensive impact on system dynamics ala transition matrices and eigen values, much like the existing definition and labeling of rule sets. colorAssociation = <|0 -> Black, 1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown, 5 -> Orange, 6 -> Red, 7 -> Magenta, 8 -> Purple, 9 -> Yellow, 10 -> Pink|>; InitializeGrid[size_] := RandomInteger[{0, 9}, {size, size}]; UpdateGrid[grid_] := Module[{neighborSum, probUpdate, newGrid, sweepProbability = 0.02, rows, cols, i, j, newValue}, neighborSum = ListConvolve[{{1, 1, 1}, {1, 0, 1}, {1, 1, 1}}, grid, {2, 2}, 0]; probUpdate = Map[If[2 <= # <= 7, Rescale[#, {2, 7}, {0.3, 1}], 0] &, neighborSum, {2}]; newGrid = MapThread[ If[RandomReal[] < #2, Mod[#1 + 1, 10], #1] &, {grid, probUpdate}, 2]; {rows, cols} = Dimensions[newGrid]; Do[If[RandomReal[] < sweepProbability, i = RandomInteger[{1, rows - 2}]; j = RandomInteger[{1, cols - 2}]; newValue = RandomInteger[{0, 9}]; newGrid[[i ;; i + 2, j ;; j + 2]] = ConstantArray[newValue, {3, 3}];], {5} ]; newGrid]; SimulateCA[size_, generations_] := Module[{evolution}, evolution = NestList[UpdateGrid, InitializeGrid[size], generations]; ListAnimate[ ArrayPlot[#, ColorRules -> Normal[colorAssociation], Mesh -> False, PlotRange -> {0, 9}, ImageSize -> 500] & /@ evolution, AnimationRate -> 8, ControlPlacement -> Top]]; SimulateCA[100, 300] Furthermore--we're used to enumerating rules systematically--now we can explore the adjacency and or rotation matrices. Eigenvalues, are a relational matrix tool for analyzing growth rates. So when we utilize adjacency matrices to analytically calculate growth rates for finite systems (e.g., the Fibonacci-like "rabbit system"), we build matrix representations and adjust them if they don't work out right away, right? Well, it depends on how many matrix representations came before us and how much facilitation of similar analytical and geometric state transitions we can evolve, and their derivatives in the form of symmetries and firm non-deterministic evolution throughout our randomized rule selections, which do directly implement them, the multi-way non-determinism described by Niederman. That's why I don't just "discard" the state stabilization and fixed points; it turns out that the sandpile stabilization mirrors the examination of fixed points and equilibrium states in the context of state evolution, so for Niederman's use of graphs we can understand complex state evolution. I think that the local-rule-based evolution based on index-adjacent rotation matrices brings about the analytical and algebraic methods for enunciating the growth and evolution rates, and comes to embody key conceptual frameworks and computational methods not just in multi-way tag systems analysis but also in non-deterministic dynamics, in all the analytical methods that we need. State evolution and complexity was awesome, loved it. colorAssociation = <|0 -> Black, 1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown|>; initializeGrid[size_] := SparseArray[{{Ceiling[size/2], Ceiling[size/2]} -> 4}, {size, size}]; stabilize[grid_] := FixedPoint[ Module[{dims = Dimensions[grid], temp = #}, Do[If[temp[[i, j]] >= 4, temp[[i, j]] -= 4; If[i > 1, temp[[i - 1, j]] += 1]; If[i < dims[[1]], temp[[i + 1, j]] += 1]; If[j > 1, temp[[i, j - 1]] += 1]; If[j < dims[[2]], temp[[i, j + 1]] += 1];], {i, dims[[1]]}, {j, dims[[2]]}]; temp] &, grid]; addGrain[grid_] := Module[{new = grid, pos, dims = Dimensions[grid]}, pos = RandomInteger[{1, #}, 2] & /@ dims; new[[pos[[1]], pos[[2]]]] += 1; stabilize[new]]; SeedRandom[42]; size = 5; initialState = initializeGrid[size]; states = NestList[addGrain, initialState, 8]; ListAnimate[ ArrayPlot[#, ColorRules -> colorAssociation, PlotLabel -> "Step " <> ToString[#2]] & @@@ Transpose[{states, Range[0, Length[states] - 1]}], AnimationRate -> 1, ImageSize -> 75] stateGraph = Graph[DirectedEdge @@@ Partition[Range[Length[states]], 2, 1], VertexShapeFunction -> (Inset[ ArrayPlot[states[[#2]], ColorRules -> colorAssociation, ImageSize -> 50], #1, Center] &), VertexSize -> 1.5, EdgeStyle -> Directive[Orange, Thick, Arrowheads[0.02]], GraphLayout -> "LayeredDigraphEmbedding", ImageSize -> 150, PlotLabel -> Style["State Transition Graph", 12, Bold]]; stateGraph This is why it's so important to facilitate analytical and geometric to decide to place some symmetrical obstacles into our state transitions, via adjacency matrices that serve as the foundation of mathematizing our linear algebraic operations which are still non-deterministic. But it's really about harnessing interference effects, opening up altogether new computational paradigms that initializations like electrons do not allow. There are "several" fabrication methods: traditional semiconductor etching (which requires doping to create narrow, electron-conducting channels) versus guiding light via channels that differ in refractive index. This not only draws in attention to the fundamental material differences but also brings up the thin veneer that--with photons--you might also bounce between interference effects for what that's worth. Here's an example of a rotation matrix: axis = {1, 0, 1}; normAxis = axis/Sqrt[axis . axis]; angle = Pi/3; rotationMatrix3 = RotationMatrix[angle, normAxis]; rotationMatrix3 // MatrixForm This rotation operation relates to the geometric and symmetric transformation Niederman implies when discussing multi-dimensional state visualization. It goes a long way in supporting exploration of state-space symmetry in full visualizations of multi-way evolution graphs. And there are nontrivial interactions of light with matter. While detecting photons typically requires conversion back to electrical signals (using the photoelectric effect), it's the unique phenomena--like frequency shifting and interference--that promise additional functionalities (for instance, doing Fourier transforms in hardware). These effects answer the question of the possibility of a photonic switch that could function analogously (yet differently) to a transistor, a key component that drives conventional electronics. ruleSet = {{{0, 0, 0} -> {0, 0, 1}, "Rule A"}, {{0, 0, 1} -> {0, 1, 1}, "Rule B"}, {{0, 1, 0} -> {1, 0, 0}, "Rule C"}, {{0, 1, 1} -> {1, 1, 0}, "Rule D"}, {{1, 0, 0} -> {0, 0, 1}, "Rule E"}, {{1, 0, 1} -> {1, 1, 1}, "Rule F"}, {{1, 1, 0} -> {0, 1, 0}, "Rule G"}, {{1, 1, 1} -> {1, 0, 1}, "Rule H"}}; colorRules = Association[ MapIndexed[#[[2]] -> ColorData["Rainbow"][#2[[1]]/Length[ruleSet]] &, ruleSet]]; matrixTable = Grid[Prepend[ Map[Function[ r, {ToString[r[[1, 1]]], "\[RightArrow]", ToString[r[[1, 2]]], ":", r[[2]]}], ruleSet], {"Input State", "", "Output State", "", "Rule"}], Frame -> All]; edges = Map[ Function[r, Module[{lhs, rhs, lab}, lhs = r[[1, 1]]; rhs = r[[1, 2]]; lab = r[[2]]; Labeled[DirectedEdge[lhs, rhs], lab]]], ruleSet]; allStates = Union[Flatten[Map[{#[[1, 1]], #[[1, 2]]} &, ruleSet], 1]]; stateGraph = Graph[edges, VertexLabels -> "Name", GraphLayout -> "LayeredDigraphEmbedding", VertexStyle -> Directive[Black], ImageSize -> 400, PlotLabel -> Style["State Transition Graph", 16, Bold]]; Grid[{{Style["Transformation Matrix", Bold, 14]}, {matrixTable}, {stateGraph}}, Spacings -> {2, 2}] These clearly defined state transitions resemble Niederman's from now on approach to tagging and reproducible rule-set within multi-way tag systems. Trying out a bunch of rules within multi-way tag systems opens the door to labeling rules corresponding to Niederman's rendition of state-graphs and rule-enumeration systems. These rule-enumeration systems frame the well-known mathematical fact about regular (Platonic) solids, contrast the limitless variety of regular polygons in 2 dimensions with the strict limitations in 3 dimensions--a classical result in geometry. Can we just extend these ideas into four dimensions and go beyond the traditional, transition to questions about hyper-platonic solids and their potential utility? For example, in network design or error-correcting codes through sphere packing? ruledRule1044 = {{0, 0} -> {0, 1}, {0, 0} -> {1, 1}, {0, 1} -> {0, 1}, {0, 1} -> {1, 1}, {1, 0} -> {1, 0}, {1, 0} -> {1, 1}, {1, 1} -> {1, 0}, {1, 1} -> {1, 1}}; colorAssociation = <|1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown, 5 -> Orange, 6 -> Red, 7 -> Magenta, 8 -> Purple|>; stateAssociation = <|{0, 0} -> 1, {0, 1} -> 2, {1, 0} -> 3, {1, 1} -> 4|>; edges = Table[Module[{from, to}, from = rule[[1]]; to = rule[[2]]; stateAssociation[from] -> stateAssociation[to]], {rule, ruledRule1044}]; edgeStyles = Table[Directive[colorAssociation[i], Thickness[0.005], Arrowheads[{{0.015, 0.7}}]], {i, Length[edges]}]; transitionGraph = Graph[edges, EdgeLabels -> Placed["Index", 0.5], EdgeStyle -> Thread[edges -> edgeStyles], VertexLabels -> "Name", VertexLabelStyle -> Directive[Black, Bold, 16], VertexSize -> 0.3, VertexStyle -> White, GraphLayout -> "SpringElectricalEmbedding", ImageSize -> 400]; vertexCount = VertexCount[transitionGraph]; edgeCount = EdgeCount[transitionGraph]; meanDegree = Mean[VertexDegree[transitionGraph]]; betweenness = Mean[BetweennessCentrality[transitionGraph]]; statsPanel = Grid[{{"Vertex Count", vertexCount}, {"Edge Count", edgeCount}, {"Average Degree", NumberForm[meanDegree, 3]}, {"Average Betweenness", NumberForm[betweenness, 3]}}, Alignment -> Left, Background -> {None, {LightBlue, LightOrange}}, Dividers -> {False, True}, ItemStyle -> Bold, Frame -> True, FrameStyle -> GrayLevel[0.8]]; Row[{transitionGraph, Spacer[20], statsPanel}, Alignment -> Top] Vertex count, edge count, degree, betweenness centrality--these metrics are comparative quantitative measure for characterizing system complexity and growth rates. And of course, the categorization of complexity and growth behaviors of multi-way systems. I think that the geometric conversation can straightforwardly say for sure that by naming the 20-cell and 60-cell four-dimensional objects, sure three dimensions limit us to only five Platonic solids..higher dimensions allow for new echelons of regular structures. rule1044 = {{{0, 0}, {0, 1}}, {{0, 0}, {1, 1}}, {{0, 1}, {0, 1}}, {{0, 1}, {1, 1}}, {{1, 0}, {1, 0}}, {{1, 0}, {1, 1}}, {{1, 1}, {1, 0}}, {{1, 1}, {1, 1}}}; rules = #[[1]] -> #[[2]] & /@ rule1044; stateAssociation = <|{0, 1, 1} -> 1, {1, 1, 1} -> 2, {0, 1, 0} -> 3, {1, 1, 0} -> 4, {1, 0, 0} -> 5, {1, 0, 1} -> 6|>; colorAssociation = <|1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown, 5 -> Orange, 6 -> Red, 7 -> Magenta, 8 -> Purple|>; Manipulate[ Module[{evolution}, evolution = ResourceFunction["MultiwaySystem"][rules, {{0, 1, 1}}, steps, "StatesGraph", GraphLayout -> "LayeredDigraphEmbedding"]; Graph[evolution, VertexStyle -> Thread[VertexList[evolution] -> Lookup[colorAssociation, Lookup[stateAssociation, VertexList[evolution], 7]]], VertexSize -> 0.5, EdgeStyle -> Directive[Gray, Thickness[0.001]], VertexLabels -> Placed["Name", Center], PerformanceGoal -> "Quality", ImageSize -> 400]], {steps, 1, 6, 1, Appearance -> "Labeled"}, ControlPlacement -> Top, FrameMargins -> 0] This is essentially the same approach to visualize multi-way evolution through directed graphs, texturizing the non-deterministic transitions and branching nature of the evolution of states. There are some practical applications of this approach such as designing optimal switching networks or developing new algorithms that benefit from high-dimensional geometry. But we had made ourselves into the ultimate, cultural and psychological reflection of the impact of interacting with advanced AIs. The grammatical issue of emotional attachment to advanced AIs that "know you better than you know yourself", thereby links the discussion of AI agents with syntactical questions about human-machine interaction and relationships. In doing so, we parrot the implications of AI personalization and trust, which intersects tag systems with the multi-language capacity of AI and human perception. colorAssociation = <|0 -> Black, 1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown, 5 -> Orange, 6 -> Red, 7 -> Magenta, 8 -> Purple|>; InitializeGrid[size_] := ArrayPad[RandomInteger[{0, 1}, {size, size}], 1, "Periodic"]; EvolutionRule[grid_] := Module[{newGrid, neighbors, cell}, newGrid = ConstantArray[0, Dimensions[grid]]; Do[neighbors = Total@Flatten@grid[[i - 1 ;; i + 1, j - 1 ;; j + 1]] - grid[[i, j]]; cell = grid[[i, j]]; newGrid[[i, j]] = Switch[cell, 0, If[neighbors == 3, 1, 0], 1, If[2 <= neighbors <= 3, 2, 1], 2, If[1 <= neighbors <= 4, 3, 2], 3, If[neighbors > 4, 4, If[neighbors < 2, 2, 3]], 4, If[EvenQ[neighbors], 5, 4], 5, If[OddQ[neighbors], 6, 5], 6, 7, 7, 8, 8, 0];, {i, 2, Length[grid] - 1}, {j, 2, Length[grid] - 1}]; ArrayPad[newGrid[[2 ;; -2, 2 ;; -2]], 1, "Periodic"]]; DynamicModule[{grid = InitializeGrid[50], running = True}, Column[{Button[Dynamic[running], running = ! running, Appearance -> "Palette"], Dynamic[If[running, grid = EvolutionRule[grid]]; ArrayPlot[grid[[2 ;; -2, 2 ;; -2]], PixelConstrained -> 8, ColorRules -> Normal[colorAssociation], Frame -> False, ImageSize -> 500]]}, Alignment -> Center]] We have this idea which frequently refers to cellular automata-like transformations. Our Cellular Automata rules define state evolution that is syntactically and conventionally iterative, in the directive construction of locally-interactive state systems. Shifting focus to practical design considerations, we wish for multi-way tag systems not only to record isomorphisms (such as with cellular automata or sandpile models) but also to interact more fluidly with the user experience. Although this topic seems distinct from next-level wearable geometry like photonic computing, both areas share a common thread: they discuss how subtle design choices (whether in hardware materials, sensor technology, or interface interaction) can make a significant difference in usability and efficiency. ruledRule1044 = {{0, 0} -> {0, 1}, {0, 0} -> {1, 1}, {0, 1} -> {0, 1}, {0, 1} -> {1, 1}, {1, 0} -> {1, 0}, {1, 0} -> {1, 1}, {1, 1} -> {1, 0}, {1, 1} -> {1, 1}}; colorAssociation = <|1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown, 5 -> Orange, 6 -> Red, 7 -> Magenta, 8 -> Purple|>; states = Union[ruledRule1044[[All, 1]], ruledRule1044[[All, 2]]]; edges = DirectedEdge @@@ ruledRule1044; edgeStyles = Table[edges[[i]] -> colorAssociation[i], {i, Length[edges]}]; stateGraph = Graph[states, edges, EdgeStyle -> edgeStyles, VertexLabels -> Placed["Name", Center], VertexStyle -> Directive[Black, EdgeForm[White]], VertexSize -> 0.35, VertexLabelStyle -> Directive[White, Bold, 14], GraphLayout -> "LayeredDigraphEmbedding", ImageSize -> 300, PlotLabel -> Style["Non-Deterministic State Transitions", White, Bold, 24]]; graphMetrics = {{"Vertex Count", VertexCount[stateGraph]}, {"Edge Count", EdgeCount[stateGraph]}, {"Mean Vertex Degree", N[Mean[VertexDegree[ stateGraph]]]}, {"Mean Betweenness Centrality", N[Mean[BetweennessCentrality[stateGraph]]]}}; Grid[{{stateGraph}, Sequence @@ ({Style[#[[1]], Bold], #[[2]]} & /@ graphMetrics)}, Alignment -> Left, Dividers -> All, Background -> {None, {LightGray, {White}}}, FrameStyle -> Directive[Gray, Thick]]; Animate[HighlightGraph[stateGraph, Subgraph[stateGraph, Evaluate[Take[EdgeList[stateGraph], n]]]], {n, 0, Length[edges], 1}] In the same way that the prior sandpile stabilization algorithms analyze system stability, fixed points, and state complexity, our analysis of tag systems includes principles of iterative stabilization and equilibrium. It relates to the earlier technical discussions by contrasting the rigorous, precision-oriented details in state complexity technology (like electron/photon channels) with the process management that exists beneath large-scale innovation. Both the iterative and the ideal approach require detailed planning, measured milestones, and recognition of when to pause for review and quality control. rules = {"A" -> "AB", "A" -> "BA", "B" -> "AA"}; colorRules = <|"A" -> "AB" -> Red, "A" -> "BA" -> Blue, "B" -> "AA" -> Green|>; evolutions = ResourceFunction["MultiwaySystem"][rules, "A", 4, "StatesGraph", VertexLabels -> "Name", GraphLayout -> "LayeredDigraphEmbedding"]; styledEdges = Map[# /. DirectedEdge[a_, b_, c_] :> Style[DirectedEdge[a, b, c], colorRules[c]] &, EdgeList[evolutions]]; styledGraph = Graph[styledEdges, VertexLabels -> "Name", GraphLayout -> "LayeredDigraphEmbedding", VertexSize -> 0.2, EdgeLabels -> Placed["EdgeTag", 0.5], EdgeLabelStyle -> Directive[Black, Italic, 10], EdgeStyle -> Thick, VertexStyle -> Directive[LightBlue, EdgeForm[Black]], ImageSize -> 800]; styledGraph We started out with 2-dimensional state transition graphs and now, our 3-dimensional cellular automaton integrations extend the Niederman framework to three-dimensional cellular automata. I thought the spatial and temporal complexities suggested in state-graph embeddings were easily recognizable, until I saw the multi-dimensional embeddings. The type of embeddings that we're talking about aren't iterative but they are coordinated in vector space and controlled deployment in the development process--this is a resounding theme of the systematic approach found in both scientific research and engineering. Whether you're designing cellular automata or managing a complex multiway system, constructing a time series is crucial. SandStep[s_] := s + ListConvolve[{{0, 1, 0}, {1, -4, 1}, {0, 1, 0}}, UnitStep[s - 4], 2, 0]; AddGrain[grid_] := Module[{x, y, dim = Length[grid]}, {x, y} = RandomInteger[{1, dim}, 2]; ReplacePart[grid, {x, y} -> grid[[x, y]] + 5]]; Stabilize[grid_] := Most@FixedPointList[SandStep, grid]; InitializeGrid[size_, height_] := CenterArray[{{height}}, {size, size}]; Manipulate[ Module[{gridState = If[reset, InitializeGrid[21, initialHeight], currentGrid]}, If[add, currentGrid = AddGrain[gridState]; avalancheSteps = Stabilize[currentGrid]; step = 1; add = False; reset = False]; Column[{ArrayPlot[ If[avalancheMode && step <= Length[avalancheSteps], avalancheSteps[[step]], currentGrid], ColorFunction -> "TemperatureMap", ImageSize -> 400, PlotLabel -> If[avalancheMode, "Avalanche Step: " <> ToString[step], "Current State"]], If[avalancheMode && Length[avalancheSteps] > 1, Row[{"Avalanche Progress: ", ProgressIndicator[step/Length[avalancheSteps]], Button["Next Step", If[step < Length[avalancheSteps], step++, step = 1]]}], Nothing]}]], {{currentGrid, InitializeGrid[21, 4]}, ControlType -> None}, {{avalancheSteps, {}}, ControlType -> None}, {{step, 1}, ControlType -> None}, {{add, False}, ControlType -> None}, {{reset, False}, ControlType -> None}, Row[{Button["Add Grain", add = True; reset = False, ImageSize -> 100], Button["Reset", currentGrid = InitializeGrid[21, initialHeight]; reset = True; step = 1, ImageSize -> 100]}], Control[{{initialHeight, 4, "Initial Center Height"}, 1, 10, 1}], Control[{{avalancheMode, True, "Show Avalanche Steps"}, {True, False}}], TrackedSymbols :> {add, reset, initialHeight, avalancheMode, step}] I would think that the Sandpile Model matches non-deterministic evolution via random choices, conceptualizing multi-way systems as where multiple outcomes are exponentially actualized from single direct states, branching out into complex state-spaces. Although these state spaces are traditionally seen as a haven for endless possibilities, the reality is filled with experimental mathematics which serve as a bridge in the discussion about non-deterministic versus deterministic evolution--whether to pursue research in an academic bubble or embrace the challenges of entrepreneurial ventures where decisions must be made rapidly and holistically. possibles = Tuples[{0, 1}, 2]; ruledRule1044 = {{0, 0} -> {0, 1}, {0, 0} -> {1, 1}, {0, 1} -> {0, 1}, {0, 1} -> {1, 1}, {1, 0} -> {1, 0}, {1, 0} -> {1, 1}, {1, 1} -> {1, 0}, {1, 1} -> {1, 1}}; colorAssociation = <|1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown, 5 -> Orange, 6 -> Red, 7 -> Magenta, 8 -> Purple|>; edges = Flatten[ Table[With[{rule = ruledRule1044[[n]]}, DirectedEdge[rule[[1]], rule[[2]], n]], {n, Length[ruledRule1044]}], 1]; stateTransitionGraph = EdgeTaggedGraph[edges, EdgeStyle -> Table[edge -> colorAssociation[edge[[3]]], {edge, edges}], VertexLabels -> "Name", VertexShapeFunction -> "Square", VertexSize -> 0.1, EdgeLabels -> "EdgeTag", ImageSize -> Large, GraphLayout -> "SpringElectricalEmbedding"]; stateTransitionGraph My answer would be not to visually analyze all the different graph layout methods, but to look at these sandpiles and state transition graphs as being in the context of Gaussian noise. It means that the assumption that we make about the noise being Gaussian can also be violated. Because what we can do is we can make good use of the appearances of the objects as the appearances of the objects change and or we're looking at not just one target but multiple targets. This is the story of analytics, the admonition that champions a STEM mindset in entrepreneurship. It conveys that the analytical skills cultivated in rigorous scientific study (like problem solving with mathematics or physics) are equally valuable in business strategy and decision-making. ruledRule1044 = {{0, 0} -> {0, 1}, {0, 0} -> {1, 1}, {0, 1} -> {0, 1}, {0, 1} -> {1, 1}, {1, 0} -> {1, 0}, {1, 0} -> {1, 1}, {1, 1} -> {1, 0}, {1, 1} -> {1, 1}}; stateAssociation = <|{0, 1, 1} -> 1, {1, 1, 1} -> 2, {0, 1, 0} -> 3, {1, 1, 0} -> 4, {1, 0, 0} -> 5, {1, 0, 1} -> 6|>; colorAssociation = <|1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown, 5 -> Orange, 6 -> Red, 7 -> Magenta, 8 -> Purple|>; transitions = {}; Do[currentState = stateAssocKeys[[i]]; Do[If[MatchQ[Part[currentState, j ;; j + 1], rule[[1]]], nextState = ReplacePart[ currentState, {j -> rule[[2, 1]], j + 1 -> rule[[2, 2]]}]; If[KeyExistsQ[stateAssociation, nextState], AppendTo[ transitions, {stateAssociation[currentState] -> stateAssociation[nextState], rule}]]], {j, 2}, {rule, ruledRule1044}], {i, Length[stateAssocKeys = Keys[stateAssociation]]}]; edges = Style[DirectedEdge @@ #[[1]], Directive[ colorAssociation@First@Flatten[Position[ruledRule1044, #[[2]]]], Thickness[0.003], Arrowheads[0.02]]] & /@ transitions; stateGraph = EdgeTaggedGraph[edges, VertexLabels -> {v_ :> Placed[Framed[Style[v, 14, White], Background -> Darker@Gray, RoundingRadius -> 5], Center]}, VertexStyle -> Darker[Gray, 0.8], VertexSize -> 0.25, VertexShapeFunction -> "RoundedRectangle", GraphLayout -> "SpringElectricalEmbedding", EdgeLabels -> Placed["EdgeTag", 0.6], Prolog -> {White, Opacity[0.1], Rectangle[Scaled[{0, 0}], Scaled[{1, 1}]]}]; graphMetrics = {{"Vertex Count", VertexCount@stateGraph}, {"Edge Count", EdgeCount@stateGraph}, {"Mean Degree", N@Mean@VertexDegree@stateGraph}, {"Diameter", GraphDiameter@stateGraph}, {"Clustering Coefficient", N@Mean@LocalClusteringCoefficient@stateGraph}}; Grid[Prepend[graphMetrics, {"Metric", "Value"}], Frame -> All, Alignment -> Left, Background -> {None, {Lighter[Yellow, 0.9], {White}}}] Labeled[stateGraph, Style["Cellular Automaton State Transition Graph", 16, Bold], Top] But nonetheless we don't understand, that the multi-way state evolutions can be understood through various embeddings, which beats all the computational complexity through pattern recognition in multi-way systems; the quantum lattice evolution that we do and the "Quantum Fibonacci" mentioned is still a fresh and unused, quantum-like complex interaction. We can think of multi-way systems via the island mentality that encompasses all elements of fractal growth. It ties back to the earlier technical discussions on photonic computing and geometry, where precise quantitative thinking is essential, and emphasizes that such skills enable one to analyze markets, manage projects, and ultimately innovate. possibles = Flatten[Table[{{a, b}, {c, d}}, {a, {0, 1}}, {b, {0, 1}}, {c, {0, 1}}, {d, {0, 1}}], 3]; mf = FunctionRepository["MultiwaySystem"]; visualizeTransitions[ruleNumber_, steps_] := Module[{rule, initState, statesGraph, cat}, rule = IntegerDigits[ruleNumber, 2, 8]; initState = {CenterArray[1, {3, 3}]}; statesGraph = mf["CellularAutomaton" -> {2, {2, 1}, rule}, initState, steps, "StatesGraph", GraphLayout -> {"LayeredDigraphEmbedding", "Orientation" -> Top}, VertexShapeFunction -> (Inset[ ArrayPlot[#2, Mesh -> All, ImageSize -> 30, ColorRules -> {0 -> Black, 1 -> White}], #1, Center, 0.1] &), EdgeStyle -> Directive[Thickness[0.005], Opacity[0.5], Gray], VertexSize -> 0.2, PerformanceGoal -> "Quality"];]; Manipulate[ Column[{Grid[{{visualizeTransitions[ruleNumber, steps], ArrayPlot[ CellularAutomaton[{ruleNumber, 2, 1}, {{1}, 0}, steps], ColorRules -> {0 -> Black, 1 -> White}, Frame -> False, ImageSize -> 300]}}, Spacings -> {2, 1}]}], {{ruleNumber, 110, "Rule Number"}}, {{steps, 5, "Evolution Steps"}, 1, 10, 1, Appearance -> "Labeled"}] It's one thing to look at these pretty black and white graphs, however we can decide to entertain vertex degree and diameter and centrality--graph metrics that technicalize the quantitative structural properties and growth complexity of state graph metrics and analysis. rules = {{0, 0} -> {0, 1}, {0, 0} -> {1, 1}, {0, 1} -> {1, 0}, {0, 1} -> {1, 1}, {1, 0} -> {0, 0}, {1, 0} -> {0, 1}, {1, 1} -> {0, 0}, {1, 1} -> {1, 0}}; init = {{0, 0, 0}}; evograph = ResourceFunction["MultiwaySystem"][rules, init, 4, "StatesGraph"]; vertexList = VertexList[evograph]; vertexColors = AssociationThread[vertexList, ColorData["SolarColors"] /@ Rescale[Hash /@ vertexList]]; Graph[evograph, VertexStyle -> Normal[vertexColors], GraphLayout -> "LayeredDigraphEmbedding", VertexSize -> 0.7, VertexShapeFunction -> "Capsule", EdgeStyle -> Directive[GrayLevel[0.3], Thickness[0.0015]], VertexLabels -> Placed["Name", Tooltip], ImageSize -> Medium, AspectRatio -> 1/2, PerformanceGoal -> "Quality"] We were right the first time about multi-dimensional, voxel-based evolutions that correspond directly to the Niederman, sophisticated approach to visualizing complex evolutionary state-graphs, including multi-dimensional embeddings which embed spatial analysis reflecting multiple outcomes from single initial conditions. By mapping symbolic states directly to their corresponding 3-dimensional voxel arrangements, users can now explore the evolution of the system through both state transitions and physical transformations. This approach innovates a robust frame of reference for user-guided exploration and may lead to the relationship between symbolic and spatial representations in complex systems. ruledRuleComplex = {{0, 0} -> {0, 1}, {0, 0} -> {1, 2}, {0, 1} -> {2, 0}, {0, 1} -> {1, 1}, {1, 0} -> {0, 2}, {1, 0} -> {2, 1}, {1, 1} -> {0, 0}, {1, 1} -> {2, 2}, {1, 2} -> {0, 1}, {1, 2} -> {2, 0}, {2, 0} -> {0, 0}, {2, 0} -> {1, 2}, {2, 1} -> {2, 0}, {2, 1} -> {0, 2}, {2, 2} -> {1, 1}, {2, 2} -> {0, 2}}; colorAssociation = <|0 -> Gray, 1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown, 5 -> Orange, 6 -> Red, 7 -> Magenta, 8 -> Purple|>; stringRules = Map[ToString[#[[1]]] -> ToString[#[[2]]] &, ruledRuleComplex]; Manipulate[ Module[{dynGraph, dynVertices, target, foundPaths, highlightPath}, dynGraph = ResourceFunction["MultiwaySystem"][stringRules, {"{0, 1}"}, generations, "StatesGraph", GraphLayout -> {"LayeredDigraphEmbedding", "Orientation" -> Left}, VertexShapeFunction -> (With[{state = ToExpression[#2]}, {colorAssociation[FromDigits[state, 3]], EdgeForm[Black], Disk[#1, 0.15], Text[Style[#2, White, Bold], #1]}] &), EdgeStyle -> Directive[Thick, Gray], VertexSize -> 0.4, ImageSize -> 400]; dynVertices = VertexList[dynGraph]; target = If[Length[dynVertices] > 1, First[Select[dynVertices, (# =!= "{0, 1}") &]], "{0, 1}"]; foundPaths = FindPath[dynGraph, "{0, 1}", target, {3}, All]; highlightPath = If[foundPaths === {}, Graph[{}, {}], PathGraph[foundPaths[[1]]]]; HighlightGraph[dynGraph, highlightPath, GraphHighlightStyle -> Directive[Thick, Red], PlotLabel -> Style[Row[{"Path length ", 3, " in a ", generations, " generation graph from {0, 1} to ", target}], 12, Bold]]], {{generations, 1, "Generations"}, 1, 4, 1, Appearance -> "Labeled"}] That's what brings these graph layouts for intuitive interpretation "onto" the quantitative characterization from looking at piles of sand to characterizing the quantitative complexity of a poptart or attending to the higher-dimensional state-space that is reachable via rule-based iterative transformations and quantum-like evolution which takes advantage of the spatial dynamics that, may be the foundation of multi-way tag systems but that aren't the deterministic, concrete computational representation of the collective concepts explored in Niederman's article, at least not yet. But anyway. possibles = Flatten[Table[{{a, b}, {c, d}}, {a, {0, 1}}, {b, {0, 1}}, {c, {0, 1}}, {d, {0, 1}}], 3]; transitionRules = {{0, 0} -> {0, 1}, {0, 1} -> {1, 1}, {1, 0} -> {1, 0}, {1, 1} -> {1, 0}}; colorAssociation = <|0 -> Blue, 1 -> Red, 2 -> Green, 3 -> Orange|>; stateAssociation[grid_] := Hash[grid, "MD5"]; ApplyRules[grid_] := Module[{newGrid}, newGrid = Partition[grid /. transitionRules, 2]] GenerateEvolutionGraph[initialState_, generations_] := Module[{states = {initialState}, edges = {}}, Do[states = DeleteDuplicates@ Flatten[Function[g, With[{newStates = ApplyRules /@ g}, edges = Join[edges, Thread[g -> newStates]]; newStates]] /@ states, 1];, {generations}]; {states, edges}] Manipulate[ Module[{states, edges, graph, vd, ed, mvd}, {states, edges} = GenerateEvolutionGraph[Partition[RandomInteger[1, 4], 2], generations]; graph = Graph[edges, VertexShapeFunction -> (Inset[ Style[Framed[MatrixForm[#2], FrameStyle -> Directive[Thick, #2 /. colorAssociation]], EdgeForm[Black], Background -> White], #, {0, 0}, {0.1, 0.1}] &), EdgeStyle -> Directive[Gray, Thickness[0.003]], GraphLayout -> "LayeredDigraphEmbedding", PerformanceGoal -> "Quality", ImageSize -> 400]; vd = VertexDegree[graph]; mvd = Mean[vd]; Column[{graph, Grid[{{"Vertex Count", VertexCount[graph]}, {"Edge Count", EdgeCount[graph]}, {"Max Degree", Max[vd]}, {"Min Degree", Min[vd]}, {"Mean Degree", mvd}}, Frame -> All]}]], {{generations, 3, "Generations"}, 1, 5, 1, Appearance -> "Labeled"}, Button["New Initial State", None, ImageSize -> Medium], ControlPlacement -> Top] So in the first evolution graph example. Without much of an explanation we find a collection of possible two-digit pairs that is created from binary values, and we have to figure out--provided a set of transition rules--for instance, replacing {0, 0} with {0, 1} and so on..what are the binary transitions? Well, the numerical values are inextricably associated via colors and, the grid state that is hashed for uniqueness..the transformation of the ApplyRules transforms a flat list (or grid) by applying these transitions and then partitions the result back into two-element blocks. InitializeQuantumGrowth[] := Association["Position" -> {{0, 0, 0}}, "Directions" -> {IdentityMatrix[3]}, "States" -> {1}, "History" -> {}]; QuantumTransform[state_] := Module[{newstates}, newstates = Switch[state, 1, {{2, RotationMatrix[Pi/2, {1, 0, 0}]}, {3, RotationMatrix[Pi/2, {0, 1, 0}]}}, 2, {{1, RotationMatrix[-Pi/2, {1, 0, 0}]}, {3, RotationMatrix[Pi/2, {0, 0, 1}]}}, 3, {{1, RotationMatrix[-Pi/2, {0, 1, 0}]}, {2, RotationMatrix[-Pi/2, {0, 0, 1}]}}, _, {}]; Map[Association["State" -> #[[1]], "Rotation" -> #[[2]]] &, newstates]]; IterateQuantumGrowth[data_] := Module[{newpositions = {}, newdirections = {}, newstates = {}, newhistory, max}, newhistory = data["History"]; max = Length[data["Position"]]; Do[Module[{pos, dir, state, transforms}, pos = data["Position"][[i]]; dir = data["Directions"][[i]]; state = data["States"][[i]]; transforms = QuantumTransform[state]; Do[Module[{newdir, newpos}, newdir = t["Rotation"] . dir; newpos = pos + newdir[[1]]; If[! MemberQ[data["Position"], newpos], AppendTo[newpositions, newpos]; AppendTo[newdirections, newdir]; AppendTo[newstates, t["State"]]; newhistory = Append[newhistory, {pos, newpos}];]], {t, transforms}]], {i, max}]; If[Length[newpositions] > 0, Association["Position" -> Join[data["Position"], newpositions], "Directions" -> Join[data["Directions"], newdirections], "States" -> Join[data["States"], newstates], "History" -> newhistory], data]]; VisualizeQuantumGrowth[data_] := Graphics3D[{Thickness[0.02], MapThread[{Hue[#/3], Line[#2]} &, {data["States"][[2 ;;]], data["History"]}], MapThread[{Hue[#/3], Specularity[White, 20], Sphere[#2, 0.3]} &, {data["States"], data["Position"]}]}, Lighting -> "Neutral", Boxed -> False, PlotRange -> {{-10, 10}, {-10, 10}, {-10, 10}}, ImageSize -> 300]; qgrowth = Nest[IterateQuantumGrowth, InitializeQuantumGrowth[], 6]; VisualizeQuantumGrowth[qgrowth] Next, we iteratively apply the rules over a set number of generations; as new states are generated, edges representing their evolution are constructed and later visualized as a directed graph. That is why we set the number of generations and generate a new initial state, while providing statistical summaries such as vertex count, edge count, and average degree..to show, how binary state transitions can be systematically explored, and how the resulting evolution can be encoded graphically. MultiwayVoxelEvolution[rules_, init_, generations_] := Module[{statesGraph, coordMap, edges, voxels, lines}, statesGraph = ResourceFunction["MultiwaySystem"][rules, init, generations, "StatesGraph"]; coordMap = Association[ MapIndexed[ ToString[#1] -> {Mod[Hash[ToString[#1], "Adler32"], 10], Mod[Hash[ToString[#1], "CRC32"], 10], Mod[Hash[ToString[#1], "MD5"], 10]} &, VertexList[statesGraph]]]; edges = List @@@ (EdgeList[ statesGraph] /. (a_ -> b_) :> {ToString[a], ToString[b]}); voxels = Table[{Hue[Norm[pt]/30, 1, 1], Cuboid[pt - 0.4, pt + 0.4]}, {pt, Values[coordMap]}]; lines = Map[Function[edge, Module[{pts = Lookup[coordMap, edge]}, {GrayLevel[0.3, 0.5], Tube[{pts[[1]] + RandomReal[{-0.1, 0.1}, 3], pts[[2]] + RandomReal[{-0.1, 0.1}, 3]}, 0.05]}]], edges]; Manipulate[ Graphics3D[{voxels, lines}, Boxed -> False, SphericalRegion -> True, ViewPoint -> {Sin[\[Theta]] Cos[\[Phi]], Sin[\[Theta]] Sin[\[Phi]], Cos[\[Theta]]}, PlotLabel -> Style["Multiway System Evolution", 16]], {{\[Theta], \[Pi]/4}, 0, \[Pi]}, {{\[Phi], \[Pi]/4}, 0, 2 \[Pi]}, TrackedSymbols -> {\[Theta], \[Phi]}]] MultiwayVoxelEvolution[{"A" -> "AB", "AB" -> "BA", "B" -> "A"}, "A", 5] The introduction of a "quantum growth" simulation..wherein the system, is initialized as a single voxel at the origin with an associated state and a list of possible directional transformations..the function QuantumTransform assigns new states and rotations to the current state--for example, if the current state is 1, it may spawn new voxels rotated by ±90° around a given axis. The appellation of the iterative function IterateQuantumGrowth then makes it easy for us to examine each current voxel's state and direction, apply the corresponding transformations, and expand and fold the overarching structure while recording the growth history. Finally, VisualizeQuantumGrowth takes the accumulated positions and histories, rendering a 3-dimensional graphic where lines trace the growth path and spheres show the evolving voxels with colors based on their state. MultiwayQuantumLattice[rules_, init_, steps_] := Module[{statesGraph, edges, vertices, coordMap, quantumStates, depthMap, rotations, quantumColor}, statesGraph = ResourceFunction["MultiwaySystem"][rules, init, steps, "StatesGraph", GraphLayout -> {"LayeredEmbedding", "Orientation" -> Top}]; vertices = VertexList[statesGraph]; edges = EdgeList[statesGraph]; quantumStates = AssociationThread[vertices, Map[Count[#, "1"]/Max[1, StringLength[#]] &, vertices]]; quantumColor[x_] := Blend[{RGBColor[0.27, 0.34, 1., 0.145], RGBColor[0.34, 0.27, 1., 0.145]}, x]; depthMap = AssociationThread[vertices, GraphDistance[statesGraph, First[vertices], #] & /@ vertices]; coordMap = Association@ MapThread[#1 -> {Cos[#2*2 Pi/5], Sin[#2*2 Pi/5], depthMap[#1]/2} &, {vertices, Range[Length[vertices]] - 1}]; rotations = Table[RotationMatrix[\[Theta], {0, 0, 1}], {\[Theta], 0, 2 Pi, Pi/2}]; Graphics3D[{{Opacity[0.2], LightBlue, Map[Line[{{0, 0, 0}, #}] &, Values[coordMap]]}, {Specularity[1, 50], MapThread[{quantumColor[#2], GeometricTransformation[ Cuboid[#1 - {0.1, 0.1, 0}, #1 + {0.1, 0.1, 0.2}], rotations]} &, {Values[coordMap], Values[quantumStates]}]}, {Thick, Opacity[0.5], Map[{ColorData["Rainbow"][RandomReal[]], Tube[{Lookup[coordMap, #[[1]]], Lookup[coordMap, #[[2]]]}, 0.02]} &, edges]}}, Boxed -> False, Lighting -> {"Ambient", White}, ViewPoint -> {3, 1, 1}, ImageSize -> 400, PlotLabel -> Style["Quantum Multiway Lattice", 12], SphericalRegion -> True]] QuantumFibonacciSystem[] := MultiwayQuantumLattice[{"A" -> "AB", "B" -> "A"}, "A", 6] QuantumFibonacciSystem[] This module illuminates how discrete rotational and translational operations can model a kind of “quantum” twist in a spatial setting. In other geometries, the code demonstrates a direct mapping from symbolic multiway systems to voxel-based 3D structures. A voxel prototype (the “headlamp prototile”) is defined with voxel positions and connectors for incoming and outgoing links. Functions for geometric transformations—one that shifts points (FourPointTransforms) and another that rotates vectors (VectorTransform)—combine into a transformation routine (FourTransforms) that applies both translation and rotation. A symbolic rule system (e.g., {"A" -> "AB", "B" -> "A"}) is then processed by a function (TransformFromState) which reads each symbol from a state string and applies a corresponding transformation. The transformed states yield specific voxel arrangements via TransformOne. An interactive 3D graph, built with dynamic controls, lets users click through the state space; each state is represented with a clickable button inserted into a 3D view, and paths through the state graph are available. This example embodies the idea that a symbolic evolution—long studied by Niederman—can be grounded in tangible 3D geometries, providing an interactive exploration of how abstract strings translate into spatial structures. LEGOMultiwayExplorer[rules_, init_, steps_] := DynamicModule[{g, coords, vertices, colors, currentState = init, path = {}, vertexColor1}, g = ResourceFunction["MultiwaySystem"][rules, init, steps, "StatesGraph"]; coords = GraphEmbedding[ Graph3D[g, GraphLayout -> "SpringElectricalEmbedding"]]; vertices = VertexList[g]; colors = Hue /@ Rescale[StringLength /@ vertices]; vertexColor1 = First[colors]; Column[{Dynamic[ Graphics3D[{EdgeForm[Thin], MapThread[{If[MemberQ[path, #1], Yellow, If[#1 === First[vertices], vertexColor1, #2]], Cuboid[#3 - {0.4, 0.4, 0.2}, #3 + {0.4, 0.4, 0.2}], If[#1 == currentState, {Red, Sphere[#3, 0.3]}, {}]} &, {vertices, colors, coords}]}, Boxed -> True, Lighting -> "Neutral", ImageSize -> 400, SphericalRegion -> True, PlotLabel -> Style["LEGO Multiway System Explorer", 16]]], Row[{Button["Reset", currentState = init; path = {}, ImageSize -> Medium], PopupMenu[ Dynamic[currentState, (currentState = #; AppendTo[path, #]) &], vertices], ColorSlider[Dynamic[vertexColor1], ImageSize -> {200, 40}]}, Spacer[20]]}]] LEGOMultiwayExplorer[{"A" -> "AB", "B" -> "BA"}, "A", 4] It turns out that we can compute a state graph of symbolic evolutions and assign each vertex a 3D coordinate based on its graph depth. The resulting voxels are placed according to these coordinates, and edges between states are rendered as tubes. This creates a “layered” display that combines state transitions with spatial embedding. Multiway3DVisualization[rules_, init_, steps_] := Module[{g, vertices, edges, depths, coordMap, counter, coords, voxels, colors, tubes, plotLabel}, g = ResourceFunction["MultiwaySystem"][rules, init, steps, "StatesGraph"]; vertices = VertexList[g]; edges = EdgeList[g]; depths = AssociationMap[GraphDistance[g, init, #] &, vertices]; coordMap = <||>; counter = <||>; Do[depth = depths[v]; If[! KeyExistsQ[counter, depth], counter[depth] = 0]; coordMap[v] = {counter[depth], 0, depth}; counter[depth] += 1, {v, vertices}]; coords = Lookup[coordMap, vertices]; voxels = Map[Cuboid[# - {0.2, 0.2, 0.1}, # + {0.2, 0.2, 0.1}] &, coords]; colors = ColorData["Rainbow"] /@ Rescale[depths /@ vertices]; tubes = Map[With[{p1 = coordMap[#[[1]]], p2 = coordMap[#[[2]]]}, {GrayLevel[0.3], Tube[{p1, p2}, 0.02]}] &, edges]; Graphics3D[{EdgeForm[Thin], MapThread[{#1, #2} &, {colors, voxels}], tubes}, Boxed -> True, Lighting -> "Neutral", Axes -> True, AxesLabel -> {"X", "Y", "Generation"}, ViewVertical -> {0, 0, 1}, ViewPoint -> {2, 2, 2}]]; Multiway3DVisualization[{"A" -> "AB", "B" -> "A"}, "A", 4] MultiwayQuantumLattice further redoes this concept by computing the much-coveted, quantum-inspired lattice. Each node is assigned a quantum “density” based on its symbolic content (for example, the proportion of ones in a binary string). We've got our color mixer as well as the 3D spring-electrical layout that we use to highlight the connections between symbolic states. Together, these tools show albeit as slowly as possible that both multiway and quantum lattice systems can join different facets of complex symbolic dynamics in three dimensions. colorAssociation = <|0 -> White, 1 -> RGBColor[0.2, 0.4, 0.9], 2 -> RGBColor[0.3, 0.7, 0.9], 3 -> RGBColor[0.1, 0.8, 0.3], _ -> RGBColor[0.9, 0.2, 0.1] |>; SandStep[s_] := s + ListConvolve[{{0, 1, 0}, {1, -4, 1}, {0, 1, 0}}, UnitStep[s - 4], 2, 0]; DynamicModule[{grid = ConstantArray[0, {41, 41}], running = False}, grid[[20, 20]] = 5000; Column[{Button[If[running, "Stop", "Start"], running = ! running, ImageSize -> {100, 30}], Dynamic[If[running, grid = SandStep[grid]; Pause[0.01];]; ArrayPlot[grid, ColorRules -> Normal[colorAssociation], PixelConstrained -> 2, PlotRangePadding -> 0, ImageSize -> 500, Epilog -> {Text[Style["Click to add sand!", 12, Black], Scaled[{0.5, 0.03}]]}]], Button["Add Sand", grid += RandomInteger[1, {41, 41}]*5;, ImageSize -> {100, 30}]}, Alignment -> Center]] A sandpile model is ordered in two dimensions. Here the grid starts with a high concentration of “grains” at a central point..the SandStep function, using convolution with a kernel, simulates the redistribution of sand when a cell exceeds a critical threshold. The evolution of the sandpile is then animated interactively, with quite similar buttons for starting, adding extra sand, or resetting the grid. Additional versions show an Abelian sandpile model experientially evolving over time, complete with statistics and multiway state transitions that can be visualized as graphs. GetRowCol[s_, i_] := Module[{dim, row, col}, dim = Length[s]; row = Ceiling[i/dim]; col = i - (row - 1)*dim; {row, col}]; ReplaceHeight2D[s_, {row_, col_}, diff_] := If[row < 1 || row > Length[s] || col < 1 || col > Length[s[[row]]], s, ReplacePart[s, {row, col} -> s[[row, col]] + diff]]; ChangeGrain2D[s_List, {}] := s; ChangeGrain2D[s_List, vertexDiff_List] := Module[{vertex, row, col, diff, newList}, vertex = Keys[vertexDiff[[1]]]; {row, col} = vertex; diff = Values[vertexDiff[[1]]]; newList = ReplaceHeight2D[s, {row, col}, diff]; ChangeGrain2D[newList, Rest[vertexDiff]]]; IsStable2D[s_, zc_] := AllTrue[Flatten[s], # < zc &]; Stabilize2D[s_, zc_] := Module[{i, dim, row, col, assoc, dec}, i = FirstPosition[Flatten[s], _?(# >= zc &)][[1]]; dim = Length[s]; {row, col} = GetRowCol[s, i]; assoc = Join[{{row, col} -> -4}, If[row > 1, {{row - 1, col} -> 1}, {}], If[col > 1, {{row, col - 1} -> 1}, {}], If[row < dim, {{row + 1, col} -> 1}, {}], If[col < dim, {{row, col + 1} -> 1}, {}]]; dec = ChangeGrain2D[s, assoc]; If[IsStable2D[dec, zc], dec, Stabilize2D[dec, zc]]]; IndexPossibility2D[s_, i_, zc_] := Module[{row, col, newList}, {row, col} = GetRowCol[s, i]; newList = ChangeGrain2D[s, {{row, col} -> 1}]; If[IsStable2D[newList, zc], newList, Stabilize2D[newList, zc]]]; PossibilityGenerator2D[s_, zc_] := DeleteDuplicates[ Table[IndexPossibility2D[s, i, zc], {i, Length[Flatten[s]]}]] init = {{{0, 0}, {0, 0}}, {{0, 0}, {0, 0}}}; graph = ResourceFunction["NestGraphTagged"][ PossibilityGenerator2D[#, 2] &, init, 3, "StateLabeling" -> True, VertexShapeFunction -> (Framed[ArrayPlot[#2, Mesh -> All], FrameStyle -> LightGray] &), GraphLayout -> "LayeredDigraphEmbedding", VertexSize -> 1.5, ImageSize -> Full]; Labeled[graph, Column[{Style["Sandpile Multiway System", 16], Row[{"Vertex count: ", VertexCount[graph]}], Row[{"Edge count: ", EdgeCount[graph]}], Row[{"Average degree: ", N@Mean[VertexDegree[graph]]}]}, Alignment -> Center], Top] I hope this answers all your questions..we could even add a 2D cellular automaton reminiscent of forest-fire models. A grid is initialized with a given density of “trees” (active cells) and an ignited cell in the center. A custom rule set governs the spread (or death) of trees based on their neighbors. After all that, an interface with clickable toggles allows the user to modify the grid and step through the evolution, providing insight into how local interactions give rise to a million, complex patterns. grid = CenterArray[0, {21, 21}]; grid[[10, 10]] = 100; colorAssociation = <|0 -> White, 1 -> RGBColor[0.9, 0.9, 1], 2 -> RGBColor[0.7, 0.7, 1], 3 -> RGBColor[0.4, 0.4, 1], 4 -> RGBColor[0.2, 0.2, 1], _ -> Blue|>; SandStep[grid_] := Module[{newgrid = grid}, Do[If[newgrid[[i, j]] >= 4, newgrid[[i, j]] -= 4; If[i > 1, newgrid[[i - 1, j]] += 1]; If[i < 21, newgrid[[i + 1, j]] += 1]; If[j > 1, newgrid[[i, j - 1]] += 1]; If[j < 21, newgrid[[i, j + 1]] += 1];], {i, 21}, {j, 21}]; newgrid]; sandpileEvolution = NestList[SandStep, grid, 45]; Animate[ArrayPlot[sandpileEvolution[[frame]], ColorRules -> colorAssociation, PlotRange -> {0, 4}, PixelConstrained -> 10, Epilog -> {Text[Style["Step: " <> ToString[frame - 1], Bold, 18], Scaled[{0.5, 0.95}]], Text[Style["2D Abelian Sandpile Model", Bold, 14], Scaled[{0.5, 0.02}]]}], {frame, 1, Length[sandpileEvolution], 1}, AnimationRepetitions -> 1, AnimationRate -> 5, DisplayAllSteps -> True] This version uses a 3D grid where each cell’s “height” is updated based on its surrounding cells. The evolving 3D structure is then rendered using ListPlot3D in an animated fashion, offering a spatial perspective on the sandpile dynamics. It's one thing to just go between mechanisms and say "binary transitions" or quantum lattice growth, voxel transformations and cellular automata, or even sandpile models..but the common theme that binds us is the translation of symbolic rules into concrete spatial or graphical evolutions. SandStep[s_List, zc_Integer] := Module[{dims = Dimensions[s], new = s}, Do[If[s[[i, j]] >= zc, new[[i, j]] -= 4; Do[If[1 <= x <= dims[[1]] && 1 <= y <= dims[[2]], new[[x, y]] += 1], {x, i - 1, i + 1}, {y, j - 1, j + 1}]], {i, dims[[1]]}, {j, dims[[2]]}]; new]; GenerateStates[init_List, zc_Integer, steps_Integer] := NestWhileList[SandStep[#, zc] &, init, UnsameQ[##] &, 2, steps]; MultiwaySandpileGraph[init_List, zc_Integer, steps_Integer] := Module[{states, transitions, graphRules}, states = GenerateStates[init, zc, steps]; transitions = DirectedEdge @@@ Partition[states, 2, 1]; graphRules = {VertexShapeFunction -> ({EdgeForm[Black], Inset[ArrayPlot[#2, ColorRules -> {0 -> White, 1 -> Blue, 2 -> Yellow, 3 -> Red}, Frame -> False, AspectRatio -> Automatic, ImageSize -> {40, 40}], #1, Center, Automatic]} &), VertexSize -> 1.5, EdgeStyle -> Directive[Gray, Thickness[0.003]], GraphLayout -> "SpringElectricalEmbedding", PerformanceGoal -> "Quality"}; Graph[transitions, graphRules]]; Manipulate[ Module[{grid = CenterArray[{{initialGrains}}, {gridSize, gridSize}]}, MultiwaySandpileGraph[grid, criticalThreshold, steps]], {{gridSize, 5, "Grid Size"}, 3, 7, 1, Appearance -> "Labeled"}, {{criticalThreshold, 4, "Critical Threshold"}, 3, 6, 1, Appearance -> "Labeled"}, {{initialGrains, 100, "Initial Grains"}, 50, 500, 50, Appearance -> "Labeled"}, {{steps, 5, "Evolution Steps"}, 1, 10, 1, Appearance -> "Labeled"}, ControlPlacement -> Left, FrameLabel -> {{None, None}, {None, Style["Multiway Sandpile Evolution", Bold, 14]}}] By moving from abstract symbolic systems to physical, often voxel-based representations, hopefully this segment embodies Niederman’s vision of concretizing tag system rules. Whether using interactive graphs, animated 2D plots, or 3D spatial arrangements, these examples provide multiple frames of reference to explore complex system dynamics interactively and visually. Stabilize2D[s_, zc_] := Module[{pos, dim, row, col, assoc, dec}, pos = FirstPosition[Flatten[s], _?(# >= zc &)]; If[pos === Missing["NotFound"], Return[s]]; pos = pos[[1]]; dim = Length[s]; {row, col} = QuotientRemainder[pos - 1, dim] + {1, 1}; assoc = Join[{{row, col} -> -zc}, If[row > 1, {{row - 1, col} -> 1}, {}], If[col > 1, {{row, col - 1} -> 1}, {}], If[row < dim, {{row + 1, col} -> 1}, {}], If[col < dim, {{row, col + 1} -> 1}, {}]]; dec = ReplacePart[s, assoc]; If[Max[Flatten[dec]] < zc, dec, Stabilize2D[dec, zc]]]; SandpileStep[s_, zc_] := Module[{i, j, newS}, {i, j} = RandomInteger[{1, Length[s]}, 2]; newS = ReplacePart[s, {i, j} -> s[[i, j]] + 1]; Stabilize2D[newS, zc]]; grid = ConstantArray[0, {21, 21}]; evolution = NestList[SandpileStep[#, 4] &, grid, 600]; ListAnimate[ ArrayPlot[#, ColorFunction -> "SunsetColors", ImageSize -> Medium] & /@ evolution, AnimationRepetitions -> Infinity] It's one thing to have an iterative stabilization procedure for a two-dimensional sandpile model..we can have a function that scans the grid s for any cell whose value meets or exceeds the critical threshold zc. Once such a cell is found, it computes its row and column indices using a quotient–remainder calculation. It then “topples” the cell by subtracting zc from it and adding 1 grain to each of its four (up/down/left/right) neighbors. The process is repeated recursively until no cell exceeds the threshold. Then in SandpileStep[s, zc], a random cell is chosen, and one grain is added to it. Then, the grid is stabilized using the procedure described above. This models a single update step of the sandpile evolution. The evolution of the grid is generated by iteratively applying SandpileStep (600 iterations in the example). An animated ArrayPlot uses a color scheme (here, “SunsetColors”) to visualize the discrete snippets of time as the sandpile evolves. colorAssociation = <|0 -> White, 1 -> RGBColor[0.2, 0.6, 1], 2 -> RGBColor[0.1, 0.8, 0.3], 3 -> RGBColor[1, 0.4, 0.2], _ -> Black|>; SandStep[s_] := s + ListConvolve[{{0, 1, 0}, {1, -4, 1}, {0, 1, 0}}, UnitStep[s - 4], 2, 0]; initialGrid = CenterArray[1000, {41, 41}]; evolution = NestList[SandStep, initialGrid, 100]; ListAnimate[ ArrayPlot[#, ColorRules -> colorAssociation, Mesh -> True, MeshStyle -> Directive[Gray, Thickness[0.005]], Frame -> False, PlotRangePadding -> 0, ImageSize -> 500] & /@ evolution, AnimationRepetitions -> Infinity] A second, much understated example uses a different update rule based on convolution..in this variation, the grid is updated by adding a correction computed with a convolution kernel (which here is designed to mimic the redistribution process when a cell topples) and using UnitStep to identify where the sand exceeds the threshold (in this case, 4). The system starts with a central cell given a high value (e.g., 1000 grains) and evolves for a set number of steps (e.g., 100 iterations). The evolution is animated using ArrayPlot with a specified color association that maps different values to colors, making the toppling and propagation visually apparent. It almost looks like a breakfast item, however we don't have to use the same color scheme. It turns out that we can shift focus from a sandpile to models that simulate different types of spreading dynamics. width = 50; height = 50; density = 0.6; ignitionProb = 0.0001; growthProb = 0.01; stateColors = <|0 -> Gray, 1 -> Darker[Green], 2 -> Orange|>; currentGrid = Table[If[RandomReal[] < density, 1, 0], {height}, {width}]; currentGrid[[height/2, width/2]] = 2; EvolveGrid[grid_] := Module[{newGrid = grid}, Do[If[grid[[i, j]] == 2, newGrid[[i, j]] = 0; Do[Do[If[ 1 <= x <= height && 1 <= y <= width && grid[[x, y]] == 1, newGrid[[x, y]] = 2], {x, i - 1, i + 1}], {y, j - 1, j + 1}]], {i, height}, {j, width}]; Do[If[grid[[i, j]] == 0 && RandomReal[] < growthProb, newGrid[[i, j]] = 1]; If[grid[[i, j]] == 1 && RandomReal[] < ignitionProb, newGrid[[i, j]] = 2], {i, height}, {j, width}]; newGrid]; DynamicModule[{grid = currentGrid}, Column[{Button["Reset", grid = currentGrid], Dynamic[ArrayPlot[grid, ColorRules -> Normal[stateColors], PixelConstrained -> 10, Frame -> False, ImageSize -> 300]], Button["Next Step", grid = EvolveGrid[grid], ImageSize -> 300]}]] So everybody knows about sand piles. Now, when we simulate forest fires temporally, we start out with some specified tree density. One cell in the center is ignited (set to a distinct state, here indicated by the value 2). Cells with fire (value 2) are EvolveGridded to extinguish, and the fire spreads to any neighboring cell that has a tree (value 1). In addition, empty cells (value 0) may grow trees with a small probability, and trees (value 1) may spontaneously ignite, the definition of the stochastic nature of a forest-fire model. We could reset the grid or we could advance the simulation one step at a time. stateColors = <|0 -> White, 1 -> Black, 2 -> Red, 3 -> Blue|>; InitializeGrid[size_, density_] := Table[RandomChoice[{1 - density, density} -> {0, 1}], {size}, {size}]; ApplyCellularRules[grid_] := Module[{neighbors, newGrid, size = Length[grid]}, neighbors = ListConvolve[{{1, 1, 1}, {1, 0, 1}, {1, 1, 1}}, grid, {2, 2}, 0]; newGrid = Table[Which[ grid[[i, j]] == 1 && (neighbors[[i, j]] < 2 || neighbors[[i, j]] > 3), 3, grid[[i, j]] == 0 && neighbors[[i, j]] == 3, 2, True, grid[[i, j]] /. {2 -> 1, 3 -> 0}], {i, size}, {j, size}]; newGrid]; DynamicModule[{grid = InitializeGrid[50, 0.2], running = False, size = 50}, Panel[Column[{Grid[{{Dynamic[ ArrayPlot[grid, ColorRules -> stateColors, PixelConstrained -> 2, Background -> Yellow, Epilog -> {Text[Style["Click to toggle!", 12, Blue], Scaled[{0.5, 0.9}]], Text[Style[ ToString[Count[Flatten[grid], 1]] <> " living cells", 12, Blue], Scaled[{0.5, 0.07}]]}]]}, {Panel[ Column[{Button["Next Step", grid = ApplyCellularRules[grid], ImageSize -> {80, 30}], Button["Randomize", grid = InitializeGrid[size, 0.2], ImageSize -> {87, 30}], Button["Clear", grid = ConstantArray[0, {size, size}], ImageSize -> {80, 30}]}], "Controls", ImageSize -> {100, 120}]}}], DynamicWrapper[Graphics[{}, ImageSize -> 1], If[CurrentValue["MouseOver"], With[{pos = MousePosition["Graphics"]}, If[ListQ[pos] && ! running, With[{x = Clip[Round[size - pos[[2]]], {1, size}], y = Clip[Round[pos[[1]] + 1], {1, size}]}, If[CurrentValue["MouseClickTest"], grid[[x, y]] = 1 - grid[[x, y]];]]]]]]}, Alignment -> Center]], TrackedSymbols :> {running}, Initialization :> (If[running, grid = ApplyCellularRules[grid]; Pause[0.1]])] Stabilizing a 2-dimensional sandpile requires an iterative stabilization procedure for a two-dimensional sandpile model. There's a bit of another version where you add a scaled contribution (multiplied by 2) from a convolution kernel and use an EventHandler within a DynamicModule. In this setup, users can click on cells (using the mouse in the ArrayPlot) to add grains and then watch the system evolve automatically through a series of stabilization steps. SandStep[s_] := Module[{topple, add}, topple = UnitStep[s - 4]; add = ListConvolve[{{0, 1, 0}, {1, 0, 1}, {0, 1, 0}}, topple, 2, 0]; s - 4*topple + add]; colorAssociation = <|0 -> White, 1 -> Blue, 2 -> Cyan, 3 -> Green, 4 -> Brown, 5 -> Orange, 6 -> Red, 7 -> Magenta, 8 -> Purple|>; size = 61; initialGrid = ConstantArray[0, {size, size}]; initialGrid[[Ceiling[size/2], Ceiling[size/2]]] = 2000; steps = 1200; simulation = NestList[SandStep, initialGrid, steps]; Animate[ArrayPlot[simulation[[frame]], ColorRules -> Normal[colorAssociation], PlotRange -> {0, 8}, Frame -> False, ImageSize -> 500], {frame, 1, Length[simulation], 1}, AnimationRepetitions -> 1, RefreshRate -> 10] Yet another interactive simulation revisits the sandpile model using a dynamic interface. A grid is initialized with sand (or trees, or empty spaces) and updated interactively as the user clicks on the display. There's a small lift, a segue between resetting the grid, randomizing the grid, or clearing it—all while the simulation visually updates the number of “living cells” (trees) and true state. SandStep[s_] := s + 2*ListConvolve[{{0, 1, 0}, {1, -4, 1}, {0, 1, 0}}, UnitStep[s - 4], 2, 0] DynamicModule[{grid = ConstantArray[0, {41, 41}], step = 0, colors = {GrayLevel[0.8], Blue, Orange, Red, Darker@Red}}, Column[{Row[{Button[ "Reset", (grid = ConstantArray[0, {41, 41}]; step = 0;), ImageSize -> 100], Button["Random Seed", (grid = RandomInteger[3, {41, 41}]; step = 0;), ImageSize -> 140]}], Dynamic[EventHandler[ ArrayPlot[grid, ColorFunction -> Function[x, Which[x == 0, colors[[1]], 1 <= x < 4, colors[[2]], x == 4, colors[[3]], 5 <= x < 8, colors[[4]], x >= 8, colors[[5]]]], ColorFunctionScaling -> False, PlotRangePadding -> 0, ImageSize -> 500, Epilog -> {Text[Style["Step: " <> ToString[step], Bold], Scaled[{0.1, 0.95}]]}], "MouseClicked" :> (With[{pos = MousePosition["Graphics"] /. Missing["KeyAbsent", _] :> {0.5, 0.5}}, With[{center = Round@First@ Nearest[Tuples[Range /@ Dimensions[grid]], pos*Reverse@Dimensions[grid] + 0.5, 1]}, grid[[center[[1]], center[[2]]]] += 1; grid = NestWhile[Function[g, step++; SandStep[g]], grid, UnsameQ, 2, 50];]])]]}]] Each of these examples makes us more prepared for our sandpile models, to momentarily focus on the way local changes (adding grains and toppling) create complex, evolving patterns. Whereas the forest-fire simulations semantically chip away at probabilistic spread and regrowth, adding a stochastic element. But it's the cellular automata I've got to watch out for--these are the automata that illustrate classic neighbor-based rule evolutions that lead to newfound structures. InitializeSandpile3D[size_, initialHeight_] := CenterArray[{{{initialHeight}}}, {size, size, size}]; SandStep3D[s_] := s + ListConvolve[{{{0, 0, 0}, {0, 1, 0}, {0, 0, 0}}, {{0, 1, 0}, {1, -6, 1}, {0, 1, 0}}, {{0, 0, 0}, {0, 1, 0}, {0, 0, 0}}}, UnitStep[s - 6], 2, 0]; Module[{evolution, colorScheme}, evolution = NestList[SandStep3D, InitializeSandpile3D[15, 100], 50]; colorScheme = "SolarColors"; ListAnimate[ MapIndexed[ ListPlot3D[#1, ColorFunction -> Function[{x, y, z}, ColorData[colorScheme][z]], ColorFunctionScaling -> False, PlotRange -> All, DataRange -> {{-1, 1}, {-1, 1}}, Mesh -> True, Boxed -> False, Axes -> True, SphericalRegion -> True, ImageSize -> 600, PlotLabel -> Style["Step " <> ToString[#2[[1]]], 18, Bold]] &, evolution], AnimationRate -> 2, ControlPlacement -> Top]] For the 3D extension, the function InitializeSandpile3D creates a three-dimensional grid with a central “height” (or number of grains) at the middle voxel. The function SandStep3D uses a three-dimensional convolution kernel that mimics the toppling process in three dimensions. The evolution is then visualized using a series of 3D plots (via ListPlot3D), which are animated to show how the 3D sandpile evolves over a sequence of steps. Different color schemes (such as “SolarColors”) slide through various perceptions of height and depth. But it's really the iterative application of localized rules, that along with proper stabilization techniques, that yield behavior that shines through both two-dimensional and three-dimensional models and, reinforces the connection between the algorithms and their visual output.