From 9868c28654f11b1af93c360bdb0643dca51e8ffd Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Tue, 10 Feb 2026 11:42:38 +0000 Subject: [PATCH 1/2] add/ adapt tests forrun more binaires --- .../test_live_neuron_voltage.py | 2 + .../test_stdp_random_run.py | 36 +++++++++++-- .../test_STDP_nearest_pair_additive.py | 19 +++++-- .../test_STDP_nearest_pair_multiplicative.py | 25 +++++++-- .../test_stdp/test_STDP_neuromodulation.py | 54 ++++++++++++++----- 5 files changed, 112 insertions(+), 24 deletions(-) diff --git a/spynnaker_integration_tests/test_external_devices/test_live_neuron_voltage.py b/spynnaker_integration_tests/test_external_devices/test_live_neuron_voltage.py index c62cd019be..b8192b7ba7 100644 --- a/spynnaker_integration_tests/test_external_devices/test_live_neuron_voltage.py +++ b/spynnaker_integration_tests/test_external_devices/test_live_neuron_voltage.py @@ -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__': diff --git a/spynnaker_integration_tests/test_multi_call_examples/test_stdp_random_run.py b/spynnaker_integration_tests/test_multi_call_examples/test_stdp_random_run.py index 476ff32af9..583c4444f7 100644 --- a/spynnaker_integration_tests/test_multi_call_examples/test_stdp_random_run.py +++ b/spynnaker_integration_tests/test_multi_call_examples/test_stdp_random_run.py @@ -12,9 +12,11 @@ # 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): @@ -22,11 +24,14 @@ class TestSTDPRandomRun(BaseTestCase): # 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( @@ -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") diff --git a/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_additive.py b/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_additive.py index 46708dfe65..b7c3aadfe7 100644 --- a/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_additive.py +++ b/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_additive.py @@ -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 @@ -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( @@ -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__': diff --git a/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_multiplicative.py b/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_multiplicative.py index 5d01e8ac3d..f6beacc737 100644 --- a/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_multiplicative.py +++ b/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_multiplicative.py @@ -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 @@ -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( @@ -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__': diff --git a/spynnaker_integration_tests/test_stdp/test_STDP_neuromodulation.py b/spynnaker_integration_tests/test_stdp/test_STDP_neuromodulation.py index 53b8707726..dfb1739bc5 100644 --- a/spynnaker_integration_tests/test_stdp/test_STDP_neuromodulation.py +++ b/spynnaker_integration_tests/test_stdp/test_STDP_neuromodulation.py @@ -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. @@ -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 @@ -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 @@ -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__': From ef55d71341c7cc6fddd2e0371683269b38835451 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Tue, 10 Feb 2026 11:52:36 +0000 Subject: [PATCH 2/2] simple test to ruin binaries --- .../test_various/test_binaries.py | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 spynnaker_integration_tests/test_various/test_binaries.py diff --git a/spynnaker_integration_tests/test_various/test_binaries.py b/spynnaker_integration_tests/test_various/test_binaries.py new file mode 100644 index 0000000000..6c0bf6eab9 --- /dev/null +++ b/spynnaker_integration_tests/test_various/test_binaries.py @@ -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)