File size: 2,969 Bytes
032a961 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | """Deterministic hashing utilities shared between the Python prototype and the
TypeScript web port.
Every function here is specified to be bit-identical to its TypeScript
counterpart (see web demo ``src/lib/spira/hashing.ts``). All arithmetic is
64-bit unsigned wrapped (Python ints / BigInt in TS) or 32-bit unsigned where
noted. No platform-dependent behaviour is allowed in this module.
"""
from __future__ import annotations
MASK32 = 0xFFFFFFFF
MASK64 = 0xFFFFFFFFFFFFFFFF
FNV_OFFSET_BASIS = 0xCBF29CE484222325
FNV_PRIME_64 = 0x100000001B3
# Salts for hippocampal lane keys (one per context slot).
LANE_SALTS = (
0x9E3779B97F4A7C15,
0xBF58476D1CE4E5B9,
0x94D049BB133111EB,
0x2545F4914F6DDC1D,
)
def fnv1a64(data: bytes, seed: int = 0) -> int:
"""FNV-1a 64-bit hash of a byte string, with an optional seed XORed into
the offset basis. Used to content-address EPT patches."""
h = (FNV_OFFSET_BASIS ^ (seed & MASK64)) & MASK64
for b in data:
h ^= b
h = (h * FNV_PRIME_64) & MASK64
return h
def splitmix64(x: int) -> int:
"""splitmix64 finalizer (MurmurHash3 helper). 64-bit in, 64-bit out."""
z = (x + 0x9E3779B97F4A7C15) & MASK64
z = ((z ^ (z >> 30)) * 0xBF58476D1CE4E5B9) & MASK64
z = ((z ^ (z >> 27)) * 0x94D049BB133111EB) & MASK64
return (z ^ (z >> 31)) & MASK64
def lane_key32(patch_id: int, lane: int) -> int:
"""32-bit key for one hippocampal lane (context slot ``lane``).
High 32 bits of splitmix64 give excellent avalanche while staying
representable as a JS ``number`` (via >>> ops) in the TypeScript port.
"""
return splitmix64(patch_id ^ LANE_SALTS[lane]) >> 32
class Mulberry32:
"""mulberry32 PRNG (bryc, public domain).
Produces the exact same float sequence as the TS implementation, so
network weights regenerated from a seed are bit-identical across
languages. Returns uniforms in [0, 1).
"""
__slots__ = ("state",)
def __init__(self, seed: int) -> None:
self.state = seed & MASK32
def next_float(self) -> float:
self.state = (self.state + 0x6D2B79F5) & MASK32
t = self.state
t = ((t ^ (t >> 15)) * (t | 1)) & MASK32
t = (t ^ (t + ((((t ^ (t >> 7)) * (t | 61)) & MASK32)))) & MASK32
return ((t ^ (t >> 14)) & MASK32) / 4294967296.0
def next_weight(self, scale: float) -> float:
"""Symmetric uniform weight in [-scale, scale]."""
return (self.next_float() * 2.0 - 1.0) * scale
def input_neurons(patch_id: int, d_in: int, k_in: int) -> list[int]:
"""Map a patch id to ``k_in`` input-neuron indices (hashing trick).
No embedding table: the projection is a pure function of the patch id,
so any patch ever seen (or never seen) projects consistently.
"""
out = []
for j in range(k_in):
h = splitmix64(patch_id ^ (0x51ED270B733D4C31 + j * 0x9E3779B97F4A7C15))
out.append((h >> 32) % d_in)
return out
|