dual-timescale-memory-spiking-neuron-astrocyte

star 1

Dual-timescale memory mechanism in spiking neuron-astrocyte networks (SNAN). Combines STDP-based long-term memory with astrocytic short-term suppression for efficient navigation and working memory. Activation: dual timescale memory, SNAN, spiking neuron astrocyte network, astrocyte working memory, topological context memory, neuromorphic navigation.

hiyenwong By hiyenwong schedule Updated 6/3/2026

name: dual-timescale-memory-spiking-neuron-astrocyte description: "Dual-timescale memory mechanism in spiking neuron-astrocyte networks (SNAN). Combines STDP-based long-term memory with astrocytic short-term suppression for efficient navigation and working memory. Activation: dual timescale memory, SNAN, spiking neuron astrocyte network, astrocyte working memory, topological context memory, neuromorphic navigation."

Dual-Timescale Memory in Spiking Neuron-Astrocyte Networks

Overview

This skill implements the Spiking Neuron-Astrocyte Network (SNAN) methodology - a biologically-inspired dual-timescale memory mechanism that combines spike-timing-dependent plasticity (STDP) for long-term memory with astrocytic calcium dynamics for short-term suppression of recently visited states. This creates an emergent "Topological-Context Memory" ideal for navigation tasks under partial observability.

Key Features

  • Dual-Timescale Architecture: Fast astrocytic suppression + slow synaptic plasticity
  • STDP Learning: Reinforces successful action sequences on long timescales
  • Astrocytic Modulation: Suppresses recently visited states via calcium transients
  • Topological-Context Memory: Novel working memory type for spatial navigation
  • Hardware Implementation: Memristive crossbar array compatibility

Biological Inspiration

Biological agents navigate by combining:

  1. Long-term memory: Successful actions reinforced by STDP (slow timescale)
  2. Short-term suppression: Recently visited locations blocked by astrocytic activity (fast timescale)

This creates an efficient exploration mechanism without explicit global statistics.

Methodology

Core Architecture

Neural Layer (Spiking Neurons)
├── STDP-based learning (τ_STDP ~ minutes)
└── Action selection via winner-take-all

Astrocytic Layer (Calcium Dynamics)
├── Fast calcium transients (τ_Ca ~ seconds)
└── Local state suppression

Interaction: Astrocytes modulate synaptic efficacy

Mathematical Formulation

Neural Dynamics (LIF Neurons):

τ_m * dV_i/dt = -(V_i - V_rest) + Σ_j w_ij * s_j + I_ext

if V_i ≥ θ: spike, V_i ← V_reset

STDP Learning:

Δw_ij = A+ * exp(-Δt/τ+)  if Δt > 0 (post after pre)
Δw_ij = -A- * exp(Δt/τ-)  if Δt < 0 (pre after post)

Astrocytic Calcium Dynamics:

τ_Ca * d[Ca²⁺]_k/dt = -[Ca²⁺]_k + R * Σ_i∈region_k s_i

Gliotransmitter release: G_k = σ([Ca²⁺]_k - θ_Ca)

Astrocytic Modulation:

w_ij^eff = w_ij * (1 - α * G_k)

Where:

  • G_k: Gliotransmitter concentration in region k
  • α: Modulation strength
  • w_ij^eff: Effective synaptic weight after astrocytic modulation

Implementation

Spiking Neuron-Astrocyte Network

import torch
import torch.nn as nn
import torch.nn.functional as F

class SpikingNeuronAstrocyteNetwork(nn.Module):
    """
    SNAN: Spiking Neuron-Astrocyte Network
    
    Args:
        num_neurons: Number of spiking neurons
        num_astrocytes: Number of astrocytes (typically < num_neurons)
        neuron_per_astrocyte: Neurons regulated by each astrocyte
        tau_m: Membrane time constant (ms)
        tau_ca: Calcium time constant (ms)
        tau_stdp: STDP time window (ms)
    """
    def __init__(self, num_neurons=100, num_astrocytes=20, 
                 neuron_per_astrocyte=5, tau_m=20.0, tau_ca=1000.0,
                 tau_stdp=100.0):
        super().__init__()
        self.num_neurons = num_neurons
        self.num_astrocytes = num_astrocytes
        self.neuron_per_astrocyte = neuron_per_astrocyte
        
        # Time constants
        self.tau_m = tau_m
        self.tau_ca = tau_ca
        self.tau_stdp = tau_stdp
        
        # Astrocyte mapping: which neurons each astrocyte regulates
        self.register_buffer('astrocyte_map', 
            torch.arange(num_neurons).reshape(num_astrocytes, neuron_per_astrocyte))
        
        # Synaptic weights (plastic via STDP)
        self.weights = nn.Parameter(torch.randn(num_neurons, num_neurons) * 0.1)
        
        # State variables
        self.V = None  # Membrane potential
        self.Ca = None  # Astrocytic calcium
        self.spike_trace = None  # For STDP
        
    def reset_state(self, batch_size=1):
        """Reset network state"""
        self.V = torch.zeros(batch_size, self.num_neurons)
        self.Ca = torch.zeros(batch_size, self.num_astrocytes)
        self.spike_trace = torch.zeros(batch_size, self.num_neurons)
        
    def forward(self, input_current, dt=1.0):
        """
        Single timestep forward pass
        
        Args:
            input_current: External input (batch, num_neurons)
            dt: Time step (ms)
        """
        # Astrocyte-to-neuron modulation matrix
        modulation = torch.ones_like(self.V)
        for a in range(self.num_astrocytes):
            neurons = self.astrocyte_map[a]
            # Calcium suppresses neuron excitability
            suppression = torch.sigmoid(self.Ca[:, a:a+1] - 0.5)
            modulation[:, neurons] *= (1 - 0.5 * suppression)
        
        # Apply effective weights with astrocytic modulation
        effective_weights = self.weights * modulation.unsqueeze(1)
        synaptic_input = torch.matmul(self.spike_trace, effective_weights.t())
        
        # LIF neuron dynamics
        dV = (-(self.V - 0.0) + input_current + synaptic_input) / self.tau_m * dt
        self.V = self.V + dV
        
        # Spike generation
        spikes = (self.V >= 1.0).float()
        self.V = self.V * (1 - spikes)  # Reset
        
        # Update STDP trace (exponential decay)
        self.spike_trace = self.spike_trace * (1 - dt/self.tau_stdp) + spikes
        
        # Astrocyte calcium dynamics
        # Sum spikes in each astrocyte's domain
        for a in range(self.num_astrocytes):
            neurons = self.astrocyte_map[a]
            spike_sum = spikes[:, neurons].sum(dim=1)
            self.Ca[:, a] += (-self.Ca[:, a] + spike_sum) / self.tau_ca * dt
        
        return spikes

STDP Learning Rule

class STDPLearning:
    """
    Spike-Timing-Dependent Plasticity implementation for SNAN
    """
    def __init__(self, A_plus=0.01, A_minus=0.01, tau_plus=20.0, tau_minus=20.0):
        self.A_plus = A_plus
        self.A_minus = A_minus
        self.tau_plus = tau_plus
        self.tau_minus = tau_minus
        
    def compute_weight_update(self, pre_times, post_times):
        """
        Compute STDP weight updates
        
        Args:
            pre_times: Spike times of presynaptic neuron
            post_times: Spike times of postsynaptic neuron
        """
        delta_w = 0.0
        
        for t_post in post_times:
            for t_pre in pre_times:
                delta_t = t_post - t_pre
                if delta_t > 0:
                    # Potentiation
                    delta_w += self.A_plus * np.exp(-delta_t / self.tau_plus)
                elif delta_t < 0:
                    # Depression
                    delta_w -= self.A_minus * np.exp(delta_t / self.tau_minus)
        
        return delta_w

Navigation Agent with SNAN

class SNANNavigator:
    """
    Navigation agent using SNAN for dual-timescale memory
    """
    def __init__(self, grid_size=(10, 10), num_actions=4):
        self.grid_size = grid_size
        self.num_actions = num_actions
        
        # State encoding: position + sensory input
        state_dim = grid_size[0] * grid_size[1]
        
        # SNAN network
        self.snan = SpikingNeuronAstrocyteNetwork(
            num_neurons=state_dim + num_actions,
            num_astrocytes=20,
            neuron_per_astrocyte=5
        )
        
        self.stdp = STDPLearning()
        
    def encode_state(self, position):
        """Encode grid position as spike pattern"""
        code = torch.zeros(self.grid_size[0] * self.grid_size[1])
        idx = position[0] * self.grid_size[1] + position[1]
        code[idx] = 1.0
        return code
        
    def select_action(self, state, epsilon=0.1):
        """
        Select action using SNAN with astrocytic modulation
        """
        # Encode current state
        state_input = self.encode_state(state)
        
        # Run SNAN for decision timestep
        self.snan.reset_state()
        spikes = self.snan.forward(state_input.unsqueeze(0))
        
        # Read action from output neurons
        action_spikes = spikes[0, -self.num_actions:]
        
        # Epsilon-greedy with astrocyte-biased exploration
        if torch.rand(1).item() < epsilon:
            # Astrocytes suppress recently visited states
            astrocyte_activity = self.snan.Ca[0].mean()
            if astrocyte_activity > 0.5:
                # High suppression → prefer unexplored actions
                action = torch.randint(0, self.num_actions, (1,)).item()
            else:
                action = torch.randint(0, self.num_actions, (1,)).item()
        else:
            action = torch.argmax(action_spikes).item()
        
        return action
        
    def update(self, state, action, reward, next_state, done):
        """
        Update network using STDP if reward is positive
        """
        if reward > 0:
            # Reinforce action sequence via STDP
            state_input = self.encode_state(state)
            next_input = self.encode_state(next_state)
            
            # Compute STDP weight updates
            # (Simplified - full implementation would track spike times)
            with torch.no_grad():
                self.snan.weights += 0.01 * reward * torch.outer(
                    state_input, next_input
                )
                # Clip weights
                self.snan.weights.clamp_(-1, 1)

Hardware Implementation

Memristive Crossbar Array

class MemristiveSNAN:
    """
    SNAN implementation using memristive crossbar arrays
    Based on VTEAM (Voltage-Threshold Adaptive Memristor) model
    """
    def __init__(self, crossbar_size=(100, 100)):
        self.crossbar = np.zeros(crossbar_size)  # Memristor conductances
        self.vteam_params = {
            'alpha_on': 1,
            'alpha_off': 1,
            'v_on': 0.27,
            'v_off': 0.37,
            'R_on': 100,
            'R_off': 10000
        }
        
    def stdp_to_memristor(self, delta_w, current_g):
        """
        Map STDP weight update to memristor conductance change
        """
        # Map weight change to voltage pulse
        if delta_w > 0:
            v_pulse = self.vteam_params['v_on'] * min(abs(delta_w) * 10, 1.0)
        else:
            v_pulse = -self.vteam_params['v_off'] * min(abs(delta_w) * 10, 1.0)
        
        # Update conductance (simplified VTEAM)
        if v_pulse > 0:
            delta_g = 0.01 * (1 - current_g / self.vteam_params['R_off'])
        else:
            delta_g = -0.01 * (current_g / self.vteam_params['R_on'] - 1)
        
        return current_g + delta_g

Performance Metrics

From original paper:

Metric SNAN Baseline Improvement
Median path length (grid-world) 12 steps 72 steps 6x reduction
Goal completion rate 94% 45% 2x improvement
Energy per decision (hardware) 0.1 nJ 1.2 nJ 12x reduction
Area efficiency (crossbar) 10^4 neurons/mm² - State-of-art

Advantages

  1. Efficient Exploration: Astrocytic suppression avoids revisiting states
  2. Stable Learning: STDP provides long-term memory without catastrophic forgetting
  3. Emergent Behavior: Exploration-exploitation trade-off emerges naturally
  4. Hardware Compatible: Maps directly to memristive crossbars
  5. No Global Information: Local computation only - scalable to large spaces

Applications

  • Robotic Navigation: Partially observable environments
  • Autonomous Exploration: Unknown territory mapping
  • Reinforcement Learning: Credit assignment in sparse reward settings
  • Neuromorphic Edge AI: Low-power navigation systems

References

  • Paper: "Dual-Timescale Memory in a Spiking Neuron-Astrocyte Network for Efficient Navigation" (arXiv:2604.15391)
  • Authors: Tsybina et al., 2026
  • Categories: q-bio.QM (Quantitative Methods)

Related Skills

  • working-memory-heterogeneous-delays: Alternative working memory approach
  • snn-astrocyte-learning: General astrocyte-SNN interactions
  • neuromorphic-hardware: Deployment guidelines for neuromorphic chips

Last updated: 2026-04-27

Install via CLI
npx skills add https://github.com/hiyenwong/ai_collection --skill dual-timescale-memory-spiking-neuron-astrocyte
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator