Release SONAPSE-ORIGIN v0.2.0: Neural Organisation Discovery, Causal Emergence, PCI_LZ, and CTC
5b4a3bd verified Download sonapse_origin/structure/development.py from Kai9987kai/SONAPSE-ORIGIN: direct link, hf CLI and curl.
- Browser
- Download file 4.48 kB
-
https://huggingface.co/Kai9987kai/SONAPSE-ORIGIN/resolve/main/sonapse_origin/structure/development.py
- Command line
-
hf download hf://Kai9987kai/SONAPSE-ORIGIN/sonapse_origin/structure/development.py
-
curl -L -o development.py https://huggingface.co/Kai9987kai/SONAPSE-ORIGIN/resolve/main/sonapse_origin/structure/development.py
4.48 kB
| """Neural Development Engine and Structural Plasticity.""" | |
| from __future__ import annotations | |
| from typing import Any, Dict, List, Optional, Tuple | |
| import numpy as np | |
| class StructuralPlasticityEngine: | |
| """Manages structural modifications, synaptogenesis, pruning, and neurogenesis.""" | |
| def __init__( | |
| self, | |
| pruning_threshold: float = 0.05, | |
| growth_correlation_threshold: float = 0.7, | |
| target_activity: float = 0.2, | |
| seed: int = 42, | |
| ): | |
| self.pruning_threshold = pruning_threshold | |
| self.growth_correlation_threshold = growth_correlation_threshold | |
| self.target_activity = target_activity | |
| self.rng = np.random.default_rng(seed) | |
| def prune_connections(self, W: np.ndarray) -> Tuple[np.ndarray, int]: | |
| """Prunes connections with absolute weight below threshold.""" | |
| mask = np.abs(W) >= self.pruning_threshold | |
| pruned_count = int(np.sum(~mask & (W != 0))) | |
| new_W = W * mask | |
| return new_W, pruned_count | |
| def sprout_synapses( | |
| self, | |
| W: np.ndarray, | |
| recent_activity: np.ndarray, | |
| max_new_synapses: int = 5, | |
| ) -> Tuple[np.ndarray, int]: | |
| """Sprouts new connections between highly co-active, currently unconnected units.""" | |
| num_nodes = W.shape[0] | |
| if recent_activity.shape[0] < 2: | |
| return W, 0 | |
| # Compute Pearson correlation matrix across time | |
| cov = np.corrcoef(recent_activity) | |
| np.nan_to_num(cov, copy=False, nan=0.0) | |
| new_W = W.copy() | |
| sprouted = 0 | |
| for i in range(num_nodes): | |
| for j in range(num_nodes): | |
| if i != j and new_W[i, j] == 0: | |
| if cov[i, j] > self.growth_correlation_threshold and self.rng.random() < 0.2: | |
| new_W[i, j] = float(self.rng.normal(0.15, 0.05)) | |
| sprouted += 1 | |
| if sprouted >= max_new_synapses: | |
| return new_W, sprouted | |
| return new_W, sprouted | |
| def homeostatic_synaptic_scaling(self, W: np.ndarray, current_firing_rate: np.ndarray) -> np.ndarray: | |
| """Scales incoming synaptic weights to stabilize target post-synaptic firing rate.""" | |
| # W has shape (num_nodes, num_nodes) where W[i, j] is connection from j to i | |
| rates = np.clip(current_firing_rate, 1e-4, 10.0) | |
| # Multiplicative scaling factor: alpha = target / rate | |
| scaling = (self.target_activity / rates) ** 0.1 # Slow homeostatic adaptation | |
| new_W = W * scaling[:, np.newaxis] | |
| return np.clip(new_W, -3.0, 3.0) | |
| def neurogenesis( | |
| self, | |
| W: np.ndarray, | |
| node_types: List[str], | |
| donor_node_idx: Optional[int] = None, | |
| ) -> Tuple[np.ndarray, List[str], int]: | |
| """Adds a new computational unit to the network.""" | |
| old_n = W.shape[0] | |
| new_n = old_n + 1 | |
| new_W = np.zeros((new_n, new_n), dtype=np.float64) | |
| new_W[:old_n, :old_n] = W | |
| donor = donor_node_idx if donor_node_idx is not None else int(self.rng.integers(0, old_n)) | |
| new_type = node_types[donor] | |
| new_node_types = list(node_types) + [new_type] | |
| # Duplicate a subset of incoming and outgoing synapses with noise | |
| for j in range(old_n): | |
| if W[donor, j] != 0 and self.rng.random() < 0.5: | |
| new_W[old_n, j] = float(W[donor, j] + self.rng.normal(0, 0.02)) | |
| if W[j, donor] != 0 and self.rng.random() < 0.5: | |
| new_W[j, old_n] = float(W[j, donor] + self.rng.normal(0, 0.02)) | |
| return new_W, new_node_types, old_n | |
| class STDPPlasticity: | |
| """Spike-Timing-Dependent Plasticity rule with explicit causal & anti-causal timing windows.""" | |
| def __init__( | |
| self, | |
| A_plus: float = 0.01, | |
| A_minus: float = 0.012, | |
| tau_plus: float = 20.0, | |
| tau_minus: float = 20.0, | |
| ): | |
| self.A_plus = A_plus | |
| self.A_minus = A_minus | |
| self.tau_plus = tau_plus | |
| self.tau_minus = tau_minus | |
| def compute_dW(self, spike_times_pre: np.ndarray, spike_times_post: np.ndarray) -> float: | |
| """Computes weight delta for a pair of spikes at times t_pre and t_post (in ms).""" | |
| dt = spike_times_post - spike_times_pre | |
| if dt > 0: | |
| return float(self.A_plus * np.exp(-dt / self.tau_plus)) | |
| elif dt < 0: | |
| return float(-self.A_minus * np.exp(dt / self.tau_minus)) | |
| return 0.0 | |