phanerozoic commited on
Commit
f258bab
·
verified ·
1 Parent(s): 1a0f84c

Uploaded using `kernel-builder`.

Browse files
build/torch211-cxx11-cpu-aarch64-linux/__init__.py CHANGED
@@ -3,8 +3,10 @@
3
  Agents diffuse a chemoattractant trail and steer up its gradient; the emergent
4
  network is the same structure a real slime mould forms solving a maze. The
5
  agent step (three sensor look-ups, steering, motion, deposition) and the trail
6
- diffusion are vectorised with NEON on aarch64 and fall back to portable scalar
7
- elsewhere. The trail is an ordinary CPU tensor you read and render each step.
 
 
8
 
9
  from kernels import get_kernel
10
  physarum = get_kernel("phanerozoic/physarum", version=1, trust_remote_code=True)
@@ -80,4 +82,100 @@ class Physarum:
80
  return (rgb * 255 + 0.5).to(torch.uint8)
81
 
82
 
83
- __all__ = ["Physarum"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  Agents diffuse a chemoattractant trail and steer up its gradient; the emergent
4
  network is the same structure a real slime mould forms solving a maze. The
5
  agent step (three sensor look-ups, steering, motion, deposition) and the trail
6
+ diffusion vectorise with NEON on aarch64; the agent step is threaded across
7
+ cores (set torch.set_num_threads) and works on tile-sorted agents for cache
8
+ locality, with a portable scalar fallback. The trail is an ordinary CPU tensor
9
+ you read and render each step.
10
 
11
  from kernels import get_kernel
12
  physarum = get_kernel("phanerozoic/physarum", version=1, trust_remote_code=True)
 
82
  return (rgb * 255 + 0.5).to(torch.uint8)
83
 
84
 
85
+ def _bfs(net, a, b):
86
+ """Shortest 4-connected path a->b through the boolean grid `net`, or None."""
87
+ import collections
88
+ H, W = net.shape; (ax, ay), (bx, by) = a, b
89
+ if not bool(net[ay, ax]) or not bool(net[by, bx]): return None
90
+ seen = torch.zeros(H, W, dtype=torch.bool); seen[ay, ax] = True
91
+ prev = {}; q = collections.deque([(ax, ay)])
92
+ while q:
93
+ x, y = q.popleft()
94
+ if (x, y) == (bx, by):
95
+ p = [(x, y)]
96
+ while (x, y) != (ax, ay): x, y = prev[(x, y)]; p.append((x, y))
97
+ return p[::-1]
98
+ for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):
99
+ nx, ny = x + dx, y + dy
100
+ if 0 <= nx < W and 0 <= ny < H and bool(net[ny, nx]) and not seen[ny, nx]:
101
+ seen[ny, nx] = True; prev[(nx, ny)] = (x, y); q.append((nx, ny))
102
+ return None
103
+
104
+
105
+ class PhysarumFlow:
106
+ """Tero adaptive-conductivity Physarum solver on a masked grid.
107
+
108
+ Repeatedly solves the weighted-Laplacian flow between terminals (the flow_cg
109
+ kernel op) and reinforces high-flux edges; the conductivity field converges
110
+ onto the shortest path (one source/sink) or an efficient transport network
111
+ (several terminals). Edges live between 4-connected open cells.
112
+
113
+ Args:
114
+ open_mask: [H, W], nonzero = open cell, zero = wall.
115
+ """
116
+
117
+ def __init__(self, open_mask):
118
+ m = open_mask.to(torch.float32).contiguous()
119
+ self.m = m; self.H, self.W = m.shape
120
+ H, W = self.H, self.W
121
+ self.oE = torch.zeros(H, W); self.oE[:, :-1] = m[:, :-1] * m[:, 1:] # open east edges
122
+ self.oS = torch.zeros(H, W); self.oS[:-1, :] = m[:-1, :] * m[1:, :] # open south edges
123
+ self.cE = self.oE.clone().contiguous()
124
+ self.cS = self.oS.clone().contiguous()
125
+ self.p = torch.zeros(H, W).contiguous()
126
+
127
+ def solve(self, terminals, inject=None, iters=160, mu=1.3, dt=0.2,
128
+ floor=1e-3, cg_iters=120, cg_tol=1e-4):
129
+ """Run `iters` adaptation steps. terminals: list of (x, y). inject: net
130
+ current per terminal (defaults to source terminals[0], sinks the rest).
131
+ Returns self; read the network with .network()/.path()."""
132
+ H, W = self.H, self.W
133
+ if inject is None:
134
+ inject = [float(len(terminals) - 1)] + [-1.0] * (len(terminals) - 1)
135
+ b = torch.zeros(H, W)
136
+ for (tx, ty), v in zip(terminals, inject): b[ty, tx] += float(v)
137
+ b = b.contiguous()
138
+ gx, gy = terminals[-1]; ground = gy * W + gx # pin one terminal's gauge
139
+ for _ in range(int(iters)):
140
+ ops.flow_cg(self.cE, self.cS, b, self.p, ground, int(cg_iters), float(cg_tol))
141
+ Qe = torch.zeros(H, W); Qe[:, :-1] = (self.cE[:, :-1] * (self.p[:, :-1] - self.p[:, 1:])).abs()
142
+ Qs = torch.zeros(H, W); Qs[:-1, :] = (self.cS[:-1, :] * (self.p[:-1, :] - self.p[1:, :])).abs()
143
+ self.cE = torch.maximum(self.cE + dt * (Qe.pow(mu) - self.cE), floor * self.oE).mul_(self.oE).contiguous()
144
+ self.cS = torch.maximum(self.cS + dt * (Qs.pow(mu) - self.cS), floor * self.oS).mul_(self.oS).contiguous()
145
+ return self
146
+
147
+ def network(self, thresh=0.5):
148
+ """Boolean [H, W] mask of cells on high-conductivity edges."""
149
+ H, W = self.H, self.W
150
+ nn = torch.zeros(H, W, dtype=torch.bool)
151
+ e = self.cE > thresh; s = self.cS > thresh
152
+ nn[:, :-1] |= e[:, :-1]; nn[:, 1:] |= e[:, :-1]
153
+ nn[:-1, :] |= s[:-1, :]; nn[1:, :] |= s[:-1, :]
154
+ return nn & (self.m > 0)
155
+
156
+ def path(self, a, b, thresh=0.5):
157
+ """Shortest path (list of (x, y)) between two terminals through the
158
+ converged network, or None if they are not linked at `thresh`."""
159
+ return _bfs(self.network(thresh), a, b)
160
+
161
+
162
+ def maze(height, width, seed=0):
163
+ """A recursive-division maze as a [H, W] float mask (1 = open, 0 = wall)."""
164
+ import random
165
+ rng = random.Random(seed); m = torch.ones(height, width)
166
+ m[0, :] = 0; m[-1, :] = 0; m[:, 0] = 0; m[:, -1] = 0
167
+ def div(x0, y0, x1, y1, dep):
168
+ if x1 - x0 < 6 or y1 - y0 < 6 or dep > 8: return
169
+ if (x1 - x0) > (y1 - y0):
170
+ wx = rng.randrange(x0 + 2, x1 - 1, 2); gy = rng.randrange(y0 + 1, y1, 2)
171
+ for y in range(y0, y1): m[y, wx] = 0
172
+ m[gy, wx] = 1; div(x0, y0, wx - 1, y1, dep + 1); div(wx + 1, y0, x1, y1, dep + 1)
173
+ else:
174
+ wy = rng.randrange(y0 + 2, y1 - 1, 2); gx = rng.randrange(x0 + 1, x1, 2)
175
+ for x in range(x0, x1): m[wy, x] = 0
176
+ m[wy, gx] = 1; div(x0, y0, x1, wy - 1, dep + 1); div(x0, wy + 1, x1, y1, dep + 1)
177
+ div(1, 1, width - 2, height - 2, 0)
178
+ return m
179
+
180
+
181
+ __all__ = ["Physarum", "PhysarumFlow", "maze"]
build/torch211-cxx11-cpu-aarch64-linux/_ops.py CHANGED
@@ -1,9 +1,9 @@
1
  import torch
2
- from . import _physarum_cpu_9dfad7c_dirty
3
- ops = torch.ops._physarum_cpu_9dfad7c_dirty
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
- return f"_physarum_cpu_9dfad7c_dirty::{op_name}"
 
1
  import torch
2
+ from . import _physarum_cpu_a57b48e
3
+ ops = torch.ops._physarum_cpu_a57b48e
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
+ return f"_physarum_cpu_a57b48e::{op_name}"
build/torch211-cxx11-cpu-aarch64-linux/{_physarum_cpu_9dfad7c_dirty.abi3.so → _physarum_cpu_a57b48e.abi3.so} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:789afdecb6b076eb834732fd618b0182e3f1c0e1e51ee5cf4e6b45e14eb8b3d9
3
- size 85120
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e3d694191dc96a1d7d915eb180e5230bcce9bb16f3b0f2de47b90747c350f28a
3
+ size 153296
build/torch211-cxx11-cpu-aarch64-linux/metadata.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "name": "physarum",
3
- "id": "_physarum_cpu_9dfad7c_dirty",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
@@ -10,9 +10,9 @@
10
  "digest": {
11
  "algorithm": "sha256",
12
  "files": {
13
- "__init__.py": "5wjltcYQACNQLyVO0SYLiz5g9Vy0o/qSogtd2z9YgE8=",
14
- "_ops.py": "CdBM1t9Ra83HGXv7+a6poQnsfTXqg9cnwJFps4vQii0=",
15
- "_physarum_cpu_9dfad7c_dirty.abi3.so": "eJr97LawduuDRzL9YYsBguPxwOHlHuXPTmtF4U64s9k=",
16
  "physarum/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
17
  }
18
  },
@@ -23,8 +23,8 @@
23
  "dirty": false
24
  },
25
  "kernel": {
26
- "sha": "9dfad7c219a1c39f98eb5d05ccd2f566e90696fe",
27
- "dirty": true
28
  }
29
  }
30
  }
 
1
  {
2
  "name": "physarum",
3
+ "id": "_physarum_cpu_a57b48e",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
 
10
  "digest": {
11
  "algorithm": "sha256",
12
  "files": {
13
+ "__init__.py": "q8Cp06QoKUiAkrc/bath8dVTdGnofXLiPVXcG1AvndQ=",
14
+ "_ops.py": "n6Gte2TrEwnD8qKlKIC3Xwk7CMXDuBFcDsymKGOqKlQ=",
15
+ "_physarum_cpu_a57b48e.abi3.so": "49aUGR3Jah19kV6xgOUjC8zpuxbzsPLeR7kHR8NQ8oo=",
16
  "physarum/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
17
  }
18
  },
 
23
  "dirty": false
24
  },
25
  "kernel": {
26
+ "sha": "a57b48e23004b429e1eca5517c5df547b8d1f8ac",
27
+ "dirty": false
28
  }
29
  }
30
  }
build/torch212-cxx11-cpu-aarch64-linux/__init__.py CHANGED
@@ -3,8 +3,10 @@
3
  Agents diffuse a chemoattractant trail and steer up its gradient; the emergent
4
  network is the same structure a real slime mould forms solving a maze. The
5
  agent step (three sensor look-ups, steering, motion, deposition) and the trail
6
- diffusion are vectorised with NEON on aarch64 and fall back to portable scalar
7
- elsewhere. The trail is an ordinary CPU tensor you read and render each step.
 
 
8
 
9
  from kernels import get_kernel
10
  physarum = get_kernel("phanerozoic/physarum", version=1, trust_remote_code=True)
@@ -80,4 +82,100 @@ class Physarum:
80
  return (rgb * 255 + 0.5).to(torch.uint8)
81
 
82
 
83
- __all__ = ["Physarum"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  Agents diffuse a chemoattractant trail and steer up its gradient; the emergent
4
  network is the same structure a real slime mould forms solving a maze. The
5
  agent step (three sensor look-ups, steering, motion, deposition) and the trail
6
+ diffusion vectorise with NEON on aarch64; the agent step is threaded across
7
+ cores (set torch.set_num_threads) and works on tile-sorted agents for cache
8
+ locality, with a portable scalar fallback. The trail is an ordinary CPU tensor
9
+ you read and render each step.
10
 
11
  from kernels import get_kernel
12
  physarum = get_kernel("phanerozoic/physarum", version=1, trust_remote_code=True)
 
82
  return (rgb * 255 + 0.5).to(torch.uint8)
83
 
84
 
85
+ def _bfs(net, a, b):
86
+ """Shortest 4-connected path a->b through the boolean grid `net`, or None."""
87
+ import collections
88
+ H, W = net.shape; (ax, ay), (bx, by) = a, b
89
+ if not bool(net[ay, ax]) or not bool(net[by, bx]): return None
90
+ seen = torch.zeros(H, W, dtype=torch.bool); seen[ay, ax] = True
91
+ prev = {}; q = collections.deque([(ax, ay)])
92
+ while q:
93
+ x, y = q.popleft()
94
+ if (x, y) == (bx, by):
95
+ p = [(x, y)]
96
+ while (x, y) != (ax, ay): x, y = prev[(x, y)]; p.append((x, y))
97
+ return p[::-1]
98
+ for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):
99
+ nx, ny = x + dx, y + dy
100
+ if 0 <= nx < W and 0 <= ny < H and bool(net[ny, nx]) and not seen[ny, nx]:
101
+ seen[ny, nx] = True; prev[(nx, ny)] = (x, y); q.append((nx, ny))
102
+ return None
103
+
104
+
105
+ class PhysarumFlow:
106
+ """Tero adaptive-conductivity Physarum solver on a masked grid.
107
+
108
+ Repeatedly solves the weighted-Laplacian flow between terminals (the flow_cg
109
+ kernel op) and reinforces high-flux edges; the conductivity field converges
110
+ onto the shortest path (one source/sink) or an efficient transport network
111
+ (several terminals). Edges live between 4-connected open cells.
112
+
113
+ Args:
114
+ open_mask: [H, W], nonzero = open cell, zero = wall.
115
+ """
116
+
117
+ def __init__(self, open_mask):
118
+ m = open_mask.to(torch.float32).contiguous()
119
+ self.m = m; self.H, self.W = m.shape
120
+ H, W = self.H, self.W
121
+ self.oE = torch.zeros(H, W); self.oE[:, :-1] = m[:, :-1] * m[:, 1:] # open east edges
122
+ self.oS = torch.zeros(H, W); self.oS[:-1, :] = m[:-1, :] * m[1:, :] # open south edges
123
+ self.cE = self.oE.clone().contiguous()
124
+ self.cS = self.oS.clone().contiguous()
125
+ self.p = torch.zeros(H, W).contiguous()
126
+
127
+ def solve(self, terminals, inject=None, iters=160, mu=1.3, dt=0.2,
128
+ floor=1e-3, cg_iters=120, cg_tol=1e-4):
129
+ """Run `iters` adaptation steps. terminals: list of (x, y). inject: net
130
+ current per terminal (defaults to source terminals[0], sinks the rest).
131
+ Returns self; read the network with .network()/.path()."""
132
+ H, W = self.H, self.W
133
+ if inject is None:
134
+ inject = [float(len(terminals) - 1)] + [-1.0] * (len(terminals) - 1)
135
+ b = torch.zeros(H, W)
136
+ for (tx, ty), v in zip(terminals, inject): b[ty, tx] += float(v)
137
+ b = b.contiguous()
138
+ gx, gy = terminals[-1]; ground = gy * W + gx # pin one terminal's gauge
139
+ for _ in range(int(iters)):
140
+ ops.flow_cg(self.cE, self.cS, b, self.p, ground, int(cg_iters), float(cg_tol))
141
+ Qe = torch.zeros(H, W); Qe[:, :-1] = (self.cE[:, :-1] * (self.p[:, :-1] - self.p[:, 1:])).abs()
142
+ Qs = torch.zeros(H, W); Qs[:-1, :] = (self.cS[:-1, :] * (self.p[:-1, :] - self.p[1:, :])).abs()
143
+ self.cE = torch.maximum(self.cE + dt * (Qe.pow(mu) - self.cE), floor * self.oE).mul_(self.oE).contiguous()
144
+ self.cS = torch.maximum(self.cS + dt * (Qs.pow(mu) - self.cS), floor * self.oS).mul_(self.oS).contiguous()
145
+ return self
146
+
147
+ def network(self, thresh=0.5):
148
+ """Boolean [H, W] mask of cells on high-conductivity edges."""
149
+ H, W = self.H, self.W
150
+ nn = torch.zeros(H, W, dtype=torch.bool)
151
+ e = self.cE > thresh; s = self.cS > thresh
152
+ nn[:, :-1] |= e[:, :-1]; nn[:, 1:] |= e[:, :-1]
153
+ nn[:-1, :] |= s[:-1, :]; nn[1:, :] |= s[:-1, :]
154
+ return nn & (self.m > 0)
155
+
156
+ def path(self, a, b, thresh=0.5):
157
+ """Shortest path (list of (x, y)) between two terminals through the
158
+ converged network, or None if they are not linked at `thresh`."""
159
+ return _bfs(self.network(thresh), a, b)
160
+
161
+
162
+ def maze(height, width, seed=0):
163
+ """A recursive-division maze as a [H, W] float mask (1 = open, 0 = wall)."""
164
+ import random
165
+ rng = random.Random(seed); m = torch.ones(height, width)
166
+ m[0, :] = 0; m[-1, :] = 0; m[:, 0] = 0; m[:, -1] = 0
167
+ def div(x0, y0, x1, y1, dep):
168
+ if x1 - x0 < 6 or y1 - y0 < 6 or dep > 8: return
169
+ if (x1 - x0) > (y1 - y0):
170
+ wx = rng.randrange(x0 + 2, x1 - 1, 2); gy = rng.randrange(y0 + 1, y1, 2)
171
+ for y in range(y0, y1): m[y, wx] = 0
172
+ m[gy, wx] = 1; div(x0, y0, wx - 1, y1, dep + 1); div(wx + 1, y0, x1, y1, dep + 1)
173
+ else:
174
+ wy = rng.randrange(y0 + 2, y1 - 1, 2); gx = rng.randrange(x0 + 1, x1, 2)
175
+ for x in range(x0, x1): m[wy, x] = 0
176
+ m[wy, gx] = 1; div(x0, y0, x1, wy - 1, dep + 1); div(x0, wy + 1, x1, y1, dep + 1)
177
+ div(1, 1, width - 2, height - 2, 0)
178
+ return m
179
+
180
+
181
+ __all__ = ["Physarum", "PhysarumFlow", "maze"]
build/torch212-cxx11-cpu-aarch64-linux/_ops.py CHANGED
@@ -1,9 +1,9 @@
1
  import torch
2
- from . import _physarum_cpu_9dfad7c_dirty
3
- ops = torch.ops._physarum_cpu_9dfad7c_dirty
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
- return f"_physarum_cpu_9dfad7c_dirty::{op_name}"
 
1
  import torch
2
+ from . import _physarum_cpu_a57b48e
3
+ ops = torch.ops._physarum_cpu_a57b48e
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
+ return f"_physarum_cpu_a57b48e::{op_name}"
build/torch212-cxx11-cpu-aarch64-linux/{_physarum_cpu_9dfad7c_dirty.abi3.so → _physarum_cpu_a57b48e.abi3.so} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:1e0a34e2de1b81efc70de4ecef7d5f32271aa5070a8d2c6ff7ada59f9f7d41d0
3
- size 85272
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:17d57732072d4daac322799037b731a03bc7a1ea6ae602979c311d54692316cf
3
+ size 153320
build/torch212-cxx11-cpu-aarch64-linux/metadata.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "name": "physarum",
3
- "id": "_physarum_cpu_9dfad7c_dirty",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
@@ -10,9 +10,9 @@
10
  "digest": {
11
  "algorithm": "sha256",
12
  "files": {
13
- "__init__.py": "5wjltcYQACNQLyVO0SYLiz5g9Vy0o/qSogtd2z9YgE8=",
14
- "_ops.py": "CdBM1t9Ra83HGXv7+a6poQnsfTXqg9cnwJFps4vQii0=",
15
- "_physarum_cpu_9dfad7c_dirty.abi3.so": "Hgo04t4bge/HDeTs731fMicapQcKjSxv962ln599QdA=",
16
  "physarum/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
17
  }
18
  },
@@ -23,8 +23,8 @@
23
  "dirty": false
24
  },
25
  "kernel": {
26
- "sha": "9dfad7c219a1c39f98eb5d05ccd2f566e90696fe",
27
- "dirty": true
28
  }
29
  }
30
  }
 
1
  {
2
  "name": "physarum",
3
+ "id": "_physarum_cpu_a57b48e",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
 
10
  "digest": {
11
  "algorithm": "sha256",
12
  "files": {
13
+ "__init__.py": "q8Cp06QoKUiAkrc/bath8dVTdGnofXLiPVXcG1AvndQ=",
14
+ "_ops.py": "n6Gte2TrEwnD8qKlKIC3Xwk7CMXDuBFcDsymKGOqKlQ=",
15
+ "_physarum_cpu_a57b48e.abi3.so": "F9V3MgctTarDInmQN7cxoDvHoepq5gKXnDEdVGkjFs8=",
16
  "physarum/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
17
  }
18
  },
 
23
  "dirty": false
24
  },
25
  "kernel": {
26
+ "sha": "a57b48e23004b429e1eca5517c5df547b8d1f8ac",
27
+ "dirty": false
28
  }
29
  }
30
  }
build/torch213-cxx11-cpu-aarch64-linux/__init__.py CHANGED
@@ -3,8 +3,10 @@
3
  Agents diffuse a chemoattractant trail and steer up its gradient; the emergent
4
  network is the same structure a real slime mould forms solving a maze. The
5
  agent step (three sensor look-ups, steering, motion, deposition) and the trail
6
- diffusion are vectorised with NEON on aarch64 and fall back to portable scalar
7
- elsewhere. The trail is an ordinary CPU tensor you read and render each step.
 
 
8
 
9
  from kernels import get_kernel
10
  physarum = get_kernel("phanerozoic/physarum", version=1, trust_remote_code=True)
@@ -80,4 +82,100 @@ class Physarum:
80
  return (rgb * 255 + 0.5).to(torch.uint8)
81
 
82
 
83
- __all__ = ["Physarum"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  Agents diffuse a chemoattractant trail and steer up its gradient; the emergent
4
  network is the same structure a real slime mould forms solving a maze. The
5
  agent step (three sensor look-ups, steering, motion, deposition) and the trail
6
+ diffusion vectorise with NEON on aarch64; the agent step is threaded across
7
+ cores (set torch.set_num_threads) and works on tile-sorted agents for cache
8
+ locality, with a portable scalar fallback. The trail is an ordinary CPU tensor
9
+ you read and render each step.
10
 
11
  from kernels import get_kernel
12
  physarum = get_kernel("phanerozoic/physarum", version=1, trust_remote_code=True)
 
82
  return (rgb * 255 + 0.5).to(torch.uint8)
83
 
84
 
85
+ def _bfs(net, a, b):
86
+ """Shortest 4-connected path a->b through the boolean grid `net`, or None."""
87
+ import collections
88
+ H, W = net.shape; (ax, ay), (bx, by) = a, b
89
+ if not bool(net[ay, ax]) or not bool(net[by, bx]): return None
90
+ seen = torch.zeros(H, W, dtype=torch.bool); seen[ay, ax] = True
91
+ prev = {}; q = collections.deque([(ax, ay)])
92
+ while q:
93
+ x, y = q.popleft()
94
+ if (x, y) == (bx, by):
95
+ p = [(x, y)]
96
+ while (x, y) != (ax, ay): x, y = prev[(x, y)]; p.append((x, y))
97
+ return p[::-1]
98
+ for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):
99
+ nx, ny = x + dx, y + dy
100
+ if 0 <= nx < W and 0 <= ny < H and bool(net[ny, nx]) and not seen[ny, nx]:
101
+ seen[ny, nx] = True; prev[(nx, ny)] = (x, y); q.append((nx, ny))
102
+ return None
103
+
104
+
105
+ class PhysarumFlow:
106
+ """Tero adaptive-conductivity Physarum solver on a masked grid.
107
+
108
+ Repeatedly solves the weighted-Laplacian flow between terminals (the flow_cg
109
+ kernel op) and reinforces high-flux edges; the conductivity field converges
110
+ onto the shortest path (one source/sink) or an efficient transport network
111
+ (several terminals). Edges live between 4-connected open cells.
112
+
113
+ Args:
114
+ open_mask: [H, W], nonzero = open cell, zero = wall.
115
+ """
116
+
117
+ def __init__(self, open_mask):
118
+ m = open_mask.to(torch.float32).contiguous()
119
+ self.m = m; self.H, self.W = m.shape
120
+ H, W = self.H, self.W
121
+ self.oE = torch.zeros(H, W); self.oE[:, :-1] = m[:, :-1] * m[:, 1:] # open east edges
122
+ self.oS = torch.zeros(H, W); self.oS[:-1, :] = m[:-1, :] * m[1:, :] # open south edges
123
+ self.cE = self.oE.clone().contiguous()
124
+ self.cS = self.oS.clone().contiguous()
125
+ self.p = torch.zeros(H, W).contiguous()
126
+
127
+ def solve(self, terminals, inject=None, iters=160, mu=1.3, dt=0.2,
128
+ floor=1e-3, cg_iters=120, cg_tol=1e-4):
129
+ """Run `iters` adaptation steps. terminals: list of (x, y). inject: net
130
+ current per terminal (defaults to source terminals[0], sinks the rest).
131
+ Returns self; read the network with .network()/.path()."""
132
+ H, W = self.H, self.W
133
+ if inject is None:
134
+ inject = [float(len(terminals) - 1)] + [-1.0] * (len(terminals) - 1)
135
+ b = torch.zeros(H, W)
136
+ for (tx, ty), v in zip(terminals, inject): b[ty, tx] += float(v)
137
+ b = b.contiguous()
138
+ gx, gy = terminals[-1]; ground = gy * W + gx # pin one terminal's gauge
139
+ for _ in range(int(iters)):
140
+ ops.flow_cg(self.cE, self.cS, b, self.p, ground, int(cg_iters), float(cg_tol))
141
+ Qe = torch.zeros(H, W); Qe[:, :-1] = (self.cE[:, :-1] * (self.p[:, :-1] - self.p[:, 1:])).abs()
142
+ Qs = torch.zeros(H, W); Qs[:-1, :] = (self.cS[:-1, :] * (self.p[:-1, :] - self.p[1:, :])).abs()
143
+ self.cE = torch.maximum(self.cE + dt * (Qe.pow(mu) - self.cE), floor * self.oE).mul_(self.oE).contiguous()
144
+ self.cS = torch.maximum(self.cS + dt * (Qs.pow(mu) - self.cS), floor * self.oS).mul_(self.oS).contiguous()
145
+ return self
146
+
147
+ def network(self, thresh=0.5):
148
+ """Boolean [H, W] mask of cells on high-conductivity edges."""
149
+ H, W = self.H, self.W
150
+ nn = torch.zeros(H, W, dtype=torch.bool)
151
+ e = self.cE > thresh; s = self.cS > thresh
152
+ nn[:, :-1] |= e[:, :-1]; nn[:, 1:] |= e[:, :-1]
153
+ nn[:-1, :] |= s[:-1, :]; nn[1:, :] |= s[:-1, :]
154
+ return nn & (self.m > 0)
155
+
156
+ def path(self, a, b, thresh=0.5):
157
+ """Shortest path (list of (x, y)) between two terminals through the
158
+ converged network, or None if they are not linked at `thresh`."""
159
+ return _bfs(self.network(thresh), a, b)
160
+
161
+
162
+ def maze(height, width, seed=0):
163
+ """A recursive-division maze as a [H, W] float mask (1 = open, 0 = wall)."""
164
+ import random
165
+ rng = random.Random(seed); m = torch.ones(height, width)
166
+ m[0, :] = 0; m[-1, :] = 0; m[:, 0] = 0; m[:, -1] = 0
167
+ def div(x0, y0, x1, y1, dep):
168
+ if x1 - x0 < 6 or y1 - y0 < 6 or dep > 8: return
169
+ if (x1 - x0) > (y1 - y0):
170
+ wx = rng.randrange(x0 + 2, x1 - 1, 2); gy = rng.randrange(y0 + 1, y1, 2)
171
+ for y in range(y0, y1): m[y, wx] = 0
172
+ m[gy, wx] = 1; div(x0, y0, wx - 1, y1, dep + 1); div(wx + 1, y0, x1, y1, dep + 1)
173
+ else:
174
+ wy = rng.randrange(y0 + 2, y1 - 1, 2); gx = rng.randrange(x0 + 1, x1, 2)
175
+ for x in range(x0, x1): m[wy, x] = 0
176
+ m[wy, gx] = 1; div(x0, y0, x1, wy - 1, dep + 1); div(x0, wy + 1, x1, y1, dep + 1)
177
+ div(1, 1, width - 2, height - 2, 0)
178
+ return m
179
+
180
+
181
+ __all__ = ["Physarum", "PhysarumFlow", "maze"]
build/torch213-cxx11-cpu-aarch64-linux/_ops.py CHANGED
@@ -1,9 +1,9 @@
1
  import torch
2
- from . import _physarum_cpu_9dfad7c_dirty
3
- ops = torch.ops._physarum_cpu_9dfad7c_dirty
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
- return f"_physarum_cpu_9dfad7c_dirty::{op_name}"
 
1
  import torch
2
+ from . import _physarum_cpu_a57b48e
3
+ ops = torch.ops._physarum_cpu_a57b48e
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
+ return f"_physarum_cpu_a57b48e::{op_name}"
build/torch213-cxx11-cpu-aarch64-linux/{_physarum_cpu_9dfad7c_dirty.abi3.so → _physarum_cpu_a57b48e.abi3.so} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:61a49d1e0cfb3223c73c0e34be651de8f6460b0b1a7fbad3429a6cd75c893002
3
- size 85272
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8f849a9e8e94bfb8f4bb1394c461c48fdfb7e2eb7b3cb809fe4a2ee92c825eb9
3
+ size 153320
build/torch213-cxx11-cpu-aarch64-linux/metadata.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "name": "physarum",
3
- "id": "_physarum_cpu_9dfad7c_dirty",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
@@ -10,9 +10,9 @@
10
  "digest": {
11
  "algorithm": "sha256",
12
  "files": {
13
- "__init__.py": "5wjltcYQACNQLyVO0SYLiz5g9Vy0o/qSogtd2z9YgE8=",
14
- "_ops.py": "CdBM1t9Ra83HGXv7+a6poQnsfTXqg9cnwJFps4vQii0=",
15
- "_physarum_cpu_9dfad7c_dirty.abi3.so": "YaSdHgz7MiPHPA40vmUd6PZGCwsaf7rTQpps11yJMAI=",
16
  "physarum/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
17
  }
18
  },
@@ -23,8 +23,8 @@
23
  "dirty": false
24
  },
25
  "kernel": {
26
- "sha": "9dfad7c219a1c39f98eb5d05ccd2f566e90696fe",
27
- "dirty": true
28
  }
29
  }
30
  }
 
1
  {
2
  "name": "physarum",
3
+ "id": "_physarum_cpu_a57b48e",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
 
10
  "digest": {
11
  "algorithm": "sha256",
12
  "files": {
13
+ "__init__.py": "q8Cp06QoKUiAkrc/bath8dVTdGnofXLiPVXcG1AvndQ=",
14
+ "_ops.py": "n6Gte2TrEwnD8qKlKIC3Xwk7CMXDuBFcDsymKGOqKlQ=",
15
+ "_physarum_cpu_a57b48e.abi3.so": "j4Sano6Uv7j0uxOUxGHEj9+34ut7PLgJ/kou6SyCXrk=",
16
  "physarum/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
17
  }
18
  },
 
23
  "dirty": false
24
  },
25
  "kernel": {
26
+ "sha": "a57b48e23004b429e1eca5517c5df547b8d1f8ac",
27
+ "dirty": false
28
  }
29
  }
30
  }