beam-boot / beamboot /tensor_memory.py
StarAtNyte1's picture
Publish Beam Boot / Super Beam source
324ecd2 verified
Raw History Blame Contribute Delete
2.7 kB
"""Bounded cache memory using PyTorch's scaled-dot-product attention.
This is a constructed attention experiment, not Beam's attention implementation.
"""
import torch
import torch.nn.functional as F
class TensorMemory:
def __init__(self, address_bits=4, max_writes=256, window=None, dtype=torch.float32):
if address_bits < 1 or max_writes < 1 or (window is not None and window < 1):
raise ValueError('invalid configuration')
self.address_bits = address_bits
self.max_writes = max_writes
self.window = window
self.dtype = dtype
self.keys = []
self.values = []
# Initial zeros establish all addresses, removing missing-key checks.
for address in range(2**address_bits):
self._append(address, 0, 0)
self.writes = 0
def _bits(self, address):
if not isinstance(address, int) or not 0 <= address < 2**self.address_bits:
raise ValueError('address out of range')
return [1.0 if address & (1 << i) else -1.0 for i in range(self.address_bits)]
def _append(self, address, value, position):
# Matching an address dominates any possible recency difference, while
# a recency difference of 16 makes the latest match dominate softmax.
self.keys.append(self._bits(address) + [float(position)])
self.values.append([float(value)])
def write(self, address, value):
if self.writes >= self.max_writes:
raise ValueError('write budget exhausted')
if not isinstance(value, int) or not 0 <= value <= 255:
raise ValueError('value must be a byte')
self._bits(address)
self.writes += 1
self._append(address, value, self.writes)
def read(self, address):
bits = self._bits(address)
# An address mismatch loses at least 2 * (16 * (max_writes + 1)),
# exceeding the full position score span. PyTorch scale=1 makes these
# logits explicit, without an additive score mask.
query = bits + [1.0]
keys = self.keys[-self.window:] if self.window else self.keys
values = self.values[-self.window:] if self.window else self.values
q = torch.tensor(query, dtype=self.dtype).reshape(1, 1, 1, -1)
k = torch.tensor(keys, dtype=self.dtype).reshape(1, 1, len(keys), -1)
v = torch.tensor(values, dtype=self.dtype).reshape(1, 1, len(values), 1)
q[..., :-1] *= 16 * (self.max_writes + 1)
q[..., -1] *= 16
with torch.no_grad():
return float(F.scaled_dot_product_attention(q, k, v, scale=1.0).item())
def read_byte(self, address):
return round(self.read(address))