algorembrant's picture
Upload 253 files
32d978d verified
Raw
History Blame Contribute Delete
10.4 kB
"""
Sparse Tensor — Lightweight ndarray wrapper with brain-inspired sparsity.
The brain is lazy and sparse: ~1-5% of neurons fire at any moment.
This module provides sparse operations that model this biological constraint.
Sparsity enables "common sense" — knowing ~60% is enough, then filling gaps.
Author: Algorembrant, Rembrant Oyangoren Albeos (2026)
"""
import numpy as np
from typing import Optional, Tuple, Union
class SparseTensor:
"""
A sparse tensor wrapper over NumPy arrays that enforces biological sparsity.
Key biological properties:
- Top-k sparsification (winner-take-all inhibition)
- Threshold activation (firing threshold)
- Lazy computation (only compute when needed)
- Efficient sparse dot products
"""
def __init__(self, data: np.ndarray, sparsity_mask: Optional[np.ndarray] = None):
"""
Args:
data: Dense NumPy array (the raw signal)
sparsity_mask: Boolean mask of active units (True = active/firing)
"""
self._data = np.asarray(data, dtype=np.float64)
if sparsity_mask is not None:
self._mask = np.asarray(sparsity_mask, dtype=bool)
assert self._mask.shape == self._data.shape, \
f"Mask shape {self._mask.shape} != data shape {self._data.shape}"
else:
# Fully dense by default (all active)
self._mask = np.ones(self._data.shape, dtype=bool)
@property
def data(self) -> np.ndarray:
"""Raw underlying data (masked values are zeroed)."""
return self._data * self._mask
@property
def dense(self) -> np.ndarray:
"""Full dense representation (unmasked)."""
return self._data
@property
def mask(self) -> np.ndarray:
"""Boolean sparsity mask: True where units are active."""
return self._mask
@property
def shape(self) -> Tuple[int, ...]:
return self._data.shape
@property
def sparsity(self) -> float:
"""Fraction of zeros (inactive units). 0.0 = fully dense, 1.0 = fully sparse."""
return 1.0 - (np.sum(self._mask) / self._mask.size)
@property
def num_active(self) -> int:
"""Number of active (non-zero) units."""
return int(np.sum(self._mask))
# ----- Sparsification Operations (biological inhibition) -----
def threshold(self, theta: float) -> 'SparseTensor':
"""
Threshold activation — only units above theta fire.
Models neuronal firing threshold / activation threshold.
Args:
theta: Firing threshold
Returns:
New SparseTensor with sub-threshold units masked out
"""
new_mask = self._mask & (np.abs(self._data) >= theta)
return SparseTensor(self._data, new_mask)
def top_k(self, k: int, axis: Optional[int] = None) -> 'SparseTensor':
"""
Top-k sparsification — winner-take-all competitive inhibition.
Only the k strongest activations survive. This is how lateral inhibition
in cortex creates sparse population codes.
Args:
k: Number of top activations to keep
axis: Axis along which to apply top-k (None = global)
Returns:
New SparseTensor with only top-k values active
"""
if axis is None:
flat = np.abs(self._data).ravel()
if k >= flat.size:
return SparseTensor(self._data.copy(), self._mask.copy())
# Find the k-th largest value
threshold_val = np.partition(flat, -k)[-k]
new_mask = self._mask & (np.abs(self._data) >= threshold_val)
# If too many elements equal the threshold, keep only k total
active_count = np.sum(new_mask)
if active_count > k:
active_indices = np.argwhere(new_mask.ravel()).ravel()
active_vals = np.abs(self._data.ravel()[active_indices])
# Sort by value descending, keep only k
sorted_order = np.argsort(-active_vals)
kill = active_indices[sorted_order[k:]]
flat_mask = new_mask.ravel().copy()
flat_mask[kill] = False
new_mask = flat_mask.reshape(self._data.shape)
return SparseTensor(self._data, new_mask)
else:
# Per-slice top-k along axis
new_mask = np.zeros_like(self._mask)
nd = self._data.ndim
slices = [slice(None)] * nd
for i in range(self._data.shape[axis]):
slices[axis] = i
sl = tuple(slices)
vals = np.abs(self._data[sl])
flat = vals.ravel()
actual_k = min(k, flat.size)
if actual_k == flat.size:
new_mask[sl] = self._mask[sl]
else:
thresh = np.partition(flat, -actual_k)[-actual_k]
new_mask[sl] = self._mask[sl] & (vals >= thresh)
return SparseTensor(self._data, new_mask)
def sparsify(self, target_sparsity: float) -> 'SparseTensor':
"""
Achieve a target sparsity level (fraction of zeros).
Brain typically has 95-99% sparsity in any population code.
Args:
target_sparsity: Desired fraction of inactive units (0.0 to 1.0)
Returns:
New SparseTensor with approximately target_sparsity inactive
"""
k = max(1, round((1.0 - target_sparsity) * self._data.size))
return self.top_k(k)
# ----- Activation Functions (neuronal nonlinearities) -----
def relu(self) -> 'SparseTensor':
"""Half-wave rectification — models neuronal firing rate (no negative rates)."""
new_data = np.maximum(self._data, 0.0)
new_mask = self._mask & (new_data > 0.0)
return SparseTensor(new_data, new_mask)
def sigmoid(self, gain: float = 1.0) -> 'SparseTensor':
"""Sigmoidal activation — saturating firing rate."""
new_data = 1.0 / (1.0 + np.exp(-gain * self._data))
return SparseTensor(new_data, self._mask)
def softmax(self, axis: int = -1) -> 'SparseTensor':
"""Softmax normalization — competitive normalization across a population."""
shifted = self._data - np.max(self._data, axis=axis, keepdims=True)
exp_vals = np.exp(shifted) * self._mask
sums = np.sum(exp_vals, axis=axis, keepdims=True)
sums = np.where(sums == 0, 1.0, sums)
new_data = exp_vals / sums
return SparseTensor(new_data, self._mask)
def divisive_normalization(self, sigma: float = 1.0, axis: int = -1) -> 'SparseTensor':
"""
Divisive normalization — the canonical neural computation.
r_i = r_i^n / (sigma^n + sum(r_j^n))
Models gain control in visual cortex.
"""
n = 2.0 # Exponent (typically 2)
powered = np.abs(self.data) ** n
pool = np.sum(powered, axis=axis, keepdims=True)
normalized = powered / (sigma ** n + pool)
# Restore sign
signs = np.sign(self._data)
new_data = signs * (normalized ** (1.0 / n))
return SparseTensor(new_data, self._mask)
# ----- Linear Algebra -----
def dot(self, other: Union['SparseTensor', np.ndarray]) -> 'SparseTensor':
"""
Sparse dot product — only active units contribute.
Efficient because inactive synapses don't transmit.
"""
if isinstance(other, SparseTensor):
result = np.dot(self.data, other.data)
else:
result = np.dot(self.data, np.asarray(other, dtype=np.float64))
return SparseTensor(result)
def outer(self, other: 'SparseTensor') -> 'SparseTensor':
"""Outer product — used for Hebbian learning (pre × post)."""
result = np.outer(self.data.ravel(), other.data.ravel())
return SparseTensor(result)
# ----- Element-wise operations -----
def __add__(self, other: Union['SparseTensor', np.ndarray, float]) -> 'SparseTensor':
if isinstance(other, SparseTensor):
return SparseTensor(self._data + other._data, self._mask | other._mask)
return SparseTensor(self._data + np.float64(other), self._mask)
def __sub__(self, other: Union['SparseTensor', np.ndarray, float]) -> 'SparseTensor':
if isinstance(other, SparseTensor):
return SparseTensor(self._data - other._data, self._mask | other._mask)
return SparseTensor(self._data - np.float64(other), self._mask)
def __mul__(self, other: Union['SparseTensor', np.ndarray, float]) -> 'SparseTensor':
if isinstance(other, SparseTensor):
return SparseTensor(self._data * other._data, self._mask & other._mask)
return SparseTensor(self._data * np.float64(other), self._mask)
def __neg__(self) -> 'SparseTensor':
return SparseTensor(-self._data, self._mask.copy())
def __repr__(self) -> str:
return (f"SparseTensor(shape={self.shape}, "
f"active={self.num_active}/{self._data.size}, "
f"sparsity={self.sparsity:.1%})")
# ----- Utility -----
def copy(self) -> 'SparseTensor':
return SparseTensor(self._data.copy(), self._mask.copy())
def reshape(self, *shape) -> 'SparseTensor':
return SparseTensor(self._data.reshape(*shape), self._mask.reshape(*shape))
def flatten(self) -> 'SparseTensor':
return SparseTensor(self._data.ravel(), self._mask.ravel())
@staticmethod
def from_dense(data: np.ndarray, threshold: float = 0.0) -> 'SparseTensor':
"""Create from dense array, automatically masking near-zero values."""
mask = np.abs(data) > threshold
return SparseTensor(data, mask)
@staticmethod
def zeros(shape: Tuple[int, ...]) -> 'SparseTensor':
return SparseTensor(np.zeros(shape), np.zeros(shape, dtype=bool))
@staticmethod
def ones(shape: Tuple[int, ...]) -> 'SparseTensor':
return SparseTensor(np.ones(shape))
@staticmethod
def random(shape: Tuple[int, ...], sparsity: float = 0.95) -> 'SparseTensor':
"""Random sparse tensor — models spontaneous neural activity."""
data = np.random.randn(*shape)
mask = np.random.random(shape) > sparsity
return SparseTensor(data, mask)