diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index 2e903bfc94f577a6ce78f8486973749aedbd8f4b..e563ce38be1f6aabdb1c7da03ea468a3f1e5291c 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -186,11 +186,11 @@ jobs: - name: Run python tests run: | cd build - python ../python/test/unit/runner.py + python ../python/test/unit/runner.py -v2 cd - - if: ${{ matrix.config.mpi == 'ON' }} name: Run python+MPI tests - run: mpirun -n 4 -oversubscribe python python/test/unit_distributed/runner.py + run: mpirun -n 4 -oversubscribe python python/test/unit_distributed/runner.py -v2 - name: Run Python examples run: | python python/example/network_ring.py @@ -201,4 +201,4 @@ jobs: python python/example/single_cell_swc.py python/example/single_cell_detailed.swc python python/example/single_cell_detailed.py python/example/single_cell_detailed.swc python python/example/single_cell_detailed_recipe.py python/example/single_cell_detailed.swc - python python/example/single_cell_cable.py \ No newline at end of file + python python/example/single_cell_cable.py diff --git a/.github/workflows/sanitize.yml b/.github/workflows/sanitize.yml index 805090d92d13d7acafa657aa8d522900daf701c0..3d6ee63de21e6cb67c2109789be9fe413b3d5d1c 100644 --- a/.github/workflows/sanitize.yml +++ b/.github/workflows/sanitize.yml @@ -113,10 +113,10 @@ jobs: mpirun -n 4 -oversubscribe build/bin/ring mpirun -n 4 -oversubscribe build/bin/single-cell - name: Run python tests - run: python python/test/unit/runner.py + run: python python/test/unit/runner.py -v2 - if: ${{ matrix.mpi == 'ON' }} name: Run python+MPI tests - run: mpirun -n 4 -oversubscribe python python/test/unit_distributed/runner.py + run: mpirun -n 4 -oversubscribe python python/test/unit_distributed/runner.py -v2 - name: Run Python examples run: | python python/example/network_ring.py @@ -127,4 +127,4 @@ jobs: python python/example/single_cell_swc.py python/example/single_cell_detailed.swc python python/example/single_cell_detailed.py python/example/single_cell_detailed.swc python python/example/single_cell_detailed_recipe.py python/example/single_cell_detailed.swc - python python/example/single_cell_cable.py \ No newline at end of file + python python/example/single_cell_cable.py diff --git a/python/test/unit/runner.py b/python/test/unit/runner.py index d27357cf42ec430eb6d12956c809b51ec05475e2..3b12c40260b5445872e5a8eae15a070de86d3e5c 100644 --- a/python/test/unit/runner.py +++ b/python/test/unit/runner.py @@ -10,39 +10,42 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../. try: import options + import test_cable_probes + import test_catalogues import test_contexts + import test_decor import test_domain_decomposition import test_event_generators import test_identifiers - import test_tests - import test_schedules - import test_cable_probes import test_morphology - import test_catalogues + import test_schedules import test_spikes + import test_tests # add more if needed except ModuleNotFoundError: from test import options - from test.unit import test_contexts + from test.unit import test_cable_probes from test.unit import test_catalogues + from test.unit import test_contexts + from test.unit import test_decor from test.unit import test_domain_decompositions from test.unit import test_event_generators from test.unit import test_identifiers - from test.unit import test_schedules - from test.unit import test_cable_probes from test.unit import test_morphology + from test.unit import test_schedules from test.unit import test_spikes # add more if needed test_modules = [\ - test_contexts,\ + test_cable_probes,\ test_catalogues,\ + test_contexts,\ + test_decor,\ test_domain_decompositions,\ test_event_generators,\ test_identifiers,\ - test_schedules,\ - test_cable_probes,\ test_morphology,\ + test_schedules,\ test_spikes,\ ] # add more if needed diff --git a/python/test/unit/test_decor.py b/python/test/unit/test_decor.py index 81fe7e497d530f8138fa70ef23e3ea64a1ef9947..2ec68d753e1845df0005710cfa30f19af8e7283a 100644 --- a/python/test/unit/test_decor.py +++ b/python/test/unit/test_decor.py @@ -24,7 +24,7 @@ class DecorClasses(unittest.TestCase): self.assertEqual(0, clamp.frequency) self.assertEqual([(0, 10)], clamp.envelope) - clamp = A.iclamp(10, 20); + clamp = A.iclamp(10, frequency=20); self.assertEqual(20, clamp.frequency) self.assertEqual([(0, 10)], clamp.envelope) @@ -33,7 +33,7 @@ class DecorClasses(unittest.TestCase): self.assertEqual(0, clamp.frequency) self.assertEqual([(100, 3), (120, 3), (120, 0)], clamp.envelope) - clamp = A.iclamp(100, 20, 3, 7); + clamp = A.iclamp(100, 20, 3, frequency=7); self.assertEqual(7, clamp.frequency) self.assertEqual([(100, 3), (120, 3), (120, 0)], clamp.envelope) @@ -43,7 +43,7 @@ class DecorClasses(unittest.TestCase): self.assertEqual(0, clamp.frequency) self.assertEqual(envelope, clamp.envelope) - clamp = A.iclamp(envelope, 7); + clamp = A.iclamp(envelope, frequency=7); self.assertEqual(7, clamp.frequency) self.assertEqual(envelope, clamp.envelope) diff --git a/python/test/unit/test_simulator.py b/python/test/unit/test_simulator.py deleted file mode 100644 index bee420b3f82bfb926b520be9d4d34d2b585b5827..0000000000000000000000000000000000000000 --- a/python/test/unit/test_simulator.py +++ /dev/null @@ -1,252 +0,0 @@ -# -*- coding: utf-8 -*- -# -# test_simulator.py - -import unittest -import numpy as np -import arbor as A - -# to be able to run .py file from child directory -import sys, os -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../'))) - -try: - import options -except ModuleNotFoundError: - from test import options - -""" -all tests for the simulator wrapper -""" - -# Test recipe cc2 comprises two cable cells and some probes. - -class cc2_recipe(A.recipe): - def __init__(self): - A.recipe.__init__(self) - st = A.segment_tree() - i = st.append(A.mnpos, (0, 0, 0, 10), (1, 0, 0, 10), 1) - st.append(i, (1, 3, 0, 5), 1) - st.append(i, (1, -4, 0, 3), 1) - self.the_morphology = A.morphology(st) - self.the_cat = A.default_catalogue() - self.the_props = A.neuron_cable_properties() - self.the_props.register(self.the_cat) - - def num_cells(self): - return 2 - - def cell_kind(self, gid): - return A.cell_kind.cable - - def connections_on(self, gid): - return [] - - def event_generators(self, gid): - return [] - - def global_properties(self, kind): - return self.the_props - - def probes(self, gid): - # Cell 0 has three voltage probes: - # 0, 0: end of branch 1 - # 0, 1: end of branch 2 - # 0, 2: all terminal points - # Values sampled from (0, 0) and (0, 1) should correspond - # to the values sampled from (0, 2). - - # Cell 1 has whole cell probes: - # 0, 0: all membrane voltages - # 0, 1: all expsyn state variable 'g' - - if gid==0: - return [A.cable_probe_membrane_voltage('(location 1 1)'), - A.cable_probe_membrane_voltage('(location 2 1)'), - A.cable_probe_membrane_voltage('(terminal)')] - elif gid==1: - return [A.cable_probe_membrane_voltage_cell(), - A.cable_probe_point_state_cell('expsyn', 'g')] - else: - return [] - - def cell_description(self, gid): - c = A.cable_cell(self.the_morphology, A.label_dict()) - c.set_properties(Vm=0.0, cm=0.01, rL=30, tempK=300) - c.paint('(all)', "pas") - c.place('(location 0 0)', A.iclamp(current=10 if gid==0 else 20)) - c.place('(sum (on-branches 0.3) (location 0 0.6))', "expsyn") - return c - -# Test recipe lif2 comprises two independent LIF cells driven by a regular, rapid -# sequence of incoming spikes. The cells have differing refactory periods. - -class lif2_recipe(A.recipe): - def __init__(self): - A.recipe.__init__(self) - - def num_cells(self): - return 2 - - def cell_kind(self, gid): - return A.cell_kind.lif - - def connections_on(self, gid): - return [] - - def event_generators(self, gid): - sched_dt = 0.25 - weight = 400 - return [A.event_generator((gid,0), weight, A.regular_schedule(sched_dt)) for gid in range(0, self.num_cells())] - - def probes(self, gid): - return [] - - def cell_description(self, gid): - c = A.lif_cell() - if gid==0: - c.t_ref = 2 - if gid==1: - c.t_ref = 4 - return c - -class Simulator(unittest.TestCase): - def init_sim(self, recipe): - context = A.context() - dd = A.partition_load_balance(recipe, context) - return A.simulation(recipe, dd, context) - - def test_simple_run(self): - sim = self.init_sim(cc2_recipe()) - sim.run(1.0, 0.01) - - def test_probe_meta(self): - sim = self.init_sim(cc2_recipe()) - - self.assertEqual([A.location(1, 1)], sim.probe_metadata((0, 0))) - self.assertEqual([A.location(2, 1)], sim.probe_metadata((0, 1))) - self.assertEqual([A.location(1, 1), A.location(2, 1)], sorted(sim.probe_metadata((0, 2)), key=lambda x:(x.branch, x.pos))) - - # Default CV policy is one per branch, which also gives a tivial CV over the branch point. - # Expect metadata cables to be one for each full branch, plus three length-zero cables corresponding to the branch point. - self.assertEqual([A.cable(0, 0, 1), A.cable(0, 1, 1), A.cable(1, 0, 0), A.cable(1, 0, 1), A.cable(2, 0, 0), A.cable(2, 0, 1)], - sorted(sim.probe_metadata((1,0))[0], key=lambda x:(x.branch, x.prox, x.dist))) - - # Four expsyn synapses; the two on branch zero should be coalesced, giving a multiplicity of 2. - # Expect entries to be in target index order. - m11 = sim.probe_metadata((1,1))[0] - self.assertEqual(4, len(m11)) - self.assertEqual([0, 1, 2, 3], [x.target for x in m11]) - self.assertEqual([2, 2, 1, 1], [x.multiplicity for x in m11]) - self.assertEqual([A.location(0, 0.3), A.location(0, 0.6), A.location(1, 0.3), A.location(2, 0.3)], [x.location for x in m11]) - - def test_probe_scalar_recorders(self): - sim = self.init_sim(cc2_recipe()) - ts = [0, 0.1, 0.3, 0.7] - h = sim.sample((0, 0), A.explicit_schedule(ts)) - dt = 0.01 - sim.run(10., dt) - s, meta = sim.samples(h)[0] - self.assertEqual(A.location(1, 1), meta) - for i, t in enumerate(s[:,0]): - self.assertLess(abs(t-ts[i]), dt) - - sim.remove_sampler(h) - sim.reset() - h = sim.sample(A.cell_member(0, 0), A.explicit_schedule(ts), A.sampling_policy.exact) - sim.run(10., dt) - s, meta = sim.samples(h)[0] - for i, t in enumerate(s[:,0]): - self.assertEqual(t, ts[i]) - - - def test_probe_multi_scalar_recorders(self): - sim = self.init_sim(cc2_recipe()) - ts = [0, 0.1, 0.3, 0.7] - h0 = sim.sample((0, 0), A.explicit_schedule(ts)) - h1 = sim.sample((0, 1), A.explicit_schedule(ts)) - h2 = sim.sample((0, 2), A.explicit_schedule(ts)) - - dt = 0.01 - sim.run(10., dt) - - r0 = sim.samples(h0) - self.assertEqual(1, len(r0)) - s0, meta0 = r0[0] - - r1 = sim.samples(h1) - self.assertEqual(1, len(r1)) - s1, meta1 = r1[0] - - r2 = sim.samples(h2) - self.assertEqual(2, len(r2)) - s20, meta20 = r2[0] - s21, meta21 = r2[1] - - # Probe id (0, 2) has probes over the two locations that correspond to probes (0, 0) and (0, 1). - - # (order is not guaranteed to line up though) - if meta20==meta0: - self.assertEqual(meta1, meta21) - self.assertTrue((s0[:,1]==s20[:,1]).all()) - self.assertTrue((s1[:,1]==s21[:,1]).all()) - else: - self.assertEqual(meta1, meta20) - self.assertTrue((s1[:,1]==s20[:,1]).all()) - self.assertEqual(meta0, meta21) - self.assertTrue((s0[:,1]==s21[:,1]).all()) - - def test_probe_vector_recorders(self): - sim = self.init_sim(cc2_recipe()) - ts = [0, 0.1, 0.3, 0.7] - h0 = sim.sample((1, 0), A.explicit_schedule(ts), A.sampling_policy.exact) - h1 = sim.sample((1, 1), A.explicit_schedule(ts), A.sampling_policy.exact) - sim.run(10., 0.01) - - # probe (1, 0) is the whole cell voltage; expect time + 6 sample values per row in returned data (see test_probe_meta above). - - s0, meta0 = sim.samples(h0)[0] - self.assertEqual(6, len(meta0)) - self.assertEqual((len(ts), 7), s0.shape) - for i, t in enumerate(s0[:,0]): - self.assertEqual(t, ts[i]) - - # probe (1, 1) is the 'g' state for all expsyn synapses. - # With the default descretization, expect two synapses with multiplicity 2 and two with multiplicity 1. - - s1, meta1 = sim.samples(h1)[0] - self.assertEqual(4, len(meta1)) - self.assertEqual((len(ts), 5), s1.shape) - for i, t in enumerate(s1[:,0]): - self.assertEqual(t, ts[i]) - - meta1_mult = {(m.location.branch, m.location.pos): m.multiplicity for m in meta1} - self.assertEqual(2, meta1_mult[(0, 0.3)]) - self.assertEqual(2, meta1_mult[(0, 0.6)]) - self.assertEqual(1, meta1_mult[(1, 0.3)]) - self.assertEqual(1, meta1_mult[(2, 0.3)]) - - def test_spikes(self): - sim = self.init_sim(lif2_recipe()) - sim.record(A.spike_recording.all) - sim.run(21, 0.01) - - spikes = sim.spikes().tolist() - s0 = sorted([t for s, t in spikes if s==(0, 0)]) - s1 = sorted([t for s, t in spikes if s==(1, 0)]) - - self.assertEqual([0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20], s0) - self.assertEqual([0, 4, 8, 12, 16, 20], s1) - -def suite(): - # specify class and test functions in tuple (here: all tests starting with 'test' from class Contexts - suite = unittest.makeSuite(Simulator, ('test')) - return suite - -def run(): - v = options.parse_arguments().verbosity - runner = unittest.TextTestRunner(verbosity = v) - runner.run(suite()) - -if __name__ == "__main__": - run() diff --git a/python/test/unit_distributed/runner.py b/python/test/unit_distributed/runner.py index cb8ddf5d83474329fde6d3e33319ddd92682b798..48c6c3a588a085ea5939052eec6432f864f5d159 100644 --- a/python/test/unit_distributed/runner.py +++ b/python/test/unit_distributed/runner.py @@ -21,6 +21,7 @@ try: import test_contexts_arbmpi import test_contexts_mpi4py import test_domain_decompositions + import test_simulator # add more if needed except ModuleNotFoundError: from test import options