Zurück zur Startseite

Glühwürmchen-Synchronisation in Wolfram Language

Der Artikel beschreibt die Implementierung des Glühwürmchen-Synchronisationsmodells in Wolfram Language. Übergang vom agentenbasierten Modell zum vektorierten zellulären Automaten mit Faltungen ListConvolve. Rendering-Optimierung über NumericArray und Videogenerierung. Demonstriert emergentes Verhalten an Beispielen mit 2–200 Agenten und 50×50 Feldern.

Glühwürmchen synchronisieren sich: Zellulärer Automat-Code
Advertisement 728x90

Feuerfliegen-Synchronisation: Von Agentenmodellen zu Zellularautomaten in Wolfram Language

Feuerfliegen synchronisieren ihre Lichtblitze durch lokale Wechselwirkungen. Jeder Agent verfügt über einen internen Timer im Bereich [0,1], der pro Schritt um 0,01 abnimmt. Sobald er den Wert 0 erreicht, erfolgt ein Blitz (Zustand 1), der Nachbarn innerhalb eines festgelegten Radius beeinflusst.

Die zentrale Mechanik ist die adaptive Phasenkorrektur: Wenn ein Nachbar blitzt, verschiebt sich der aktuelle Timer je nach Position im Zyklus vorwärts oder rückwärts. Die Richtung wird durch Sign[2 Round[state] - 1] bestimmt: negativ für Zustände nahe bei 0, positiv für solche nahe bei 1.

Agentenmodell: Erzeugung und Verbindungen

Zunächst werden n Agenten in einem Bereich platziert und mit zufälligen Ausgangszuständen versehen:

Google AdInline article slot
 generateFireFlies[n_:200, region_:Rectangle[{-10,-10}, {10,10}]] := fireFlies = With[{ 
  pos = RandomPoint[region, n]
},
  Transpose[{pos, Table[RandomReal[{0,1.0}], {Length[pos]}]}]
];

Jeder Feuerfliege ist ein Paar {Position, Zustand}. Die Verbindungs-Matrix wird mittels euklidischer Distanz berechnet:

bakeConnections[r_:3.7] := (
  connectionMatrix = Table[
    If[i == j, Infinity, Power[Norm[i[[1]] - j[[1]]], 2]]
  , {i, fireFlies}, {j, fireFlies}];

  connectionMatrix = MapIndexed[
    Function[{value, index},
      If[value < r, index[[1]], Nothing]
    ],
    #
  ] & /@ connectionMatrix
);

Kernoperationen pro Schritt:

  • Abklingen: Clip[Zustand - 0,01, {0,1}]
  • Blitz: wenn Zustand == 0 → 1
  • Korrektur: falls Nachbar im Zustand 1 ist, Zustand + Sign[2 Round[Zustand] - 1] * 0,0001

Aktualisierung: fireFlies = MapIndexed[adjust, decay /@ flash /@ fireFlies];

Google AdInline article slot

Test auf kleiner Skala

Bei zwei Agenten dauert die Synchronisation etwa 10 Zyklen. Zustandsdiagramme zeigen die Konvergenz:

generateFireFlies[2, Circle[{0,0},0.5]];
bakeConnections[2.0];

Visualisierung mittels Animation via EventHandler[AnimationFrameListener[...]] demonstriert die Phasenausrichtung.

Bei 200 Agenten werden farbige Kreise verwendet:

Google AdInline article slot
cf = Blend[{Darker[Red], Yellow}, #] &;
Graphics[{ 
  Table[
    With[{i = i, xy = fireFlies[[i,1]]},
      {RGBColor[colors[[i]]], Disk[xy, 0.3]}
    ], {i, Length[fireFlies]}
  ],
  EventHandler[AnimationFrameListener[colors], update]
}, Background->Black]

Das Verdoppeln einer Ebene mit leichter Verschmierung erzeugt einen leuchtenden Effekt.

Übergang zum Zellularautomaten

Die Logik ähnelt dem Game of Life, unterscheidet sich jedoch durch kontinuierliche Zustände. Ein 25×25-Gitter wird initialisiert mit RandomReal[{0,1.01}, {25,25}].

Blitze werden über Floor[Feld] erkannt. Die Nachbarbeeinflussung nutzt einen 3×3-Faltungskern:

ListConvolve[
  {{1,0,1,0,1}, {1,0,0,0,1}, {1,0,1,0,1}},
  Floor[Feld],
  2, 0
]

Korrektur: (2.0 Round[Feld] - 1.0) * Clip[Faltung, {0, 0.001}]. Abklingen-Blitz: Map[Clip[# - 0.01, {0,0,1,0}, {1,0,1,0}], f, {2}].

Gesamtschleife in Refresh bei 30 FPS:

Module[{f = RandomReal[{0,1.01}, {25,25}]}, Refresh[
  f = Map[Clip[# - 0.01, {0,0,1,0}, {1,0,1,0}]&, f, {2}];

  f = Clip[
    f + (2.0 Round[f] - 1.0)
      Clip[
        ListConvolve[
          {{1,0,1,0,1}, {1,0,0,0,1}, {1,0,1,0,1}},
          Floor[f],
          2, 0
        ], {0, 0.001}
      ],
    {0,1}
  ];

  ArrayPlot[f, Frame->True, ColorFunction->"PlumColors"]
, 1/30]]

Wellenartige Muster entstehen über die Iterationen.

Leistungssteigerung

ArrayPlot in Refresh funktioniert gut für Prototypen, doch für größere Skalen empfiehlt sich NumericArray und Image:

field = RandomReal[{0,1.0}, {50,50}];

render = Function[Null,
  Do[
    field = Map[Clip[# - 0.01, {0,0,1,0}, {1,0,1,0}]&, field, {2}];
    field = Clip[
      field + (2.0 Round[field] - 1.0)
        Clip[ListConvolve[{{1,1,1},{1,0,1},{1,1,1}}, Floor[field], 2, 0], {0,0.001}],
      {0,1}
    ];
  , {2}];

  imageBuffer = NumericArray[255.0 field, "Byte", "ClipAndRound"];
];

Image[imageBuffer, "Byte", Epilog -> EventHandler[AnimationFrameListener[imageBuffer], render], Magnification -> 20]

Auf einem 50×50-Gitter erscheinen radarähnliche Strukturen.

Videogenerierung

Für Export:

movie = Table[
  render[];
  ImageResize[Colorize[Image[imageBuffer]], Scaled[6], Method->"NearestNeighbor"],
  {600}
];
FrameListVideo[movie, FrameRate->60]

Das Modell skaliert von agentenbasierten Systemen zu GPU-freundlichen Faltungen und zeigt emergentes Verhalten.

Wichtige Erkenntnisse

  • Lokale Regeln (Abklingen + adaptive Korrektur) führen zu globaler Synchronisation.
  • Agentenmodell → Zellularautomat: Faltung ersetzt Nachbar-Loops.
  • Sign[2 Round[Zustand] - 1] ermöglicht bidirektionale Phasenanpassung.
  • NumericArray + Image optimieren die Darstellung großer Felder.
  • Rechteckiger Faltungskern erzeugt quadratische Muster; für Realismus sind Gauß-Kerne vorzuziehen.

— Editorial Team

Advertisement 728x90

Weiterlesen