From a62d3e81d347cc35c7fd5f740911d804877a352b Mon Sep 17 00:00:00 2001 From: Sam Yates <yates@cscs.ch> Date: Tue, 5 Feb 2019 16:08:32 +0100 Subject: [PATCH] Add test for correct delivery of events to individual cells in mc_cell_group. (#684) * Add unit test for event delivery pathing. * Temporarily disable GJ-specific test pending GJ PR. --- test/unit/CMakeLists.txt | 1 + test/unit/test_event_delivery.cpp | 130 ++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 test/unit/test_event_delivery.cpp diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index f64b834b..977372b9 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -63,6 +63,7 @@ set(unit_sources test_domain_decomposition.cpp test_either.cpp test_event_binner.cpp + test_event_delivery.cpp test_event_generators.cpp test_event_queue.cpp test_filter.cpp diff --git a/test/unit/test_event_delivery.cpp b/test/unit/test_event_delivery.cpp new file mode 100644 index 00000000..d521f8bf --- /dev/null +++ b/test/unit/test_event_delivery.cpp @@ -0,0 +1,130 @@ +// Check that events are routed correctly to underlying cell implementations. +// +// Model: +// * N identical cells, one synapse and gap junction site each. +// * Set up dynamics so that one incoming spike generates one outgoing spike. +// * Inject events one per cell in a given order, and confirm generated spikes +// are in the same order. + +// Note: this test anticipates the gap junction PR; the #if guards below +// will be removed when that PR is merged. + +#define NO_GJ_YET + +#include <arbor/common_types.hpp> +#include <arbor/domain_decomposition.hpp> +#include <arbor/simulation.hpp> +#include <arbor/mc_cell.hpp> +#include <arbor/spike.hpp> +#include <arbor/spike_event.hpp> + +#include "util/rangeutil.hpp" +#include "util/transform.hpp" + +#include "../simple_recipes.hpp" +#include "../gtest.h" + +using namespace arb; + +using n_mc_cell_recipe = homogeneous_recipe<cell_kind::cable1d_neuron, mc_cell>; + +struct test_recipe: public n_mc_cell_recipe { + explicit test_recipe(int n): n_mc_cell_recipe(n, test_cell()) {} + + static mc_cell test_cell() { + mc_cell c; + c.add_soma(10.)->add_mechanism("pas"); + c.add_synapse({0, 0.5}, "expsyn"); + c.add_detector({0, 0.5}, -64); +#ifndef NO_GJ_YET + c.add_gap_junction({0, 0.5}); +#endif + return c; + } + + cell_size_type num_sources(cell_gid_type) const override { return 1; } + cell_size_type num_targets(cell_gid_type) const override { return 1; } +}; + +using gid_vector = std::vector<cell_gid_type>; +using group_gids_type = std::vector<gid_vector>; + +std::vector<cell_gid_type> run_test_sim(const recipe& R, const group_gids_type& group_gids) { + arb::context ctx = make_context(proc_allocation{}); + unsigned n = R.num_cells(); + + domain_decomposition D; + D.gid_domain = [](cell_gid_type) { return 0; }; + D.num_domains = 1; + D.num_local_cells = n; + D.num_global_cells = n; + + for (const auto& gidvec: group_gids) { + group_description group{cell_kind::cable1d_neuron, gidvec, backend_kind::multicore}; + D.groups.push_back(group); + } + + std::vector<spike> spikes; + + simulation sim(R, D, ctx); + sim.set_global_spike_callback( + [&spikes](const std::vector<spike>& ss) { + spikes.insert(spikes.end(), ss.begin(), ss.end()); + }); + + constexpr time_type ev_delta_t = 0.2; + + pse_vector cell_events; + for (unsigned i = 0; i<n; ++i) { + cell_events.push_back({{i, 0u}, i*ev_delta_t, 1.f}); + } + + sim.inject_events(cell_events); + sim.run((n+1)*ev_delta_t, 0.01); + + std::vector<cell_gid_type> spike_gids; + util::sort_by(spikes, [](auto s) { return s.time; }); + util::assign(spike_gids, util::transform_view(spikes, [](auto s) { return s.source.gid; })); + + return spike_gids; +} + + +TEST(mc_event_delivery, one_cell_per_group) { + gid_vector spike_gids = run_test_sim(test_recipe(10), {{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}}); + EXPECT_EQ((gid_vector{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}), spike_gids); +} + +TEST(mc_event_delivery, two_interleaved_groups) { + gid_vector spike_gids = run_test_sim(test_recipe(10), {{0, 2, 4, 6, 8}, {1, 3, 5, 7, 9}}); + EXPECT_EQ((gid_vector{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}), spike_gids); +} + +#ifndef NO_GJ_YET + +typedef std::vector<std::pair<unsigned, unsigned>> cell_gj_pairs; + +struct test_recipe_gj: public test_recipe { + explicit test_recipe_gj(int n, cell_gj_pairs gj_pairs): + test_recipe(n), gj_pairs_(std::move(gj_pairs)) {} + + cell_size_type num_gap_junctions(cell_gid_type) const override { return 1; } + + std::vector<gap_junction_connection> gap_junctions_on(cell_gid_type i) const override { + std::vector<gap_junction_connection> gjs; + for (auto p: gj_pairs_) { + if (p.first == i || p.second == i) gjs.push_back({{p.first, 0u}, {p.second, 0u}, 0.}); + } + return gjs; + } + + cell_gj_pairs gj_pairs_; +}; + +TEST(mc_event_delivery, gj_reordered) { + test_recipe_gj R(5, {{1u, 3u}, {2u, 4u}}); + gid_vector spike_gids = run_test_sim(R, {{0, 1, 2, 3, 4}}); + EXPECT_EQ((gid_vector{0, 1, 2, 3, 4}), spike_gids); +} + +#endif // ndef NO_GJ_YET -- GitLab