Skip to content

Robotics — CPG + Swarm Coupling

Spiking neural network primitives for robotic control: central pattern generation for locomotion and multi-agent spike-based swarm synchronization.

StochasticCPG — Central Pattern Generator

Two mutually inhibiting HomeostaticLIFNeuron instances produce rhythmic alternating spike outputs (e.g., left/right leg stepping). The mutual inhibition ensures anti-phase firing: when neuron 1 fires, its spike trace suppresses neuron 2 via inhibitory current, and vice versa.

Parameter Default Meaning
drive_current 2.0 Tonic excitatory input to both neurons
inhibition_weight 2.0 Strength of mutual inhibition

The step() method returns a (spike1, spike2) tuple on each timestep. The adaptation rate (0.1) and target rate (0.3) of the homeostatic neurons control oscillation frequency.

SwarmCoupling — Multi-Agent Synchronization

Synchronizes two SCLearningLayer agents by shifting their weights toward each other via Hebbian cross-correlation: W_a += α * (W_b - W_a), W_b -= α * (W_b - W_a). After sufficient synchronization steps, both agents converge to identical weight configurations.

Parameter Default Meaning
coupling_strength 0.1 Fraction of weight difference applied per step

Both agents must have the same neuron count (raises ValueError otherwise).

Usage

from sc_neurocore.robotics import StochasticCPG, SwarmCoupling

# Locomotion CPG
cpg = StochasticCPG(drive_current=2.0, inhibition_weight=2.0)
left_steps, right_steps = [], []
for _ in range(500):
    s1, s2 = cpg.step()
    left_steps.append(s1)
    right_steps.append(s2)
print(f"Left spikes: {sum(left_steps)}, Right spikes: {sum(right_steps)}")

# Swarm synchronization
from sc_neurocore.layers.sc_learning_layer import SCLearningLayer
agent_a = SCLearningLayer(n_inputs=8, n_neurons=4)
agent_b = SCLearningLayer(n_inputs=8, n_neurons=4)
coupler = SwarmCoupling(coupling_strength=0.3)
for _ in range(20):
    coupler.synchronize(agent_a, agent_b)
# Weights now converged

sc_neurocore.robotics

sc_neurocore.robotics -- Tier: research (experimental / research).

StochasticCPG dataclass

Central Pattern Generator using two mutually inhibiting neurons. Generates rhythmic alternating outputs (e.g., Left/Right leg).

Source code in src/sc_neurocore/robotics/cpg.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@dataclass
class StochasticCPG:
    """
    Central Pattern Generator using two mutually inhibiting neurons.
    Generates rhythmic alternating outputs (e.g., Left/Right leg).
    """

    drive_current: float = 2.0
    inhibition_weight: float = 2.0

    def __post_init__(self) -> None:
        # High adaptation rate to force switching
        self.n1 = HomeostaticLIFNeuron(v_threshold=1.0, adaptation_rate=0.1, target_rate=0.3)
        self.n2 = HomeostaticLIFNeuron(v_threshold=1.0, adaptation_rate=0.1, target_rate=0.3)

        self.s1_trace = 0.0
        self.s2_trace = 0.05  # Small asymmetry to break initial symmetry
        self.decay = 0.8

    def step(self) -> tuple[int, int]:
        # Inhibition logic:
        # Input to N1 = Drive - Weight * N2_Activity
        # Input to N2 = Drive - Weight * N1_Activity

        # We use a trace of spikes for inhibition "potential"
        i1 = self.drive_current - self.inhibition_weight * self.s2_trace
        i2 = self.drive_current - self.inhibition_weight * self.s1_trace

        spike1 = self.n1.step(i1)
        spike2 = self.n2.step(i2)

        # Update traces
        self.s1_trace = self.s1_trace * self.decay + spike1
        self.s2_trace = self.s2_trace * self.decay + spike2

        return spike1, spike2

SwarmCoupling dataclass

Brain-to-Brain coupling for SC Networks (Swarm Intelligence). Synchronizes two agents via mutual spike injection.

Source code in src/sc_neurocore/robotics/swarm.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@dataclass
class SwarmCoupling:
    """
    Brain-to-Brain coupling for SC Networks (Swarm Intelligence).
    Synchronizes two agents via mutual spike injection.
    """

    coupling_strength: float = 0.1

    def synchronize(self, agent_a: SCLearningLayer, agent_b: SCLearningLayer) -> None:
        """
        Adjust weights of both agents to align their firing patterns.
        (Simplified: Hebbian cross-correlation update)
        """
        # We assume both agents have same number of neurons
        if agent_a.n_neurons != agent_b.n_neurons:
            raise ValueError("Agents must have same size for direct coupling.")

        # Extract weights
        wa = agent_a.get_weights()
        wb = agent_b.get_weights()

        # Mutual Attraction: Weights shift toward each other
        # W_new = W + alpha * (W_other - W)
        delta = self.coupling_strength * (wb - wa)

        # Update Agent A
        new_wa = wa + delta
        for i in range(agent_a.n_neurons):
            for j in range(agent_a.n_inputs):
                agent_a.synapses[i][j].update_weight(new_wa[i, j])

        # Update Agent B (Reciprocal)
        new_wb = wb - delta
        for i in range(agent_b.n_neurons):
            for j in range(agent_b.n_inputs):
                agent_b.synapses[i][j].update_weight(new_wb[i, j])

        logger.info(
            "Swarm Synchronization: Shifted weights by magnitude %.6f", np.mean(np.abs(delta))
        )

synchronize(agent_a, agent_b)

Adjust weights of both agents to align their firing patterns. (Simplified: Hebbian cross-correlation update)

Source code in src/sc_neurocore/robotics/swarm.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
def synchronize(self, agent_a: SCLearningLayer, agent_b: SCLearningLayer) -> None:
    """
    Adjust weights of both agents to align their firing patterns.
    (Simplified: Hebbian cross-correlation update)
    """
    # We assume both agents have same number of neurons
    if agent_a.n_neurons != agent_b.n_neurons:
        raise ValueError("Agents must have same size for direct coupling.")

    # Extract weights
    wa = agent_a.get_weights()
    wb = agent_b.get_weights()

    # Mutual Attraction: Weights shift toward each other
    # W_new = W + alpha * (W_other - W)
    delta = self.coupling_strength * (wb - wa)

    # Update Agent A
    new_wa = wa + delta
    for i in range(agent_a.n_neurons):
        for j in range(agent_a.n_inputs):
            agent_a.synapses[i][j].update_weight(new_wa[i, j])

    # Update Agent B (Reciprocal)
    new_wb = wb - delta
    for i in range(agent_b.n_neurons):
        for j in range(agent_b.n_inputs):
            agent_b.synapses[i][j].update_weight(new_wb[i, j])

    logger.info(
        "Swarm Synchronization: Shifted weights by magnitude %.6f", np.mean(np.abs(delta))
    )