Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ class TestLiveNeuronVoltage(BaseTestCase):

def test_live_neuron_voltage(self) -> None:
self.runsafe(live_neuron_voltage)
self.check_binaries_used(["external_device_lif_control.aplx",
"external_device_lif_control_neuron.aplx"])


if __name__ == '__main__':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,26 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import numpy
import pyNN.spiNNaker as sim

from spinnaker_testbase import BaseTestCase
import numpy
from spynnaker.pyNN.models.abstract_pynn_model import AbstractPyNNModel


class TestSTDPRandomRun(BaseTestCase):
# Test that reset with STDP and using Random weights results in the
# same thing being loaded twice, both with data generated off and on the
# machine

def do_run(self) -> None:
def run_model(self, model: AbstractPyNNModel) -> None:
"""
Runs the test with the requested model
"""
sim.setup(1.0)
pop = sim.Population(1, sim.IF_curr_exp(i_offset=5.0), label="pop")
pop.record("spikes")
pop_2 = sim.Population(1, sim.IF_curr_exp(), label="pop_2")
pop_2 = sim.Population(1, model, label="pop_2")
proj = sim.Projection(
pop, pop_2, sim.AllToAllConnector(),
sim.STDPMechanism(
Expand All @@ -53,7 +58,28 @@ def do_run(self) -> None:

assert numpy.array_equal(weights_1_1, weights_2_1)
assert numpy.array_equal(weights_1_2, weights_2_2)
assert len(spikes_1[0]) > 0
assert numpy.array_equal(spikes_1, spikes_2)

def test_do_run(self) -> None:
self.runsafe(self.do_run)
def _do_if_curr_exp(self) -> None:
self.run_model(sim.IF_curr_exp())

def test_check_if_curr_exp(self) -> None:
self.runsafe(self._do_if_curr_exp)
self.check_binary_used("IF_curr_exp_stdp_mad_pair_additive.aplx")

def _do_if_curr_exp_ca2_additive(self) -> None:
self.run_model(sim.extra_models.IFCurrExpCa2Adaptive())

def test_check_if_curr_exp_ca2_additive(self) -> None:
self.runsafe(self._do_if_curr_exp_ca2_additive)
self.check_binary_used(
"IF_curr_exp_ca2_adaptive_stdp_mad_pair_additive.aplx")

def _do_izk_cond_exp_dual(self) -> None:
self.run_model(sim.extra_models.Izhikevich_cond_dual())

def test_check_izk_cond_exp_dual(self) -> None:
self.runsafe(self._do_izk_cond_exp_dual)
self.check_binary_used(
"IZK_cond_exp_dual_stdp_mad_pair_additive.aplx")
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

class TestSTDPNearestPairAdditive(BaseTestCase):

def potentiation_and_depression(self) -> None:
def potentiation_and_depression(self, n_synapse_cores: int) -> None:
p.setup(1)
runtime = 100
initial_run = 1000 # to negate any initial conditions
Expand Down Expand Up @@ -54,7 +54,8 @@ def potentiation_and_depression(self) -> None:
{'spike_times': extra_spikes}, label="extra")

# Post-plastic-synapse population
post_pop = p.Population(1, p.IF_curr_exp(), label="post")
post_pop = p.Population(1, p.IF_curr_exp(), label="post",
n_synapse_cores=n_synapse_cores)

# Create projections
p.Projection(
Expand Down Expand Up @@ -136,8 +137,20 @@ def potentiation_and_depression(self) -> None:
self.assertTrue(numpy.allclose(
weights[0], new_weight_exact, atol=0.001))

def do_synapse(self) -> None:
self.potentiation_and_depression(1)

def test_potentiation_and_depression(self) -> None:
self.runsafe(self.potentiation_and_depression)
self.runsafe(self.do_synapse)
self.check_binary_used("synapses_stdp_mad_nearest_pair_additive.aplx")

def do_combined(self) -> None:
self.potentiation_and_depression(0)

def test_combined(self) -> None:
self.runsafe(self.do_combined)
self.check_binary_used(
"IF_curr_exp_stdp_mad_nearest_pair_additive.aplx")


if __name__ == '__main__':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@

class TestSTDPNearestPairAdditive(BaseTestCase):

def potentiation_and_depression(self) -> None:
def potentiation_and_depression(self, n_synapse_cores: int) -> None:
"""
Runs the test with or without synapse cores

:param n_synapse_cores: Numer of synapse cores to use. Zero = combined
"""
p.setup(1)
runtime = 100
initial_run = 1000 # to negate any initial conditions
Expand Down Expand Up @@ -54,7 +59,8 @@ def potentiation_and_depression(self) -> None:
{'spike_times': extra_spikes}, label="extra")

# Post-plastic-synapse population
post_pop = p.Population(1, p.IF_curr_exp(), label="post")
post_pop = p.Population(1, p.IF_curr_exp(), label="post",
n_synapse_cores=n_synapse_cores)

# Create projections
p.Projection(
Expand Down Expand Up @@ -136,8 +142,21 @@ def potentiation_and_depression(self) -> None:
self.assertTrue(numpy.allclose(
weights[0], new_weight_exact, atol=0.001))

def do_synapse(self) -> None:
self.potentiation_and_depression(1)

def test_potentiation_and_depression(self) -> None:
self.runsafe(self.potentiation_and_depression)
self.runsafe(self.do_synapse)
self.check_binary_used(
"synapses_stdp_mad_nearest_pair_multiplicative.aplx")

def do_combined(self) -> None:
self.potentiation_and_depression(0)

def test_combined(self) -> None:
self.runsafe(self.do_combined)
self.check_binary_used(
"IF_curr_exp_stdp_mad_nearest_pair_multiplicative.aplx")


if __name__ == '__main__':
Expand Down
54 changes: 41 additions & 13 deletions spynnaker_integration_tests/test_stdp/test_STDP_neuromodulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import math
from typing import cast, List
import unittest

import numpy
import pyNN.spiNNaker as sim

from spinnaker_testbase import BaseTestCase
import numpy
import unittest
import math

from spynnaker.pyNN.models.neuron import ConnectionHolder
from spynnaker.pyNN.models.neuron.plasticity.stdp.weight_dependence import (
AbstractWeightDependence)

class TestSTDPNeuromodulation(BaseTestCase):

def neuromodulation(self) -> None:
class TestSTDPNeuromodulation(BaseTestCase):

def neuromodulation(self, weight_dependence: AbstractWeightDependence
) -> ConnectionHolder:
"""
Simple test for neuromodulated STDP.
Two pre-synaptic spikes are added, at times 1500 and 2400ms.
Expand Down Expand Up @@ -97,8 +104,7 @@ def neuromodulation(self) -> None:
timing_dependence=sim.SpikePairRule(
tau_plus=10, tau_minus=12,
A_plus=1, A_minus=1),
weight_dependence=sim.AdditiveWeightDependence(
w_min=0, w_max=20),
weight_dependence=weight_dependence,
weight=rewarded_syn_weight)

# Create a plastic connection between pre and post neurons
Expand Down Expand Up @@ -127,6 +133,13 @@ def neuromodulation(self) -> None:

print(spikes)

return weights

def do_additive(self) -> None:
weight_dependence = sim.AdditiveWeightDependence(w_min=0, w_max=20)
weights = self.neuromodulation(weight_dependence)

DA_concentration = 0.1
pot = 1 * math.exp(-((1504 - 1500)/10))
decay = math.exp(-((1601 - 1504)/1000))
el = pot * decay
Expand All @@ -135,15 +148,30 @@ def neuromodulation(self) -> None:
decay_e = math.exp(-((2400 - 1601)/1000))
weight_exact = (
((el * DA_concentration) * const)*((decay_d * decay_e) - 1))

print(f"Weight calculated: {weight_exact}")
print(f"Weight from SpiNNaker: {weights[0][2]}")

weights0 = cast(List[float], weights[0])
print(f"Weight from SpiNNaker: {weights0[2]}")
self.assertTrue(numpy.allclose(
weights[0][2], weight_exact, atol=0.02))
weights0[2], weight_exact, atol=0.02))

self.check_binary_used(
"synapses_stdp_izhikevich_neuromodulation_pair_additive.aplx")

def test_additive(self) -> None:
self.runsafe(self.do_additive)

def do_multiplicative(self) -> None:
weight_dependence = sim.MultiplicativeWeightDependence(
w_min=0, w_max=20)
self.neuromodulation(weight_dependence)

# TODO Weights expected
self.check_binary_used(
"synapses_stdp_izhikevich_neuromodulation_"
"pair_multiplicative.aplx")

def test_neuromodulation(self) -> None:
self.runsafe(self.neuromodulation)
def test_multiplicative(self) -> None:
self.runsafe(self.do_multiplicative)


if __name__ == '__main__':
Expand Down
103 changes: 103 additions & 0 deletions spynnaker_integration_tests/test_various/test_binaries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Copyright (c) 2026 The University of Manchester
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import pyNN.spiNNaker as sim
from spinnaker_testbase import BaseTestCase

from spynnaker.pyNN.models.abstract_pynn_model import AbstractPyNNModel
from spynnaker.pyNN.models.populations import Population

INTERVAL = 50
N_NEURONS = 5


class TestBinaries(BaseTestCase):
"""
Tests limited to checking binaries run and spike
"""

def add_population(self, model: AbstractPyNNModel, weight: int,
input_pop: Population) -> Population:
"""
Add a Populations and input projection for this model in combined mode
"""
population = sim.Population(
N_NEURONS, model, label=model.name)
sim.Projection(input_pop, population, sim.OneToOneConnector(),
synapse_type=sim.StaticSynapse(weight=weight, delay=1))
population.record("spikes")
return population

def add_neuron_population(self, model: AbstractPyNNModel, weight: int,
input_pop: Population) -> Population:
"""
Add a Populations and input projection for this model in split mode
"""
population = sim.Population(
N_NEURONS, model, label=model.name, n_synapse_cores=1)
sim.Projection(input_pop, population, sim.OneToOneConnector(),
synapse_type=sim.StaticSynapse(weight=weight, delay=1))
population.record("spikes")
return population

def check_population(self, population: Population) -> None:
"""
Check the population produced at least 1 spike
"""
neo = population.get_data(variables="spikes")
spikes_trains = neo.segments[0].spiketrains
print(population.label, spikes_trains)
self.assertEqual(len(spikes_trains), N_NEURONS)
for spike_trains in spikes_trains:
self.assertGreaterEqual(len(spikes_trains), 1)

def check_binaries(self) -> None:
"""
Run a toy script with binaries not tested better elsewhere
"""

sim.setup(timestep=1.0)

spike_times = []
for i in range(N_NEURONS):
spike_times.append([i, i + INTERVAL, i + INTERVAL * 2])
input_pop = sim.Population(
N_NEURONS, sim.SpikeSourceArray(spike_times=spike_times),
label="input")

# Ideally there should be better tests for these modules
# remove models tested elsewhere!
populations = []

# IF_curr_delta_ca2_adaptive.aplx
populations.append(self.add_population(
sim.extra_models.IFCurrDeltaCa2Adaptive(), 15, input_pop))

# IF_curr_delta_ca2_adaptive_neuron.aplx
populations.append(self.add_neuron_population(
sim.extra_models.IFCurrDeltaCa2Adaptive(), 15, input_pop))

sim.run(N_NEURONS + INTERVAL * 3)

for population in populations:
self.check_population(population)
sim.end()

self.check_binaries_used([
"IF_curr_delta_ca2_adaptive.aplx",
"IF_curr_delta_ca2_adaptive_neuron.aplx",
])

def test_binaries(self) -> None:
self.runsafe(self.check_binaries)
Loading