| name | causal-learning-neural-assemblies |
| description | DIRECT mechanism for causal learning with neural assemblies - local plasticity-based directional learning without backpropagation. Enables neural assembly networks to internalize causal directionality through projection, local plasticity control, and sparse winner selection. |
| tags | ["neuroscience","neural-assemblies","causal-learning","local-plasticity","biologically-plausible"] |
Causal Learning with Neural Assemblies
This skill implements the DIRECT (DIRectional Edge Coupling/Training) mechanism for enabling neural assemblies to learn the direction of causal influence between variables using purely local plasticity operations.
Overview
Neural assemblies are groups of neurons that fire together and strengthen through co-activation. This skill demonstrates how these assemblies can learn causal directionality—an ability not previously shown with traditional neural assembly approaches.
Key Concepts
Neural Assemblies
- Definition: Groups of neurons that exhibit coordinated firing patterns
- Properties: Strengthen through co-activation, form via Hebbian-like plasticity
- Capabilities: Classification, parsing, planning, and now causal learning
DIRECT Mechanism
DIRECT enables directional learning through three core operations:
- Projection: Source and target assemblies are connected via weighted projections
- Local Plasticity Control: Adaptive gain modulation based on co-activation
- Sparse Winner Selection: Winner-take-all dynamics for assembly activation
Causal Direction Learning
Unlike correlation-based learning, DIRECT learns:
- Directionality: Which variable causes which
- Asymmetric relations: A → B is different from B → A
- Intervention effects: How manipulating one variable affects another
When to Use
Use this skill when:
- Building biologically plausible neural networks
- Implementing causal inference without backpropagation
- Designing local-learning-based AI systems
- Modeling directional relationships in neural data
- Creating interpretable causal models
Methodology
Core Algorithm
class NeuralAssembly:
def __init__(self, size, threshold):
self.neurons = np.zeros(size)
self.threshold = threshold
self.projections = {}
def activate(self, input_signal):
"""Sparse winner-take-all activation."""
potentials = input_signal + self.neurons
winners = potentials > self.threshold
self.neurons = potentials * winners
return self.neurons
class DIRECT:
def __init__(self, learning_rate=0.01, gain_schedule="adaptive"):
self.lr = learning_rate
self.gain_schedule = gain_schedule
def train_direction(self, source_assembly, target_assembly,
coactivation_strength, direction="source_to_target"):
"""
Train causal directionality between two assemblies.
Args:
source_assembly: Assembly representing potential cause
target_assembly: Assembly representing potential effect
coactivation_strength: Strength of joint activation
direction: Direction of causal influence to learn
"""
gain = self.compute_adaptive_gain(source_assembly, target_assembly)
direction == :
delta_w = gain * coactivation_strength * .lr
source_assembly.projections[target_assembly] += delta_w
:
delta_w = gain * coactivation_strength * .lr
target_assembly.projections[source_assembly] += delta_w
():
history_score = .get_coactivation_history(assembly_a, assembly_b)
/ ( + history_score)
Training Protocol
class CausalAssemblyNetwork:
def __init__(self):
self.assemblies = {}
self.direct = DIRECT()
def add_assembly(self, name, size, threshold=0.5):
"""Add a new neural assembly."""
self.assemblies[name] = NeuralAssembly(size, threshold)
def train_causal_relation(self, source_name, target_name,
observations, num_epochs=1000):
"""
Train causal direction from observations.
Args:
source_name: Name of source assembly
target_name: Name of target assembly
observations: List of (source_pattern, target_pattern, temporal_order)
num_epochs: Number of training iterations
"""
source = self.assemblies[source_name]
target = self.assemblies[target_name]
for epoch in range(num_epochs):
for source_pattern, target_pattern, temporal_order in observations:
source.activate(source_pattern)
target.activate(target_pattern)
strength = np.dot(source.neurons, target.neurons)
if temporal_order == "source_first":
self.direct.train_direction(
source, target, strength, "source_to_target"
)
elif temporal_order == :
.direct.train_direction(
target, source, strength,
)
Implementation
Step 1: Define Neural Assemblies
import numpy as np
from typing import Dict, List, Tuple
class NeuralAssembly:
"""
Neural assembly with sparse winner-take-all dynamics.
Attributes:
size: Number of neurons in assembly
threshold: Activation threshold for winner selection
activation: Current activation state
projections: Dictionary of outgoing connections
"""
def __init__(self, size: int, threshold: float = 0.5):
self.size = size
self.threshold = threshold
self.activation = np.zeros(size)
self.projections: Dict['NeuralAssembly', np.ndarray] = {}
self.activation_history = []
def activate(self, input_pattern: np.ndarray) -> np.ndarray:
"""
Sparse winner-take-all activation.
Args:
input_pattern: Input activation pattern
Returns:
Activation vector after winner selection
"""
combined = input_pattern + 0.3 * self.activation
k = max(1, int(0.1 * self.size))
top_k_indices = np.argsort(combined)[-k:]
self.activation = np.zeros_like(combined)
.activation[top_k_indices] = combined[top_k_indices]
.activation_history.append(.activation.copy())
.activation
():
weight_matrix :
weight_matrix = np.random.randn(.size, target.size) *
.projections[target] = weight_matrix
() -> [, np.ndarray]:
outputs = {}
target, weights .projections.items():
outputs[target] = .activation @ weights
outputs
Step 2: Implement DIRECT Learning
class DIRECTLearner:
"""
DIRECT (DIRectional Edge Coupling/Training) learner.
Implements causal direction learning through local plasticity
operations without backpropagation.
"""
def __init__(self,
learning_rate: float = 0.01,
gain_decay: float = 0.95,
min_gain: float = 0.1):
self.lr = learning_rate
self.gain_decay = gain_decay
self.min_gain = min_gain
self.coactivation_counts: Dict[Tuple, int] = {}
self.gains: Dict[Tuple, float] = {}
def train_causal_edge(self,
source: NeuralAssembly,
target: NeuralAssembly,
temporal_order: str = "source_first",
coactivation_strength: float = None):
"""
Train causal direction on a directed edge.
Args:
source: Source assembly (potential cause)
target: Target assembly (potential effect)
temporal_order: "source_first" or "target_first"
coactivation_strength: Override strength computation
"""
assembly_pair = (id(source), id(target))
if assembly_pair not in self.gains:
.gains[assembly_pair] =
.coactivation_counts[assembly_pair] =
coactivation_strength :
coactivation_strength = np.dot(
source.activation,
target.activation
)
current_gain = .gains[assembly_pair]
temporal_order == :
target source.projections:
delta = current_gain * coactivation_strength * .lr
source.projections[target] += delta
temporal_order == :
source target.projections:
delta = current_gain * coactivation_strength * .lr
target.projections[source] += delta
.coactivation_counts[assembly_pair] +=
.gains[assembly_pair] = (
.min_gain,
/ ( + * .coactivation_counts[assembly_pair])
)
() -> [, ]:
forward_strength =
backward_strength =
target source.projections:
forward_strength = np.linalg.norm(source.projections[target])
source target.projections:
backward_strength = np.linalg.norm(target.projections[source])
total = forward_strength + backward_strength
total > :
{
: forward_strength / total,
: backward_strength / total,
: forward_strength > backward_strength
}
{: }
Step 3: Build Causal Learning Network
class CausalAssemblyNetwork:
"""
Network of neural assemblies capable of causal learning.
"""
def __init__(self):
self.assemblies: Dict[str, NeuralAssembly] = {}
self.learner = DIRECTLearner()
self.observations = []
def add_variable(self, name: str, assembly_size: int = 100):
"""Add a variable represented by a neural assembly."""
self.assemblies[name] = NeuralAssembly(assembly_size)
def connect(self, var_a: str, var_b: str,
bidirectional: bool = False):
"""Create connections between variable assemblies."""
assembly_a = self.assemblies[var_a]
assembly_b = self.assemblies[var_b]
assembly_a.project_to(assembly_b)
if bidirectional:
assembly_b.project_to(assembly_a)
def observe(self, var_a: str, var_b: str,
value_a: np.ndarray, value_b: np.ndarray,
temporal_order: str):
"""
Record an observation for causal learning.
Args:
var_a: First variable name
var_b: Second variable name
value_a: Activation pattern for variable A
value_b: Activation pattern for variable B
temporal_order: "a_first", "b_first", or "simultaneous"
"""
.observations.append({
: var_a,
: var_b,
: value_a,
: value_b,
: temporal_order
})
():
epoch (epochs):
obs .observations:
assembly_a = .assemblies[obs[]]
assembly_b = .assemblies[obs[]]
assembly_a.activate(obs[])
assembly_b.activate(obs[])
obs[] == :
order =
obs[] == :
order =
:
.learner.train_causal_edge(
assembly_a, assembly_b, order
)
() -> :
assembly_a = .assemblies[var_a]
assembly_b = .assemblies[var_b]
.learner.test_direction(assembly_a, assembly_b)
Usage Example
network = CausalAssemblyNetwork()
network.add_variable("temperature", assembly_size=100)
network.add_variable("ice_cream_sales", assembly_size=100)
network.connect("temperature", "ice_cream_sales", bidirectional=True)
np.random.seed(42)
for i in range(500):
temp_pattern = np.random.randn(100)
sales_pattern = temp_pattern + np.random.randn(100) * 0.3
network.observe(
"temperature", "ice_cream_sales",
temp_pattern, sales_pattern,
temporal_order="a_first"
)
network.train(epochs=50)
result = network.infer_causality("temperature", "ice_cream_sales")
print(f"Causal direction: {result['direction']}")
Advantages
- Biologically Plausible: Uses only local plasticity, no backpropagation
- Interpretable: Clear causal direction representation
- Efficient: O(n) complexity per learning step
- Flexible: Can learn from temporal patterns in data
Limitations
- Requires Temporal Information: Needs temporal ordering of events
- Sparse Activation: Performance depends on winner-take-all parameters
- Assembly Structure: Requires pre-defined assembly architecture
References
- Paper: "Causal Learning with Neural Assemblies" (arXiv:2604.26919)
- Authors: Evangelia Kopadi, Dimitris Kalles
- Category: cs.LG, Published: 2026-04-29
Related Skills
neural-assembly-learning: General neural assembly operations
synaptic-plasticity: Synaptic plasticity mechanisms
spiking-neural-networks: SNN implementation techniques