Uploaded using `kernel-builder`.
Browse files- build/torch211-cxx11-cpu-aarch64-linux/__init__.py +101 -3
- build/torch211-cxx11-cpu-aarch64-linux/_ops.py +3 -3
- build/torch211-cxx11-cpu-aarch64-linux/{_physarum_cpu_9dfad7c_dirty.abi3.so → _physarum_cpu_a57b48e.abi3.so} +2 -2
- build/torch211-cxx11-cpu-aarch64-linux/metadata.json +6 -6
- build/torch212-cxx11-cpu-aarch64-linux/__init__.py +101 -3
- build/torch212-cxx11-cpu-aarch64-linux/_ops.py +3 -3
- build/torch212-cxx11-cpu-aarch64-linux/{_physarum_cpu_9dfad7c_dirty.abi3.so → _physarum_cpu_a57b48e.abi3.so} +2 -2
- build/torch212-cxx11-cpu-aarch64-linux/metadata.json +6 -6
- build/torch213-cxx11-cpu-aarch64-linux/__init__.py +101 -3
- build/torch213-cxx11-cpu-aarch64-linux/_ops.py +3 -3
- build/torch213-cxx11-cpu-aarch64-linux/{_physarum_cpu_9dfad7c_dirty.abi3.so → _physarum_cpu_a57b48e.abi3.so} +2 -2
- build/torch213-cxx11-cpu-aarch64-linux/metadata.json +6 -6
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
|
| 7 |
-
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 3 |
-
ops = torch.ops.
|
| 4 |
|
| 5 |
def add_op_namespace_prefix(op_name: str):
|
| 6 |
"""
|
| 7 |
Prefix op by namespace.
|
| 8 |
"""
|
| 9 |
-
return f"
|
|
|
|
| 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:
|
| 3 |
-
size
|
|
|
|
| 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": "
|
| 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": "
|
| 14 |
-
"_ops.py": "
|
| 15 |
-
"
|
| 16 |
"physarum/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
|
| 17 |
}
|
| 18 |
},
|
|
@@ -23,8 +23,8 @@
|
|
| 23 |
"dirty": false
|
| 24 |
},
|
| 25 |
"kernel": {
|
| 26 |
-
"sha": "
|
| 27 |
-
"dirty":
|
| 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
|
| 7 |
-
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 3 |
-
ops = torch.ops.
|
| 4 |
|
| 5 |
def add_op_namespace_prefix(op_name: str):
|
| 6 |
"""
|
| 7 |
Prefix op by namespace.
|
| 8 |
"""
|
| 9 |
-
return f"
|
|
|
|
| 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:
|
| 3 |
-
size
|
|
|
|
| 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": "
|
| 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": "
|
| 14 |
-
"_ops.py": "
|
| 15 |
-
"
|
| 16 |
"physarum/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
|
| 17 |
}
|
| 18 |
},
|
|
@@ -23,8 +23,8 @@
|
|
| 23 |
"dirty": false
|
| 24 |
},
|
| 25 |
"kernel": {
|
| 26 |
-
"sha": "
|
| 27 |
-
"dirty":
|
| 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
|
| 7 |
-
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 3 |
-
ops = torch.ops.
|
| 4 |
|
| 5 |
def add_op_namespace_prefix(op_name: str):
|
| 6 |
"""
|
| 7 |
Prefix op by namespace.
|
| 8 |
"""
|
| 9 |
-
return f"
|
|
|
|
| 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:
|
| 3 |
-
size
|
|
|
|
| 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": "
|
| 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": "
|
| 14 |
-
"_ops.py": "
|
| 15 |
-
"
|
| 16 |
"physarum/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
|
| 17 |
}
|
| 18 |
},
|
|
@@ -23,8 +23,8 @@
|
|
| 23 |
"dirty": false
|
| 24 |
},
|
| 25 |
"kernel": {
|
| 26 |
-
"sha": "
|
| 27 |
-
"dirty":
|
| 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 |
}
|