{
"cells": [
{
"cell_type": "markdown",
"id": "f38d0a5c",
"metadata": {
"papermill": {
"duration": 0.0,
"end_time": "2026-05-27T13:15:11.650833+00:00",
"exception": false,
"start_time": "2026-05-27T13:15:11.650833+00:00",
"status": "completed"
},
"tags": []
},
"source": [
"# 16 · Cellular Automata — grid of LLM cells with local rules → emergent behaviour\n",
"\n",
"> **TL;DR.** Each cell on an H×W grid is an LLM agent. At every time step, every cell reads its own state + its 4-neighbours' states and calls a `with_structured_output(_CellUpdate)` LLM to decide its next state. Global behaviour *emerges* from local decisions — fires spread, opinions diffuse, populations grow / decay.\n",
">\n",
"> **Reach for it when** you want to study spatial / decentralised emergence: forest-fire dynamics, opinion polarisation, infection spread, agent-based simulation.\n",
"> **Avoid when** centralised reasoning would do the same job cheaper — CA is *expensive* (`grid_cells × steps` LLM calls).\n",
"\n",
"| Property | Value |\n",
"|---|---|\n",
"| Origin | Cellular automata (von Neumann 1966; Conway's *Game of Life* 1970); LLM-as-rule variant |\n",
"| Neighbourhood | Von Neumann (4-neighbour) by default; easy to extend to Moore (8-neighbour) |\n",
"| Cost | `H × W × steps` LLM calls. **4×4 × 3 steps = 48 calls** for this demo. |\n",
"| Synchronous? | Yes — all cells update simultaneously each step (next_grid built from old_grid) |\n",
"| Structured output | Yes — `_CellUpdate(next_state, reason)` per cell |\n",
"\n",
"This is the **most expensive** architecture in the repo per task. Keep grids tiny (≤ 5×5) and step counts low (≤ 5) for educational use. Production CA simulations switch to hard-coded transition rules and use the LLM only for *interpreting* the result."
]
},
{
"cell_type": "markdown",
"id": "732e14be",
"metadata": {
"papermill": {
"duration": 0.000977,
"end_time": "2026-05-27T13:15:11.668371+00:00",
"exception": false,
"start_time": "2026-05-27T13:15:11.667394+00:00",
"status": "completed"
},
"tags": []
},
"source": [
"## 2 · Architecture at a glance\n",
"\n",
"```mermaid\n",
"flowchart LR\n",
" G([initial grid]) --> S[Step
for each cell:
read self + 4 neighbours
→ LLM update
build new_grid synchronously]\n",
" S --> Q{step < max?}\n",
" Q -->|yes| S\n",
" Q -->|no| Z([final grid + history])\n",
"\n",
" style S fill:#fff3e0,stroke:#f57c00\n",
"```\n",
"\n",
"**Single-node loop.** Each iteration of `_step` makes `H × W` LLM calls (one per cell). The router halts after `max_steps`. The full grid history is preserved for analysis."
]
},
{
"cell_type": "markdown",
"id": "6b35966c",
"metadata": {
"papermill": {
"duration": 0.008229,
"end_time": "2026-05-27T13:15:11.686721+00:00",
"exception": false,
"start_time": "2026-05-27T13:15:11.678492+00:00",
"status": "completed"
},
"tags": []
},
"source": [
"## 3 · Theory\n",
"\n",
"### 3.1 · Why LLM cells, not hard-coded rules?\n",
"\n",
"Classic cellular automata (Conway's Life, the abelian sandpile, lattice gases) use *hard-coded transition functions*: given the cell's state and neighbour states, lookup the next state. Tiny, fast, mathematically clean.\n",
"\n",
"LLM cells trade off speed for **rule flexibility**. You can write the transition rule in **English** (\"a tree cell catches fire if any neighbour is fire\") and the LLM applies it. You can encode rich states beyond `{alive, dead}` — *\"trust=0.8\"*, *\"opinion=optimistic\"*, *\"infection=mild\"*. You can change the rule mid-simulation by editing the prompt.\n",
"\n",
"This is useful for:\n",
"- Rapid prototyping of agent-based models.\n",
"- Simulations where rules are expressed naturally as descriptions rather than equations.\n",
"- Hybrid systems where some rules are hard-coded and some are LLM-driven.\n",
"\n",
"### 3.2 · Synchronous update — why the new_grid is built from the OLD grid\n",
"\n",
"```python\n",
"for r, c in cells:\n",
" new_grid[r][c] = llm_decide(old_grid[r][c], neighbours_in_old_grid)\n",
"# Then atomically swap: grid = new_grid\n",
"```\n",
"\n",
"Critical detail: **all cells decide based on the OLD grid**. If cell (0,1) updated *during* the step and cell (0,2) used the *new* (0,1) state, the dynamics depend on iteration order and lose the symmetry CA relies on.\n",
"\n",
"### 3.3 · The deterministic clamp on `next_state`\n",
"\n",
"The `_CellUpdate` schema constrains `next_state` to a string, but doesn't enforce it must be one of the `allowed_states`. If the LLM emits a state outside the allowed list, we **clamp** to the current state (no change):\n",
"\n",
"```python\n",
"next_state = update.next_state if update.next_state in self.allowed_states else grid[r][c]\n",
"```\n",
"\n",
"This is the deterministic-picker pattern at the cell level — Python silently corrects when the LLM goes off-script.\n",
"\n",
"### 3.4 · Where this sits\n",
"\n",
"| Pattern | Coordination | Best for |\n",
"|---|---|---|\n",
"| Cellular Automata *(this notebook)* | spatial / local-rule | emergent spatial dynamics |\n",
"| Multi-Agent (nb 05) | central supervisor | task spans domains |\n",
"| Blackboard (nb 07) | distributed bidding | opportunistic collaboration |\n",
"| Ensemble (nb 13) | parallel voters | wisdom of crowds |\n",
"\n",
"Compared to Blackboard's *bidding*-based dynamic team, CA is *fully local* — each cell only sees its 4 neighbours. No global coordination at all.\n",
"\n",
"### 3.5 · What goes wrong (you'll see in § 9)\n",
"\n",
"1. **Cost** — `H × W × steps` calls is brutal. A 5×5 × 5 steps run = 125 calls per simulation.\n",
"2. **Rule-prompt sensitivity** — small wording changes can flip the macro behaviour entirely. Test your rule on a 3×3 first.\n",
"3. **LLM drift** — over many steps, the LLM's interpretation of the rule drifts. Conway's Life run by an LLM doesn't look quite like Conway's Life.\n",
"4. **No batching by default** — every cell is one LLM call. Production would batch all 16 cells into one structured-output call per step (extension idea § 11.3)."
]
},
{
"cell_type": "markdown",
"id": "7463ba66",
"metadata": {
"papermill": {
"duration": 0.001487,
"end_time": "2026-05-27T13:15:11.695612+00:00",
"exception": false,
"start_time": "2026-05-27T13:15:11.694125+00:00",
"status": "completed"
},
"tags": []
},
"source": [
"## 4 · Setup"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "7157a633",
"metadata": {
"execution": {
"iopub.execute_input": "2026-05-27T13:15:11.711905Z",
"iopub.status.busy": "2026-05-27T13:15:11.711905Z",
"iopub.status.idle": "2026-05-27T13:15:13.103880Z",
"shell.execute_reply": "2026-05-27T13:15:13.103570Z"
},
"papermill": {
"duration": 1.400329,
"end_time": "2026-05-27T13:15:13.103880+00:00",
"exception": false,
"start_time": "2026-05-27T13:15:11.703551+00:00",
"status": "completed"
},
"tags": []
},
"outputs": [
{
"data": {
"text/html": [
"
Provider: nebius · Model: meta-llama/Llama-3.3-70B-Instruct ─────────────────────────────────────────────────────\n", "\n" ], "text/plain": [ "\u001b[1;36mProvider: nebius · Model: meta-llama/Llama-\u001b[0m\u001b[1;36m3.3\u001b[0m\u001b[1;36m-70B-Instruct\u001b[0m \u001b[92m─────────────────────────────────────────────────────\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from agentic_architectures import get_llm, enable_langsmith, settings\n", "from agentic_architectures.architectures import CellularAutomata\n", "from agentic_architectures.ui import print_md, print_header, print_step\n", "\n", "enable_langsmith()\n", "print_header(f\"Provider: {settings.llm_provider} · Model: {settings.llm_model}\")" ] }, { "cell_type": "markdown", "id": "a23c343b", "metadata": { "papermill": { "duration": 0.0, "end_time": "2026-05-27T13:15:13.111981+00:00", "exception": false, "start_time": "2026-05-27T13:15:13.111981+00:00", "status": "completed" }, "tags": [] }, "source": [ "## 5 · Library walkthrough\n", "\n", "Source: [`src/agentic_architectures/architectures/cellular_automata.py`](../src/agentic_architectures/architectures/cellular_automata.py).\n", "\n", "Five key pieces:\n", "\n", "1. **`__init__(rule_prompt, allowed_states, H, W, max_steps)`** — the rule is just a string the LLM reads each cell.\n", "2. **`_neighbours(grid, r, c)`** — pure Python, returns the 4-neighbourhood with `'edge'` for off-grid cells.\n", "3. **`_step`** — for each cell, builds a per-cell prompt (rule + own state + neighbours) and calls `_updater` with structured output.\n", "4. **Deterministic clamp** — if LLM emits a state outside `allowed_states`, keep the cell's current state (no change).\n", "5. **`run(task)`** — accepts an initial grid as a multi-line string (rows by `\\n`, cells by `|`)." ] }, { "cell_type": "code", "execution_count": 2, "id": "bfdb8d64", "metadata": { "execution": { "iopub.execute_input": "2026-05-27T13:15:13.130366Z", "iopub.status.busy": "2026-05-27T13:15:13.130366Z", "iopub.status.idle": "2026-05-27T13:15:13.145706Z", "shell.execute_reply": "2026-05-27T13:15:13.144559Z" }, "papermill": { "duration": 0.02104, "end_time": "2026-05-27T13:15:13.145706+00:00", "exception": false, "start_time": "2026-05-27T13:15:13.124666+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"description\": \"The next state a cell should transition to, given its neighbours.\",\n", " \"properties\": {\n", " \"next_state\": {\n", " \"description\": \"The cell's state for the NEXT time step. MUST be one of the allowed state labels listed in the prompt. Keep state labels SHORT (1-2 words).\",\n", " \"tit...\n" ] } ], "source": [ "from agentic_architectures.architectures.cellular_automata import _CellUpdate\n", "import json\n", "print(json.dumps(_CellUpdate.model_json_schema(), indent=2)[:300] + '...')" ] }, { "cell_type": "markdown", "id": "33dd8eb8", "metadata": { "papermill": { "duration": 0.008134, "end_time": "2026-05-27T13:15:13.153840+00:00", "exception": false, "start_time": "2026-05-27T13:15:13.145706+00:00", "status": "completed" }, "tags": [] }, "source": [ "## 6 · State\n", "\n", "| Field | Type | Set by |\n", "|---|---|---|\n", "| `grid` | `list[list[str]]` (HxW) | `_step` (each iteration) |\n", "| `history` | `list[grid]` | `_step` (appends old grid before update) |\n", "| `step` | `int` | `_step` |\n", "| `max_steps` | `int` | `__init__` |" ] }, { "cell_type": "markdown", "id": "50e9f5be", "metadata": { "papermill": { "duration": 0.008191, "end_time": "2026-05-27T13:15:13.162031+00:00", "exception": false, "start_time": "2026-05-27T13:15:13.153840+00:00", "status": "completed" }, "tags": [] }, "source": [ "## 7 · Build the graph" ] }, { "cell_type": "code", "execution_count": 3, "id": "9757585e", "metadata": { "execution": { "iopub.execute_input": "2026-05-27T13:15:13.171454Z", "iopub.status.busy": "2026-05-27T13:15:13.171454Z", "iopub.status.idle": "2026-05-27T13:15:16.996657Z", "shell.execute_reply": "2026-05-27T13:15:16.996657Z" }, "papermill": { "duration": 3.834626, "end_time": "2026-05-27T13:15:16.996657+00:00", "exception": false, "start_time": "2026-05-27T13:15:13.162031+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
Initial grid (step 0) ─────────────────────────────────────────────────────────────────────────────────────────────\n", "\n" ], "text/plain": [ "\u001b[1;36mInitial grid \u001b[0m\u001b[1;36m(\u001b[0m\u001b[1;36mstep \u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;36m)\u001b[0m \u001b[92m─────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "tree|tree|tree|empty\n", "tree|fire|tree|tree\n", "tree|tree|tree|empty\n", "empty|tree|tree|tree\n", "\n" ] }, { "data": { "text/html": [ "
\n", " Step 0: \n", " tree|tree|tree|empty \n", " tree|fire|tree|tree \n", " tree|tree|tree|empty \n", " empty|tree|tree|tree \n", " \n", " Step 1: \n", " tree|fire|tree|empty \n", " fire|ash|fire|tree \n", " tree|fire|tree|empty \n", " empty|tree|tree|tree \n", " \n", " Step 2: \n", " fire|ash|fire|empty \n", " ash|ash|ash|fire \n", " fire|ash|fire|empty \n", " empty|fire|tree|tree \n", " \n", " Step 3: \n", " ash|ash|ash|empty \n", " ash|ash|ash|ash \n", " ash|ash|ash|fire \n", " fire|ash|fire|tree \n", " \n", "\n" ], "text/plain": [ "\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mStep 0:\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mtree|tree|tree|empty\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mtree|fire|tree|tree\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mtree|tree|tree|empty\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mempty|tree|tree|tree\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mStep 1:\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mtree|fire|tree|empty\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mfire|ash|fire|tree\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mtree|fire|tree|empty\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mempty|tree|tree|tree\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mStep 2:\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mfire|ash|fire|empty\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mash|ash|ash|fire\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mfire|ash|fire|empty\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mempty|fire|tree|tree\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mStep 3:\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mash|ash|ash|empty\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mash|ash|ash|ash\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mash|ash|ash|fire\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\u001b[38;2;248;248;242;48;2;39;40;34mfire|ash|fire|tree\u001b[0m\u001b[48;2;39;40;34m \u001b[0m\u001b[48;2;39;40;34m \u001b[0m\n", "\u001b[48;2;39;40;34m \u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n" ] }, { "data": { "text/html": [ "
Per-step state counts ─────────────────────────────────────────────────────────────────────────────────────────────\n", "\n" ], "text/plain": [ "\u001b[1;36mPer-step state counts\u001b[0m \u001b[92m─────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ " step 0: {'tree': 12, 'empty': 3, 'fire': 1}\n", " step 1: {'tree': 8, 'fire': 4, 'empty': 3, 'ash': 1}\n", " step 2: {'fire': 6, 'ash': 5, 'empty': 3, 'tree': 2}\n", " step 3: {'ash': 11, 'empty': 1, 'fire': 3, 'tree': 1}\n" ] } ], "source": [ "INITIAL_GRID = (\n", " \"tree|tree|tree|empty\\n\"\n", " \"tree|fire|tree|tree\\n\"\n", " \"tree|tree|tree|empty\\n\"\n", " \"empty|tree|tree|tree\"\n", ")\n", "\n", "print_header(\"Initial grid (step 0)\")\n", "print(INITIAL_GRID)\n", "print()\n", "result = arch.run(INITIAL_GRID)\n", "print_md(\"```\\n\" + result.output + \"\\n```\")\n", "print()\n", "print_header(\"Per-step state counts\")\n", "for i, counts in enumerate(result.metadata['per_step_counts']):\n", " print(f\" step {i}: {counts}\")" ] }, { "cell_type": "markdown", "id": "44e7f2c3", "metadata": { "papermill": { "duration": 0.006024, "end_time": "2026-05-27T13:16:23.506451+00:00", "exception": false, "start_time": "2026-05-27T13:16:23.500427+00:00", "status": "completed" }, "tags": [] }, "source": [ "### 8.0 · What just happened, briefly\n", "\n", "Look at the per-step counts:\n", "\n", "- `fire` count should **rise then fall** as fire spreads and burns out.\n", "- `ash` count should **rise monotonically** (ash never reverts).\n", "- `tree` count should **fall monotonically** (trees only convert to fire, never the reverse).\n", "- `empty` count should be roughly constant (empties don't catch fire).\n", "\n", "If `tree` increases or `ash` decreases, the rule wasn't followed — the LLM hallucinated state transitions." ] }, { "cell_type": "markdown", "id": "a4a759f1", "metadata": { "papermill": { "duration": 0.006341, "end_time": "2026-05-27T13:16:23.516237+00:00", "exception": false, "start_time": "2026-05-27T13:16:23.509896+00:00", "status": "completed" }, "tags": [] }, "source": [ "## 9 · What we just observed\n", "\n", "The cells above ran a 4×4 forest-fire CA for 3 steps — each step makes 16 LLM calls (one per cell), so this run cost ~48 calls total.\n", "\n", "### 9.1 · Per-step state counts\n", "\n", "| Step | tree | fire | ash | empty |\n", "|---|---|---|---|---|\n", "| 0 | 12 | 1 | 0 | 3 |\n", "| 1 | 8 | 4 | 1 | 3 |\n", "| 2 | 2 | 6 | 5 | 3 |\n", "| 3 | 1 | 3 | 11 | 1 |\n", "\n", "### 9.2 · Grid evolution\n", "\n", "**Step 0**:\n", "```\n", "tree|tree|tree|empty\n", "tree|fire|tree|tree\n", "tree|tree|tree|empty\n", "empty|tree|tree|tree\n", "```\n", "**Step 1**:\n", "```\n", "tree|fire|tree|empty\n", "fire|ash|fire|tree\n", "tree|fire|tree|empty\n", "empty|tree|tree|tree\n", "```\n", "**Step 2**:\n", "```\n", "fire|ash|fire|empty\n", "ash|ash|ash|fire\n", "fire|ash|fire|empty\n", "empty|fire|tree|tree\n", "```\n", "**Step 3**:\n", "```\n", "ash|ash|ash|empty\n", "ash|ash|ash|ash\n", "ash|ash|ash|fire\n", "fire|ash|fire|tree\n", "```\n", "\n", "### 9.3 · Rule-violation checks\n", "\n", "- **Tree count is monotone non-increasing**: [12, 8, 2, 1]. The rule 'trees only convert to fire' was followed faithfully — no LLM hallucinated a fire-back-to-tree transition.\n", "\n", "- **Ash count is monotone non-decreasing**: [0, 1, 5, 11]. Ash never reverts, as the rule specifies. Good signal that the rule was followed.\n", "\n", "- **Fire count peaks then declines**: [1, 4, 6, 3]. Classic forest-fire emergent dynamic — fire spreads radially, then burns out as cells convert to ash.\n", "\n", "### 9.4 · The takeaway\n", "\n", "A *correctly* running forest-fire CA shows three signatures:\n", "\n", "1. **Tree count monotone non-increasing** — trees only convert to fire, never the reverse.\n", "2. **Ash count monotone non-decreasing** — ash is the absorbing state.\n", "3. **Fire count rises then falls** — the wavefront moves outward, then burns through.\n", "\n", "When the LLM hallucinates a rule violation (e.g., ash → tree), the macro counts violate one of these signatures. This is **a free correctness check** baked into the dynamics — no separate verifier needed.\n", "\n", "For production CA simulations, validate the rule on a tiny grid first via these monotonicity checks, then compile the rule to hard-coded Python once you trust it." ] }, { "cell_type": "markdown", "id": "ee00e114", "metadata": { "papermill": { "duration": 0.007873, "end_time": "2026-05-27T13:16:23.526448+00:00", "exception": false, "start_time": "2026-05-27T13:16:23.518575+00:00", "status": "completed" }, "tags": [] }, "source": [ "## 10 · Try a different rule — opinion dynamics\n", "\n", "Same architecture, different rule and state space: opinion polarisation. Each cell holds a stance (`for`, `against`, `neutral`). A neutral cell adopts the majority of its neighbours; for/against rarely flip." ] }, { "cell_type": "code", "execution_count": 5, "id": "16b6cc42", "metadata": { "execution": { "iopub.execute_input": "2026-05-27T13:16:23.536649Z", "iopub.status.busy": "2026-05-27T13:16:23.536649Z", "iopub.status.idle": "2026-05-27T13:16:42.392610Z", "shell.execute_reply": "2026-05-27T13:16:42.390028Z" }, "papermill": { "duration": 18.860139, "end_time": "2026-05-27T13:16:42.392610+00:00", "exception": false, "start_time": "2026-05-27T13:16:23.532471+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Step 0:\n", "for|neutral|against\n", "neutral|neutral|neutral\n", "for|neutral|against\n", "\n", "Step 1:\n", "for|neutral|against\n", "for|neutral|against\n", "for|neutral|against\n", "\n", "Step 2:\n", "for|for|against\n", "for|neutral|against\n", "for|neutral|against\n" ] } ], "source": [ "opinion_arch = CellularAutomata(\n", " rule_prompt=(\n", " \"Opinion dynamics: a 'neutral' cell adopts the majority opinion of its for/against neighbours. \"\n", " \"'for' and 'against' cells stay the same unless 3+ neighbours hold the opposite view (then flip).\"\n", " ),\n", " allowed_states=[\"for\", \"against\", \"neutral\"],\n", " height=3, width=3, max_steps=2,\n", ")\n", "INITIAL = (\n", " \"for|neutral|against\\n\"\n", " \"neutral|neutral|neutral\\n\"\n", " \"for|neutral|against\"\n", ")\n", "r = opinion_arch.run(INITIAL)\n", "print(r.output)" ] }, { "cell_type": "markdown", "id": "0d016b23", "metadata": { "papermill": { "duration": 0.012689, "end_time": "2026-05-27T13:16:42.418957+00:00", "exception": false, "start_time": "2026-05-27T13:16:42.406268+00:00", "status": "completed" }, "tags": [] }, "source": [ "## 11 · Failure modes, safety, extensions\n", "\n", "### 11.1 · Where this breaks\n", "\n", "| Failure | Mechanism | Mitigation |\n", "|---|---|---|\n", "| **Cost explosion** | H×W×steps LLM calls | Cap grid + steps; or batch many cells per call |\n", "| **Rule drift over many steps** | LLM's interpretation of the rule wobbles | Re-state the rule prompt every step; use lower temperature |\n", "| **LLM emits invalid state** | Returns `\"tree-fire\"` instead of `\"tree\"` or `\"fire\"` | Python **clamp** in `_step` falls back to current state (we do this) |\n", "| **Synchronous vs async confusion** | Cell uses updated neighbour instead of pre-step neighbour | New grid built from OLD grid only (we enforce this) |\n", "| **Rule sensitivity** | Tiny prompt changes flip macro behaviour | Test on 3×3 first; track macro counts step-by-step |\n", "\n", "### 11.2 · Production safety\n", "\n", "- **Always cap step count.** Runaway CA can drain budget fast.\n", "- **Track per-step state counts.** If a \"monotone\" quantity violates the rule, the LLM is hallucinating transitions.\n", "- **Use hard-coded rules where possible.** LLM cells make sense for *prototyping* rules; production sims should compile the rule to Python once it's validated.\n", "\n", "### 11.3 · Three extensions\n", "\n", "1. **Batch all cells per step** — one structured-output call returning the full next-grid; 16× cheaper for a 4×4. Trade-off: the LLM has to reason about the whole grid at once.\n", "2. **Moore neighbourhood** — 8-neighbour instead of 4. Forest-fire dynamics change subtly.\n", "3. **Mixed rules** — some cells hard-coded (cheap), some LLM (flexible). E.g., obstacles hard-coded as stationary, agents LLM-driven.\n", "\n", "### 11.4 · What to read next\n", "\n", "- [**05 · Multi-Agent**](./05_multi_agent.ipynb) — central coordination instead of local.\n", "- [**07 · Blackboard**](./07_blackboard.ipynb) — distributed agents but with shared global state instead of local neighbours.\n", "- [**13 · Ensemble**](./13_ensemble.ipynb) — parallel voters with no spatial structure.\n", "\n", "### 11.5 · References\n", "\n", "1. von Neumann, J. *Theory of Self-Reproducing Automata.* 1966.\n", "2. Conway, J. H. *Game of Life.* 1970.\n", "3. Wolfram, S. *A New Kind of Science.* 2002. (extensive CA taxonomy)\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.0" }, "papermill": { "default_parameters": {}, "duration": 93.83228, "end_time": "2026-05-27T13:16:43.301574+00:00", "environment_variables": {}, "exception": null, "input_path": "all-agentic-architectures/notebooks/16_cellular_automata.ipynb", "output_path": "all-agentic-architectures/notebooks/16_cellular_automata.ipynb", "parameters": {}, "start_time": "2026-05-27T13:15:09.469294+00:00", "version": "2.7.0" } }, "nbformat": 4, "nbformat_minor": 5 }