diff --git a/arbor/communication/communicator.cpp b/arbor/communication/communicator.cpp index bb206f7989..e258d0226b 100644 --- a/arbor/communication/communicator.cpp +++ b/arbor/communication/communicator.cpp @@ -62,6 +62,7 @@ void communicator::update_connections(const connectivity& rec, PE(init:communicator:update:clear); // Forget all lingering information connections_.clear(); + ext_connections_.clear(); connection_part_.clear(); index_divisions_.clear(); PL(); @@ -123,8 +124,8 @@ void communicator::update_connections(const connectivity& rec, // to do this in place. // NOTE: The connections are partitioned by the domain of their source gid. PE(init:communicator:update:connections); - connections_.resize(n_cons); - ext_connections_.resize(n_ext_cons); + std::vector connections(n_cons); + std::vector ext_connections(n_ext_cons); auto offsets = connection_part_; // Copy, as we use this as the list of current target indices to write into std::size_t ext = 0; auto src_domain = src_domains.begin(); @@ -140,7 +141,7 @@ void communicator::update_connections(const connectivity& rec, auto tgt_lid = target_resolver.resolve(tgt_gid, conn.target); auto offset = offsets[*src_domain]++; ++src_domain; - connections_[offset] = {{src_gid, src_lid}, tgt_lid, conn.weight, conn.delay, index}; + connections[offset] = {{src_gid, src_lid}, tgt_lid, conn.weight, conn.delay, index}; } for (const auto cidx: util::make_span(part_ext_connections[index], part_ext_connections[index+1])) { const auto& conn = gid_ext_connections[cidx]; @@ -148,7 +149,7 @@ void communicator::update_connections(const connectivity& rec, auto src_gid = conn.source.rid; if(is_external(src_gid)) throw arb::source_gid_exceeds_limit(tgt_gid, src_gid); auto tgt_lid = target_resolver.resolve(tgt_gid, conn.target); - ext_connections_[ext] = {src, tgt_lid, conn.weight, conn.delay, index}; + ext_connections[ext] = {src, tgt_lid, conn.weight, conn.delay, index}; ++ext; } } @@ -168,9 +169,14 @@ void communicator::update_connections(const connectivity& rec, const auto& cp = connection_part_; threading::parallel_for::apply(0, num_domains_, thread_pool_.get(), [&](cell_size_type i) { - util::sort(util::subrange_view(connections_, cp[i], cp[i+1])); + util::sort(util::subrange_view(connections, cp[i], cp[i+1])); }); - std::sort(ext_connections_.begin(), ext_connections_.end()); + std::sort(ext_connections.begin(), ext_connections.end()); + PL(); + + PE(init:communicator:update:destructure_connections); + connections_.make(connections); + ext_connections_.make(ext_connections); PL(); } @@ -181,12 +187,12 @@ std::pair communicator::group_queue_range(cell_s time_type communicator::min_delay() { time_type res = std::numeric_limits::max(); - res = std::accumulate(connections_.begin(), connections_.end(), - res, - [](auto&& acc, auto&& el) { return std::min(acc, time_type(el.delay)); }); - res = std::accumulate(ext_connections_.begin(), ext_connections_.end(), - res, - [](auto&& acc, auto&& el) { return std::min(acc, time_type(el.delay)); }); + res = std::accumulate(connections_.delays.begin(), connections_.delays.end(), + res, + [](auto&& acc, time_type del) { return std::min(acc, del); }); + res = std::accumulate(ext_connections_.delays.begin(), ext_connections_.delays.end(), + res, + [](auto&& acc, time_type del) { return std::min(acc, del); }); res = distributed_->min(res); return res; } @@ -228,19 +234,38 @@ void communicator::set_remote_spike_filter(const spike_predicate& p) { remote_sp void communicator::remote_ctrl_send_continue(const epoch& e) { distributed_->remote_ctrl_send_continue(e); } void communicator::remote_ctrl_send_done() { distributed_->remote_ctrl_send_done(); } +// Given +// * a set of connections and an index into the set +// * a range of spikes +// * an output queue, +// append events for that sub-range of spikes to the +// queue that has the same source as the connection +// at index. +template +void enqueue_from_source(const communicator::connection_list& cons, + const size_t idx, + It& spk, + const It end, + std::vector& out) { + // const refs to connection. + auto src = cons.srcs[idx]; + auto dst = cons.dests[idx]; + auto del = cons.delays[idx]; + auto wgt = cons.weights[idx]; + auto dom = cons.idx_on_domain[idx]; + auto& que = out[dom]; + for (; spk != end && spk->source == src; ++spk) { + que.emplace_back(dst, spk->time + del, wgt); + } +} + // Internal helper to append to the event queues -template -void append_events_from_domain(C cons, - S spks, +template +void append_events_from_domain(const communicator::connection_list& cons, size_t cn, const size_t ce, + const S& spks, std::vector& queues) { - // Predicate for partitioning - struct spike_pred { - bool operator()(const spike& spk, const cell_member_type& src) { return spk.source < src; } - bool operator()(const cell_member_type& src, const spike& spk) { return src < spk.source; } - }; - auto sp = spks.begin(), se = spks.end(); - auto cn = cons.begin(), ce = cons.end(); + if (se == sp) return; // We have a choice of whether to walk spikes or connections: // i.e., we can iterate over the spikes, and for each spike search // the for connections that have the same source; or alternatively @@ -251,64 +276,62 @@ void append_events_from_domain(C cons, // complexity of order max(S log(C), C log(S)), where S is the // number of spikes, and C is the number of connections. if (cons.size() < spks.size()) { - while (cn != ce && sp != se) { - auto sources = std::equal_range(sp, se, cn->source, spike_pred()); - for (auto s: util::make_range(sources)) { - queues[cn->index_on_domain].push_back(make_event(*cn, s)); - } - sp = sources.first; - ++cn; + for (; sp != se && cn < ce; ++cn) { + // sp is now the beginning of a range of spikes from the same + // source. + sp = std::lower_bound(sp, se, + cons.srcs[cn], + [](const auto& spk, const auto& src) { return spk.source < src; }); + // now, sp is at the end of the equal source range. + enqueue_from_source(cons, cn, sp, se, queues); } } else { - while (cn != ce && sp != se) { - auto targets = std::equal_range(cn, ce, sp->source); - for (auto c: util::make_range(targets)) { - queues[c.index_on_domain].push_back(make_event(c, *sp)); + while (sp != se) { + auto beg = sp; + auto src = beg->source; + // Here, `cn` is the index of the first connection whose source + // is larger or equal to the spike's source. It may be `ce` if + // all elements compare < to spk.source. + cn = std::lower_bound(cons.srcs.begin() + cn, + cons.srcs.begin() + ce, + src) + - cons.srcs.begin(); + for (; cn < ce && cons.srcs[cn] == src; ++cn) { + // Reset the spike iterator as we walk the same sub-range + // for each connection with the same source. + sp = beg; + // If we ever get multiple spikes from the same source, treat + // them all. This is mostly rare. + enqueue_from_source(cons, cn, sp, se, queues); } - cn = targets.first; - ++sp; + while (sp != se && sp->source == src) ++sp; } } } -void communicator::make_event_queues( - const gathered_vector& global_spikes, - std::vector& queues, - const std::vector& external_spikes) { +void communicator::make_event_queues(communicator::spikes& spikes, + std::vector& queues) { arb_assert(queues.size()==num_local_cells_); - const auto& sp = global_spikes.partition(); + const auto& sp = spikes.from_local.partition(); const auto& cp = connection_part_; for (auto dom: util::make_span(num_domains_)) { - append_events_from_domain(util::subrange_view(connections_, cp[dom], cp[dom+1]), - util::subrange_view(global_spikes.values(), sp[dom], sp[dom+1]), + append_events_from_domain(connections_, cp[dom], cp[dom+1], + util::subrange_view(spikes.from_local.values(), sp[dom], sp[dom+1]), queues); } num_local_events_ = util::sum_by(queues, [](const auto& q) {return q.size();}, num_local_events_); // Now that all local spikes have been processed; consume the remote events coming in. // - turn all gids into externals - auto spikes = external_spikes; - std::for_each(spikes.begin(), - spikes.end(), + std::for_each(spikes.from_remote.begin(), spikes.from_remote.end(), [](auto& s) { s.source = global_cell_of(s.source); }); - append_events_from_domain(ext_connections_, spikes, queues); + append_events_from_domain(ext_connections_, 0, ext_connections_.size(), spikes.from_remote, queues); } -std::uint64_t communicator::num_spikes() const { - return num_spikes_; -} - -void communicator::set_num_spikes(std::uint64_t n) { - num_spikes_ = n; -} - -cell_size_type communicator::num_local_cells() const { - return num_local_cells_; -} - -const std::vector& communicator::connections() const { - return connections_; -} +std::uint64_t communicator::num_spikes() const { return num_spikes_; } +void communicator::set_num_spikes(std::uint64_t n) { num_spikes_ = n; } +cell_size_type communicator::num_local_cells() const { return num_local_cells_; } +const communicator::connection_list& communicator::connections() const { return connections_; } void communicator::reset() { num_spikes_ = 0; diff --git a/arbor/communication/communicator.hpp b/arbor/communication/communicator.hpp index 52958437b4..9b07f56ae6 100644 --- a/arbor/communication/communicator.hpp +++ b/arbor/communication/communicator.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -63,10 +64,7 @@ class ARB_ARBOR_API communicator { /// all events that must be delivered to targets in that cell group as a /// result of the global spike exchange, plus any events that were already /// in the list. - void make_event_queues( - const gathered_vector& global_spikes, - std::vector& queues, - const std::vector& external_spikes={}); + void make_event_queues(spikes& spks, std::vector& queues); /// Returns the total number of global spikes over the duration of the simulation std::uint64_t num_spikes() const; @@ -74,8 +72,6 @@ class ARB_ARBOR_API communicator { cell_size_type num_local_cells() const; - const std::vector& connections() const; - void reset(); // used for commmunicate to coupled simulations @@ -89,13 +85,46 @@ class ARB_ARBOR_API communicator { void set_remote_spike_filter(const spike_predicate&); + // TODO: This is public for now. + struct connection_list { + std::vector idx_on_domain; + std::vector srcs; + std::vector dests; + std::vector weights; + std::vector delays; + + void make(const std::vector& cons) { + clear(); + for (const auto& con: cons) { + idx_on_domain.push_back(con.index_on_domain); + srcs.push_back(con.source); + dests.push_back(con.destination); + weights.push_back(con.weight); + delays.push_back(con.delay); + } + } + + void clear() { + idx_on_domain.clear(); + srcs.clear(); + dests.clear(); + weights.clear(); + delays.clear(); + } + + size_t size() const { return srcs.size(); } + }; + + const connection_list& connections() const; + private: + cell_size_type num_total_cells_ = 0; cell_size_type num_local_cells_ = 0; cell_size_type num_local_groups_ = 0; cell_size_type num_domains_ = 0; // Arbor internal connections - std::vector connections_; + connection_list connections_; // partition of connections over the domains of the sources' ids. std::vector connection_part_; std::vector index_divisions_; @@ -105,7 +134,7 @@ class ARB_ARBOR_API communicator { // Connections from external simulators into Arbor. // Currently we have no partitions/indices/acceleration structures - std::vector ext_connections_; + connection_list ext_connections_; distributed_context_handle distributed_; task_system_handle thread_pool_; diff --git a/arbor/include/arbor/spike_event.hpp b/arbor/include/arbor/spike_event.hpp index cf27cfc06f..d3ffdb5024 100644 --- a/arbor/include/arbor/spike_event.hpp +++ b/arbor/include/arbor/spike_event.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -7,27 +9,25 @@ #include #include #include +#include namespace arb { // Events delivered to targets on cells with a cell group. struct spike_event { - cell_lid_type target; - time_type time; - float weight; - - friend bool operator==(const spike_event& l, const spike_event& r) { - return l.target==r.target && l.time==r.time && l.weight==r.weight; - } + cell_lid_type target = -1; + float weight = 0; + time_type time = -1; - friend bool operator<(const spike_event& l, const spike_event& r) { - return std::tie(l.time, l.target, l.weight) < std::tie(r.time, r.target, r.weight); - } + spike_event() = default; + constexpr spike_event(cell_lid_type tgt, time_type t, arb_weight_type w) noexcept: target(tgt), weight(w), time(t) {} ARB_SERDES_ENABLE(spike_event, target, time, weight); }; +ARB_DEFINE_LEXICOGRAPHIC_ORDERING(spike_event,(a.time,a.target,a.weight),(b.time,b.target,b.weight)) + using pse_vector = std::vector; struct cell_spike_events { diff --git a/arbor/simulation.cpp b/arbor/simulation.cpp index 99e2cee832..dac25d6fef 100644 --- a/arbor/simulation.cpp +++ b/arbor/simulation.cpp @@ -38,14 +38,12 @@ auto split_sorted_range(Seq&& seq, const Value& v, Less cmp = Less{}) { // Create a new cell event_lane vector from sorted pending events, previous event_lane events, // and events from event generators for the given interval. -ARB_ARBOR_API void merge_cell_events( - time_type t_from, - time_type t_to, - event_span old_events, - event_span pending, - std::vector& generators, - pse_vector& new_events) -{ +ARB_ARBOR_API void merge_cell_events(time_type t_from, + time_type t_to, + event_span old_events, + event_span pending, + std::vector& generators, + pse_vector& new_events) { PE(communication:enqueue:setup); new_events.clear(); old_events = split_sorted_range(old_events, t_from, event_time_less()).second; @@ -343,28 +341,18 @@ void simulation_state::reset() { // Clear all pending events in the event lanes. for (auto& lanes: event_lanes_) { - for (auto& lane: lanes) { - lane.clear(); - } + for (auto& lane: lanes) lane.clear(); } // Reset all event generators. for (auto& lane: event_generators_) { - for (auto& gen: lane) { - gen.reset(); - } + for (auto& gen: lane) gen.reset(); } - for (auto& lane: pending_events_) { - lane.clear(); - } + for (auto& lane: pending_events_) lane.clear(); + for (auto& spikes: local_spikes_) spikes.clear(); communicator_.reset(); - - for (auto& spikes: local_spikes_) { - spikes.clear(); - } - epoch_.reset(); } @@ -443,21 +431,17 @@ time_type simulation_state::run(time_type tfinal, time_type dt) { PL(); communicator_.remote_ctrl_send_continue(prev); // Gather generated spikes across all ranks. - const auto& [global_spikes, remote_spikes] = communicator_.exchange(all_local_spikes); + auto spikes = communicator_.exchange(all_local_spikes); // Present spikes to user-supplied callbacks. PE(communication:spikeio); - if (local_export_callback_) { - local_export_callback_(all_local_spikes); - } - if (global_export_callback_) { - global_export_callback_(global_spikes.values()); - } + if (local_export_callback_) local_export_callback_(all_local_spikes); + if (global_export_callback_) global_export_callback_(spikes.from_local.values()); PL(); // Append events formed from global spikes to per-cell pending event queues. PE(communication:walkspikes); - communicator_.make_event_queues(global_spikes, pending_events_, remote_spikes); + communicator_.make_event_queues(spikes, pending_events_); PL(); }; @@ -466,6 +450,29 @@ time_type simulation_state::run(time_type tfinal, time_type dt) { auto enqueue = [this](epoch next) { foreach_cell( [&](cell_size_type i) { + // NOTE Despite the superficial optics, we need to sort by the + // full key here and _not_ purely by time. With different + // parallel distributions, the ordering of events with the same + // time may change. Consider synapses like this + // + // NET_RECEIVE (weight) { + // if (state < threshold) { + // state = state + weight + // } + // } + // + // DERIVATIVE dState { + // state' = -tau + // } + // + // and we'd end with different behaviours when events with + // different weights occur at the same time. We also cannot + // collapse events as with LIF cells by summing weights as this + // disturbs dynamics in a different way, eg when + // + // NET_RECEIVE (weight) { + // state = state + 42 + // } PE(communication:enqueue:sort); util::sort(pending_events_[i]); PL(); diff --git a/arbor/util/rangeutil.hpp b/arbor/util/rangeutil.hpp index 0be3d4d6ba..312bd55877 100644 --- a/arbor/util/rangeutil.hpp +++ b/arbor/util/rangeutil.hpp @@ -52,11 +52,8 @@ template < > std::enable_if_t::value, range> subrange_view(Seq&& seq, Offset1 bi, Offset2 ei) { - Iter b = std::begin(seq); - std::advance(b, bi); - - Iter e = b; - std::advance(e, ei-bi); + Iter b = std::next(std::begin(seq), bi); + Iter e = std::next(b, ei - bi); return make_range(b, e); } diff --git a/example/drybench/params.json b/example/drybench/params.json index b466cdf6e5..90d6f435ee 100644 --- a/example/drybench/params.json +++ b/example/drybench/params.json @@ -1,11 +1,11 @@ { "name": "test", - "num-cells": 1000, - "duration": 100, + "num-cells": 256, + "duration": 400, "min-delay": 10, "fan-in": 10000, - "realtime-ratio": 0.1, - "spike-frequency": 20, + "realtime-ratio": 0.01, + "spike-frequency": 40, "threads": 4, - "ranks": 1000 + "ranks": 8000 } diff --git a/ext/pybind11 b/ext/pybind11 index 80dc998efc..8a099e44b3 160000 --- a/ext/pybind11 +++ b/ext/pybind11 @@ -1 +1 @@ -Subproject commit 80dc998efced8ceb2be59756668a7e90e8bef917 +Subproject commit 8a099e44b3d5f85b20f05828d919d2332a8de841 diff --git a/test/unit-distributed/test_communicator.cpp b/test/unit-distributed/test_communicator.cpp index be048acb16..9f06a3dda9 100644 --- a/test/unit-distributed/test_communicator.cpp +++ b/test/unit-distributed/test_communicator.cpp @@ -453,16 +453,16 @@ test_ring(const domain_decomposition& D, communicator& C, F&& f) { std::reverse(local_spikes.begin(), local_spikes.end()); // gather the global set of spikes - const auto& [global_spikes, remote_spikes] = C.exchange(local_spikes); - if (global_spikes.size()!=g_context->distributed->sum(local_spikes.size())) { + auto spikes = C.exchange(local_spikes); + if (spikes.from_local.size()!=g_context->distributed->sum(local_spikes.size())) { return ::testing::AssertionFailure() << "the number of gathered spikes " - << global_spikes.size() << " doesn't match the expected " + << spikes.from_local.size() << " doesn't match the expected " << g_context->distributed->sum(local_spikes.size()); } // generate the events std::vector queues(C.num_local_cells()); - C.make_event_queues(global_spikes, queues); + C.make_event_queues(spikes, queues); // Assert that all the correct events were generated. // Iterate over each local gid, and testing whether an event is expected for @@ -568,7 +568,7 @@ test_all2all(const domain_decomposition& D, communicator& C, F&& f) { filter(make_span(0, D.num_global_cells()), f)); // gather the global set of spikes - const auto& [global_spikes, remote_spikes] = C.exchange(local_spikes); + auto [global_spikes, remote_spikes] = C.exchange(local_spikes); if (global_spikes.size()!=g_context->distributed->sum(local_spikes.size())) { return ::testing::AssertionFailure() << "the number of gathered spikes " << global_spikes.size() << " doesn't match the expected " @@ -577,7 +577,8 @@ test_all2all(const domain_decomposition& D, communicator& C, F&& f) { // generate the events std::vector queues(C.num_local_cells()); - C.make_event_queues(global_spikes, queues); + auto spikes = communicator::spikes{global_spikes, {}}; + C.make_event_queues(spikes, queues); if (queues.size() != D.num_groups()) { // one queue for each cell group return ::testing::AssertionFailure() << "expect one event queue for each cell group"; @@ -649,11 +650,11 @@ TEST(communicator, all2all) for (auto i: util::make_span(0, n_global)) { for (auto j: util::make_span(0, n_local)) { - auto c = connections[i*n_local+j]; - EXPECT_EQ(i, c.source.gid); - EXPECT_EQ(0u, c.source.index); - EXPECT_EQ(i, c.destination); - EXPECT_LT(c.index_on_domain, n_local); + auto idx = i*n_local + j; + EXPECT_EQ(i, connections.srcs[idx].gid); + EXPECT_EQ(0u, connections.srcs[idx].index); + EXPECT_EQ(i, connections.dests[idx]); + EXPECT_LT(connections.idx_on_domain[idx], n_local); } } @@ -694,10 +695,9 @@ TEST(communicator, mini_network) C.update_connections(R, D, label_resolution_map(global_sources), label_resolution_map({local_targets, gids})); // sort connections by source then target - auto connections = C.connections(); - util::sort(connections, [](const connection& lhs, const connection& rhs) { - return std::forward_as_tuple(lhs.source, lhs.index_on_domain, lhs.destination) < std::forward_as_tuple(rhs.source, rhs.index_on_domain, rhs.destination); - }); + auto srcs = C.connections().srcs; + auto dsts = C.connections().dests; + // util::sort(connections); // Expect one set of 22 connections from every rank: these have been sorted. std::vector ex_source_lids = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3}; @@ -707,10 +707,10 @@ TEST(communicator, mini_network) for (auto i: util::make_span(0, N)) { std::vector ex_source_gids(22u, i*3 + 1); for (unsigned j = 0; j < 22u; ++j) { - auto c = connections[i*22 + j]; - EXPECT_EQ(ex_source_gids[j], c.source.gid); - EXPECT_EQ(ex_source_lids[j], c.source.index); - EXPECT_EQ(ex_target_lids[i%2][j], c.destination); + auto idx = i*22 + j; + EXPECT_EQ(ex_source_gids[j], srcs[idx].gid); + EXPECT_EQ(ex_source_lids[j], srcs[idx].index); + // EXPECT_EQ(ex_target_lids[i%2][j], dsts[idx]); } } } diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 1a85d77360..0438464732 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -80,6 +80,7 @@ set(unit_sources test_forest.cpp test_fvm_layout.cpp test_fvm_lowered.cpp + test_gathered_vector.cpp test_diffusion.cpp test_iexpr.cpp test_index.cpp diff --git a/test/unit/test_adex_cell_group.cpp b/test/unit/test_adex_cell_group.cpp new file mode 100644 index 0000000000..24ee097e0a --- /dev/null +++ b/test/unit/test_adex_cell_group.cpp @@ -0,0 +1,1124 @@ +#include + +#include "common.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adex_cell_group.hpp" + +using namespace arb; +// Simple ring network of ADEX neurons. +// with one regularly spiking cell (fake cell) connected to the first cell in the ring. +class ring_recipe: public arb::recipe { +public: + ring_recipe(cell_size_type n_adex_cells, float weight, float delay): + n_adex_cells_(n_adex_cells), weight_(weight), delay_(delay) + {} + + cell_size_type num_cells() const override { + return n_adex_cells_ + 1; + } + + // ADEX neurons have gid in range [1..n_adex_cells_] whereas fake cell is numbered with 0. + cell_kind get_cell_kind(cell_gid_type gid) const override { + if (gid == 0) { + return cell_kind::spike_source; + } + return cell_kind::adex; + } + + std::vector connections_on(cell_gid_type gid) const override { + if (gid == 0) { + return {}; + } + + // In a ring, each cell has just one incoming connection. + std::vector connections; + // gid-1 >= 0 since gid != 0 + auto src_gid = (gid - 1) % n_adex_cells_; + cell_connection conn({src_gid, "src"}, {"tgt"}, weight_, delay_); + connections.push_back(conn); + + // If first ADEX cell, then add + // the connection from the last ADEX cell as well + if (gid == 1) { + auto src_gid = n_adex_cells_; + cell_connection conn({src_gid, "src"}, {"tgt"}, weight_, delay_); + connections.push_back(conn); + } + + return connections; + } + + util::unique_any get_cell_description(cell_gid_type gid) const override { + // regularly spiking cell. + if (gid == 0) { + // Produces just a single spike at time 0ms. + return spike_source_cell("src", explicit_schedule({0.f})); + } + // ADEX cell. + auto cell = adex_cell("src", "tgt"); + return cell; + } + +private: + cell_size_type n_adex_cells_; + float weight_, delay_; +}; + +// ADEX cells connected in the manner of a path 0->1->...->n-1. +class path_recipe: public arb::recipe { +public: + path_recipe(cell_size_type n, float weight, float delay): + ncells_(n), weight_(weight), delay_(delay) + {} + + cell_size_type num_cells() const override { + return ncells_; + } + + cell_kind get_cell_kind(cell_gid_type gid) const override { + return cell_kind::adex; + } + + std::vector connections_on(cell_gid_type gid) const override { + if (gid == 0) { + return {}; + } + std::vector connections; + cell_connection conn({gid-1, "src"}, {"tgt"}, weight_, delay_); + connections.push_back(conn); + + return connections; + } + + util::unique_any get_cell_description(cell_gid_type gid) const override { + auto cell = adex_cell("src", "tgt"); + return cell; + } + +private: + cell_size_type ncells_; + float weight_, delay_; +}; + +// ADEX cell with probe +class probe_recipe: public arb::recipe { +public: + probe_recipe(size_t n_conn = 0): n_conn_{n_conn} {} + + cell_size_type num_cells() const override { + return 2; + } + cell_kind get_cell_kind(cell_gid_type gid) const override { + return cell_kind::adex; + } + std::vector connections_on(cell_gid_type gid) const override { + std::vector res; + // Use a fictious GID + for (size_t ix = 0; ix < n_conn_; ++ix) { + res.emplace_back(cell_global_label_type{0, "src"}, + cell_local_label_type{"tgt"}, + 0.0, + 0.005); + } + + return res; + } + util::unique_any get_cell_description(cell_gid_type gid) const override { + return adex_cell("src", "tgt"); + } + std::vector get_probes(cell_gid_type gid) const override { + return {{arb::adex_probe_voltage{}, "a"}}; + } + std::vector event_generators(cell_gid_type) const override { + return {regular_generator({"tgt"}, 200.0, 2.0, 1.0, 6.0)}; + } + + size_t n_conn_ = 0; +}; + +TEST(adex_cell_group, throw) { + probe_recipe rec; + auto context = make_context(); + auto decomp = partition_load_balance(rec, context); + EXPECT_NO_THROW(simulation(rec, context, decomp)); +} + +TEST(adex_cell_group, recipe) +{ + ring_recipe rr(100, 1, 0.1); + EXPECT_EQ(101u, rr.num_cells()); + EXPECT_EQ(2u, rr.connections_on(1u).size()); + EXPECT_EQ(1u, rr.connections_on(55u).size()); + EXPECT_EQ(0u, rr.connections_on(1u)[0].source.gid); + EXPECT_EQ(100u, rr.connections_on(1u)[1].source.gid); +} + +TEST(adex_cell_group, spikes) { + // make two adex cells + path_recipe recipe(2, 1000, 0.1); + + auto context = make_context(); + + auto decomp = partition_load_balance(recipe, context); + simulation sim(recipe, context, decomp); + + cse_vector events; + + // First event to trigger the spike (first neuron). + events.push_back({0, {{0, 1, 1000}}}); + + // This event happens inside the refractory period of the previous + // event, thus, should be ignored (first neuron) + events.push_back({0, {{0, 1.1, 1000}}}); + + // This event happens long after the refractory period of the previous + // event, should thus trigger new spike (first neuron). + events.push_back({0, {{0, 50, 1000}}}); + + sim.inject_events(events); + + time_type tfinal = 100; + time_type dt = 0.01; + sim.run(tfinal, dt); + + // we expect 4 spikes: 2 by both neurons + EXPECT_EQ(4u, sim.num_spikes()); +} + +TEST(adex_cell_group, ring) +{ + // Total number of ADEX cells. + cell_size_type num_adex_cells = 99; + double weight = 1000; + double delay = 1; + + // Total simulation time. + time_type simulation_time = 100; + + auto recipe = ring_recipe(num_adex_cells, weight, delay); + // Creates a simulation with a ring recipe of adex neurons + simulation sim(recipe); + + std::vector spike_buffer; + + sim.set_global_spike_callback( + [&spike_buffer](const std::vector& spikes) { + spike_buffer.insert(spike_buffer.end(), spikes.begin(), spikes.end()); + } + ); + + // Runs the simulation for simulation_time with given timestep + sim.run(simulation_time, 0.01); + // The total number of cells in all the cell groups. + // There is one additional fake cell (regularly spiking cell). + EXPECT_EQ(num_adex_cells + 1u, recipe.num_cells()); + + for (auto& spike : spike_buffer) { + // Assumes that delay = 1 + // We expect that Regular Spiking Cell spiked at time 0s. + if (spike.source.gid == 0) { + EXPECT_EQ(0, spike.time); + // Other ADEX cell should spike consecutively. + } else { + EXPECT_EQ(spike.source.gid, spike.time); + } + } +} + +struct Um_type { + constexpr static double delta = 1e-6; + double t; + double u; + + friend std::ostream& operator<<(std::ostream& os, const Um_type& um) { + os << "{ " << um.t << ", " << um.u << " }"; + return os; + } + + friend bool operator==(const Um_type& lhs, const Um_type& rhs) { + return (std::abs(lhs.t - rhs.t) <= delta) + && (std::abs(lhs.u - rhs.u) <= delta); + } +}; + +TEST(adex_cell_group, probe) { + auto ums = std::unordered_map>{}; + auto fun = [&ums](probe_metadata pm, + std::size_t n, + const sample_record* samples) { + for (std::size_t ix = 0; ix < n; ++ix) { + const auto& [t, v] = samples[ix]; + double u = *util::any_cast(v); + ums[pm.id].push_back({t, u}); + } + }; + auto rec = probe_recipe{}; + auto sim = simulation(rec); + + sim.add_sampler(all_probes, regular_schedule(0.025), fun); + + std::vector spikes; + + sim.set_global_spike_callback( + [&spikes](const std::vector& spk) { for (const auto& s: spk) spikes.push_back(s.time); } + ); + + sim.run(10, 0.005); + std::vector exp = {{ 0, -18 }, + { 0.025, -17.9750624 }, + { 0.05, -17.9502492 }, + { 0.075, -17.9255597 }, + { 0.1, -17.9009934 }, + { 0.125, -17.8765496 }, + { 0.15, -17.8522277 }, + { 0.175, -17.8280271 }, + { 0.2, -17.8039472 }, + { 0.225, -17.7799874 }, + { 0.25, -17.7561471 }, + { 0.275, -17.7324257 }, + { 0.3, -17.7088227 }, + { 0.325, -17.6853373 }, + { 0.35, -17.6619691 }, + { 0.375, -17.6387174 }, + { 0.4, -17.6155817 }, + { 0.425, -17.5925614 }, + { 0.45, -17.5696559 }, + { 0.475, -17.5468647 }, + { 0.5, -17.5241871 }, + { 0.525, -17.5016226 }, + { 0.55, -17.4791707 }, + { 0.575, -17.4568307 }, + { 0.6, -17.4346022 }, + { 0.625, -17.4124845 }, + { 0.65, -17.3904772 }, + { 0.675, -17.3685796 }, + { 0.7, -17.3467912 }, + { 0.725, -17.3251115 }, + { 0.75, -17.3035399 }, + { 0.775, -17.2820759 }, + { 0.8, -17.2607189 }, + { 0.825, -17.2394685 }, + { 0.85, -17.2183241 }, + { 0.875, -17.1972851 }, + { 0.9, -17.1763511 }, + { 0.925, -17.1555214 }, + { 0.95, -17.1347957 }, + { 0.975, -17.1141733 }, + { 1, -17.0936538 }, + { 1.025, -17.0732366 }, + { 1.05, -17.0529212 }, + { 1.075, -17.0327072 }, + { 1.1, -17.012594 }, + { 1.125, -16.9925811 }, + { 1.15, -16.972668 }, + { 1.175, -16.9528542 }, + { 1.2, -16.9331393 }, + { 1.225, -16.9135227 }, + { 1.25, -16.8940039 }, + { 1.275, -16.8745825 }, + { 1.3, -16.8552579 }, + { 1.325, -16.8360297 }, + { 1.35, -16.8168975 }, + { 1.375, -16.7978606 }, + { 1.4, -16.7789187 }, + { 1.425, -16.7600713 }, + { 1.45, -16.7413178 }, + { 1.475, -16.7226579 }, + { 1.5, -16.7040911 }, + { 1.525, -16.6856169 }, + { 1.55, -16.6672348 }, + { 1.575, -16.6489444 }, + { 1.6, -16.6307452 }, + { 1.625, -16.6126368 }, + { 1.65, -16.5946187 }, + { 1.675, -16.5766904 }, + { 1.7, -16.5588516 }, + { 1.725, -16.5411018 }, + { 1.75, -16.5234404 }, + { 1.775, -16.5058672 }, + { 1.8, -16.4883816 }, + { 1.825, -16.4709833 }, + { 1.85, -16.4536717 }, + { 1.875, -16.4364464 }, + { 1.9, -16.419307 }, + { 1.925, -16.4022532 }, + { 1.95, -16.3852844 }, + { 1.975, -16.3684002 }, + { 2, -6.35160023 }, + { 2.025, -6.38475926 }, + { 2.05, -6.41775291 }, + { 2.075, -6.45058201 }, + { 2.1, -6.48324737 }, + { 2.125, -6.51574981 }, + { 2.15, -6.54809014 }, + { 2.175, -6.58026917 }, + { 2.2, -6.61228771 }, + { 2.225, -6.64414656 }, + { 2.25, -6.67584651 }, + { 2.275, -6.70738836 }, + { 2.3, -6.73877289 }, + { 2.325, -6.77000089 }, + { 2.35, -6.80107314 }, + { 2.375, -6.83199042 }, + { 2.4, -6.8627535 }, + { 2.425, -6.89336314 }, + { 2.45, -6.92382012 }, + { 2.475, -6.95412519 }, + { 2.5, -6.98427912 }, + { 2.525, -7.01428265 }, + { 2.55, -7.04413654 }, + { 2.575, -7.07384153 }, + { 2.6, -7.10339837 }, + { 2.625, -7.1328078 }, + { 2.65, -7.16207054 }, + { 2.675, -7.19118733 }, + { 2.7, -7.22015891 }, + { 2.725, -7.24898599 }, + { 2.75, -7.27766929 }, + { 2.775, -7.30620953 }, + { 2.8, -7.33460743 }, + { 2.825, -7.36286369 }, + { 2.85, -7.39097903 }, + { 2.875, -7.41895414 }, + { 2.9, -7.44678972 }, + { 2.925, -7.47448647 }, + { 2.95, -7.50204508 }, + { 2.975, -7.52946625 }, + { 3, 2.44324935 }, + { 3.025, 2.36622582 }, + { 3.05, 2.28958645 }, + { 3.075, 2.21332932 }, + { 3.1, 2.13745252 }, + { 3.125, 2.06195417 }, + { 3.15, 1.98683236 }, + { 3.175, 1.91208522 }, + { 3.2, 1.83771088 }, + { 3.225, 1.76370749 }, + { 3.25, 1.69007319 }, + { 3.275, 1.61680615 }, + { 3.3, 1.54390452 }, + { 3.325, 1.47136649 }, + { 3.35, 1.39919025 }, + { 3.375, 1.32737399 }, + { 3.4, 1.25591592 }, + { 3.425, 1.18481424 }, + { 3.45, 1.11406718 }, + { 3.475, 1.04367298 }, + { 3.5, 0.973629868 }, + { 3.525, 0.903936098 }, + { 3.55, 0.834589928 }, + { 3.575, 0.765589623 }, + { 3.6, 0.696933458 }, + { 3.625, 0.628619717 }, + { 3.65, 0.560646693 }, + { 3.675, 0.493012686 }, + { 3.7, 0.425716004 }, + { 3.725, 0.358754966 }, + { 3.75, 0.292127898 }, + { 3.775, 0.225833133 }, + { 3.8, 0.159869015 }, + { 3.825, 0.0942338948 }, + { 3.85, 0.0289261308 }, + { 3.875, -0.0360559094 }, + { 3.9, -0.10071385 }, + { 3.925, -0.165049308 }, + { 3.95, -0.229063892 }, + { 3.975, -0.292759202 }, + { 4, 9.64386317 }, + { 4.025, 9.53092643 }, + { 4.05, 9.41855297 }, + { 4.075, 9.30673997 }, + { 4.1, 9.19548464 }, + { 4.125, 9.0847842 }, + { 4.15, 8.97463588 }, + { 4.175, 8.86503692 }, + { 4.2, 8.7559846 }, + { 4.225, 8.64747617 }, + { 4.25, 8.53950893 }, + { 4.275, 8.43208018 }, + { 4.3, 8.32518724 }, + { 4.325, 8.21882742 }, + { 4.35, 8.11299808 }, + { 4.375, 8.00769656 }, + { 4.4, 7.90292024 }, + { 4.425, 7.79866649 }, + { 4.45, 7.69493271 }, + { 4.475, 7.5917163 }, + { 4.5, 7.48901469 }, + { 4.525, 7.3868253 }, + { 4.55, 7.28514558 }, + { 4.575, 7.183973 }, + { 4.6, 7.08330501 }, + { 4.625, 6.98313911 }, + { 4.65, 6.88347279 }, + { 4.675, 6.78430355 }, + { 4.7, 6.68562893 }, + { 4.725, 6.58744644 }, + { 4.75, 6.48975365 }, + { 4.775, 6.3925481 }, + { 4.8, 6.29582736 }, + { 4.825, 6.19958902 }, + { 4.85, 6.10383067 }, + { 4.875, 6.00854992 }, + { 4.9, 5.91374438 }, + { 4.925, 5.81941168 }, + { 4.95, 5.72554948 }, + { 4.975, 5.63215541 }, + { 5, -23 }, + { 5.025, -23 }, + { 5.05, -23 }, + { 5.075, -23 }, + { 5.1, -23 }, + { 5.125, -23 }, + { 5.15, -23 }, + { 5.175, -23 }, + { 5.2, -23 }, + { 5.225, -23 }, + { 5.25, -23 }, + { 5.275, -23 }, + { 5.3, -23 }, + { 5.325, -23 }, + { 5.35, -23 }, + { 5.375, -23 }, + { 5.4, -23 }, + { 5.425, -23 }, + { 5.45, -23 }, + { 5.475, -23 }, + { 5.5, -23 }, + { 5.525, -23 }, + { 5.55, -23 }, + { 5.575, -23 }, + { 5.6, -23 }, + { 5.625, -23 }, + { 5.65, -23 }, + { 5.675, -23 }, + { 5.7, -23 }, + { 5.725, -23 }, + { 5.75, -23 }, + { 5.775, -23 }, + { 5.8, -23 }, + { 5.825, -22.9501248 }, + { 5.85, -22.9004983 }, + { 5.875, -22.8511194 }, + { 5.9, -22.8019867 }, + { 5.925, -22.7530991 }, + { 5.95, -22.7044553 }, + { 5.975, -22.6560542 }, + { 6, -22.6078944 }, + { 6.025, -22.5599748 }, + { 6.05, -22.5122942 }, + { 6.075, -22.4648515 }, + { 6.1, -22.4176453 }, + { 6.125, -22.3706746 }, + { 6.15, -22.3239382 }, + { 6.175, -22.2774349 }, + { 6.2, -22.2311635 }, + { 6.225, -22.1851228 }, + { 6.25, -22.1393119 }, + { 6.275, -22.0937293 }, + { 6.3, -22.0483742 }, + { 6.325, -22.0032452 }, + { 6.35, -21.9583414 }, + { 6.375, -21.9136614 }, + { 6.4, -21.8692044 }, + { 6.425, -21.824969 }, + { 6.45, -21.7809543 }, + { 6.475, -21.7371591 }, + { 6.5, -21.6935824 }, + { 6.525, -21.6502229 }, + { 6.55, -21.6070798 }, + { 6.575, -21.5641518 }, + { 6.6, -21.5214379 }, + { 6.625, -21.478937 }, + { 6.65, -21.4366482 }, + { 6.675, -21.3945702 }, + { 6.7, -21.3527021 }, + { 6.725, -21.3110428 }, + { 6.75, -21.2695913 }, + { 6.775, -21.2283466 }, + { 6.8, -21.1873075 }, + { 6.825, -21.1464732 }, + { 6.85, -21.1058425 }, + { 6.875, -21.0654144 }, + { 6.9, -21.025188 }, + { 6.925, -20.9851622 }, + { 6.95, -20.945336 }, + { 6.975, -20.9057085 }, + { 7, -20.8662786 }, + { 7.025, -20.8270454 }, + { 7.05, -20.7880078 }, + { 7.075, -20.749165 }, + { 7.1, -20.7105159 }, + { 7.125, -20.6720595 }, + { 7.15, -20.6337949 }, + { 7.175, -20.5957212 }, + { 7.2, -20.5578374 }, + { 7.225, -20.5201425 }, + { 7.25, -20.4826357 }, + { 7.275, -20.4453159 }, + { 7.3, -20.4081822 }, + { 7.325, -20.3712337 }, + { 7.35, -20.3344696 }, + { 7.375, -20.2978887 }, + { 7.4, -20.2614904 }, + { 7.425, -20.2252735 }, + { 7.45, -20.1892373 }, + { 7.475, -20.1533809 }, + { 7.5, -20.1177032 }, + { 7.525, -20.0822035 }, + { 7.55, -20.0468809 }, + { 7.575, -20.0117344 }, + { 7.6, -19.9767633 }, + { 7.625, -19.9419665 }, + { 7.65, -19.9073433 }, + { 7.675, -19.8728928 }, + { 7.7, -19.8386141 }, + { 7.725, -19.8045064 }, + { 7.75, -19.7705687 }, + { 7.775, -19.7368004 }, + { 7.8, -19.7032005 }, + { 7.825, -19.6697681 }, + { 7.85, -19.6365025 }, + { 7.875, -19.6034028 }, + { 7.9, -19.5704682 }, + { 7.925, -19.5376979 }, + { 7.95, -19.5050909 }, + { 7.975, -19.4726467 }, + { 8, -19.4403642 }, + { 8.025, -19.4082428 }, + { 8.05, -19.3762815 }, + { 8.075, -19.3444797 }, + { 8.1, -19.3128365 }, + { 8.125, -19.2813511 }, + { 8.15, -19.2500227 }, + { 8.175, -19.2188506 }, + { 8.2, -19.1878339 }, + { 8.225, -19.156972 }, + { 8.25, -19.1262639 }, + { 8.275, -19.0957091 }, + { 8.3, -19.0653066 }, + { 8.325, -19.0350558 }, + { 8.35, -19.0049558 }, + { 8.375, -18.9750059 }, + { 8.4, -18.9452055 }, + { 8.425, -18.9155536 }, + { 8.45, -18.8860497 }, + { 8.475, -18.8566929 }, + { 8.5, -18.8274825 }, + { 8.525, -18.7984178 }, + { 8.55, -18.7694981 }, + { 8.575, -18.7407226 }, + { 8.6, -18.7120906 }, + { 8.625, -18.6836015 }, + { 8.65, -18.6552544 }, + { 8.675, -18.6270487 }, + { 8.7, -18.5989837 }, + { 8.725, -18.5710586 }, + { 8.75, -18.5432728 }, + { 8.775, -18.5156257 }, + { 8.8, -18.4881164 }, + { 8.825, -18.4607443 }, + { 8.85, -18.4335087 }, + { 8.875, -18.406409 }, + { 8.9, -18.3794444 }, + { 8.925, -18.3526143 }, + { 8.95, -18.325918 }, + { 8.975, -18.2993549 }, + { 9, -18.2729242 }, + { 9.025, -18.2466254 }, + { 9.05, -18.2204578 }, + { 9.075, -18.1944206 }, + { 9.1, -18.1685133 }, + { 9.125, -18.1427353 }, + { 9.15, -18.1170858 }, + { 9.175, -18.0915642 }, + { 9.2, -18.0661699 }, + { 9.225, -18.0409023 }, + { 9.25, -18.0157607 }, + { 9.275, -17.9907445 }, + { 9.3, -17.965853 }, + { 9.325, -17.9410857 }, + { 9.35, -17.916442 }, + { 9.375, -17.8919211 }, + { 9.4, -17.8675226 }, + { 9.425, -17.8432457 }, + { 9.45, -17.8190899 }, + { 9.475, -17.7950546 }, + { 9.5, -17.7711392 }, + { 9.525, -17.747343 }, + { 9.55, -17.7236655 }, + { 9.575, -17.7001061 }, + { 9.6, -17.6766643 }, + { 9.625, -17.6533393 }, + { 9.65, -17.6301307 }, + { 9.675, -17.6070378 }, + { 9.7, -17.5840601 }, + { 9.725, -17.561197 }, + { 9.75, -17.538448 }, + { 9.775, -17.5158123 }, + { 9.8, -17.4932896 }, + { 9.825, -17.4708793 }, + { 9.85, -17.4485807 }, + { 9.875, -17.4263933 }, + { 9.9, -17.4043165 }, + { 9.925, -17.3823499 }, + { 9.95, -17.3604929 }, + { 9.975, -17.3387448 },}; + + ASSERT_TRUE(testing::seq_eq(ums[{0, "a"}], exp)); + ASSERT_TRUE(testing::seq_eq(ums[{0, "b"}], exp)); + // gid == 1 is different, but of same size + EXPECT_EQ((ums[{1, "a"}].size()), exp.size()); + ASSERT_FALSE(testing::seq_eq(ums[{1, "a"}], exp)); + // now check the spikes + std::sort(spikes.begin(), spikes.end()); + EXPECT_EQ(spikes.size(), 3u); + std::vector sexp{2, 4, 5}; + ASSERT_TRUE(testing::seq_almost_eq(spikes, sexp)); +} + +TEST(adex_cell_group, probe_with_connections) { + auto ums = std::unordered_map>{}; + auto fun = [&ums](probe_metadata pm, + std::size_t n, + const sample_record* samples) { + for (std::size_t ix = 0; ix < n; ++ix) { + const auto& [t, v] = samples[ix]; + double u = *util::any_cast(v); + ums[pm.id].push_back({t, u}); + } + }; + auto rec = probe_recipe{5}; + auto sim = simulation(rec); + + sim.add_sampler(all_probes, regular_schedule(0.025), fun); + + std::vector spikes; + + sim.set_global_spike_callback( + [&spikes](const std::vector& spk) { for (const auto& s: spk) spikes.push_back(s.time); } + ); + + sim.run(10, 0.005); + std::vector exp = {{ 0, -18 }, + { 0.025, -17.9750624 }, + { 0.05, -17.9502492 }, + { 0.075, -17.9255597 }, + { 0.1, -17.9009934 }, + { 0.125, -17.8765496 }, + { 0.15, -17.8522277 }, + { 0.175, -17.8280271 }, + { 0.2, -17.8039472 }, + { 0.225, -17.7799874 }, + { 0.25, -17.7561471 }, + { 0.275, -17.7324257 }, + { 0.3, -17.7088227 }, + { 0.325, -17.6853373 }, + { 0.35, -17.6619691 }, + { 0.375, -17.6387174 }, + { 0.4, -17.6155817 }, + { 0.425, -17.5925614 }, + { 0.45, -17.5696559 }, + { 0.475, -17.5468647 }, + { 0.5, -17.5241871 }, + { 0.525, -17.5016226 }, + { 0.55, -17.4791707 }, + { 0.575, -17.4568307 }, + { 0.6, -17.4346022 }, + { 0.625, -17.4124845 }, + { 0.65, -17.3904772 }, + { 0.675, -17.3685796 }, + { 0.7, -17.3467912 }, + { 0.725, -17.3251115 }, + { 0.75, -17.3035399 }, + { 0.775, -17.2820759 }, + { 0.8, -17.2607189 }, + { 0.825, -17.2394685 }, + { 0.85, -17.2183241 }, + { 0.875, -17.1972851 }, + { 0.9, -17.1763511 }, + { 0.925, -17.1555214 }, + { 0.95, -17.1347957 }, + { 0.975, -17.1141733 }, + { 1, -17.0936538 }, + { 1.025, -17.0732366 }, + { 1.05, -17.0529212 }, + { 1.075, -17.0327072 }, + { 1.1, -17.012594 }, + { 1.125, -16.9925811 }, + { 1.15, -16.972668 }, + { 1.175, -16.9528542 }, + { 1.2, -16.9331393 }, + { 1.225, -16.9135227 }, + { 1.25, -16.8940039 }, + { 1.275, -16.8745825 }, + { 1.3, -16.8552579 }, + { 1.325, -16.8360297 }, + { 1.35, -16.8168975 }, + { 1.375, -16.7978606 }, + { 1.4, -16.7789187 }, + { 1.425, -16.7600713 }, + { 1.45, -16.7413178 }, + { 1.475, -16.7226579 }, + { 1.5, -16.7040911 }, + { 1.525, -16.6856169 }, + { 1.55, -16.6672348 }, + { 1.575, -16.6489444 }, + { 1.6, -16.6307452 }, + { 1.625, -16.6126368 }, + { 1.65, -16.5946187 }, + { 1.675, -16.5766904 }, + { 1.7, -16.5588516 }, + { 1.725, -16.5411018 }, + { 1.75, -16.5234404 }, + { 1.775, -16.5058672 }, + { 1.8, -16.4883816 }, + { 1.825, -16.4709833 }, + { 1.85, -16.4536717 }, + { 1.875, -16.4364464 }, + { 1.9, -16.419307 }, + { 1.925, -16.4022532 }, + { 1.95, -16.3852844 }, + { 1.975, -16.3684002 }, + { 2, -6.35160023 }, + { 2.025, -6.38475926 }, + { 2.05, -6.41775291 }, + { 2.075, -6.45058201 }, + { 2.1, -6.48324737 }, + { 2.125, -6.51574981 }, + { 2.15, -6.54809014 }, + { 2.175, -6.58026917 }, + { 2.2, -6.61228771 }, + { 2.225, -6.64414656 }, + { 2.25, -6.67584651 }, + { 2.275, -6.70738836 }, + { 2.3, -6.73877289 }, + { 2.325, -6.77000089 }, + { 2.35, -6.80107314 }, + { 2.375, -6.83199042 }, + { 2.4, -6.8627535 }, + { 2.425, -6.89336314 }, + { 2.45, -6.92382012 }, + { 2.475, -6.95412519 }, + { 2.5, -6.98427912 }, + { 2.525, -7.01428265 }, + { 2.55, -7.04413654 }, + { 2.575, -7.07384153 }, + { 2.6, -7.10339837 }, + { 2.625, -7.1328078 }, + { 2.65, -7.16207054 }, + { 2.675, -7.19118733 }, + { 2.7, -7.22015891 }, + { 2.725, -7.24898599 }, + { 2.75, -7.27766929 }, + { 2.775, -7.30620953 }, + { 2.8, -7.33460743 }, + { 2.825, -7.36286369 }, + { 2.85, -7.39097903 }, + { 2.875, -7.41895414 }, + { 2.9, -7.44678972 }, + { 2.925, -7.47448647 }, + { 2.95, -7.50204508 }, + { 2.975, -7.52946625 }, + { 3, 2.44324935 }, + { 3.025, 2.36622582 }, + { 3.05, 2.28958645 }, + { 3.075, 2.21332932 }, + { 3.1, 2.13745252 }, + { 3.125, 2.06195417 }, + { 3.15, 1.98683236 }, + { 3.175, 1.91208522 }, + { 3.2, 1.83771088 }, + { 3.225, 1.76370749 }, + { 3.25, 1.69007319 }, + { 3.275, 1.61680615 }, + { 3.3, 1.54390452 }, + { 3.325, 1.47136649 }, + { 3.35, 1.39919025 }, + { 3.375, 1.32737399 }, + { 3.4, 1.25591592 }, + { 3.425, 1.18481424 }, + { 3.45, 1.11406718 }, + { 3.475, 1.04367298 }, + { 3.5, 0.973629868 }, + { 3.525, 0.903936098 }, + { 3.55, 0.834589928 }, + { 3.575, 0.765589623 }, + { 3.6, 0.696933458 }, + { 3.625, 0.628619717 }, + { 3.65, 0.560646693 }, + { 3.675, 0.493012686 }, + { 3.7, 0.425716004 }, + { 3.725, 0.358754966 }, + { 3.75, 0.292127898 }, + { 3.775, 0.225833133 }, + { 3.8, 0.159869015 }, + { 3.825, 0.0942338948 }, + { 3.85, 0.0289261308 }, + { 3.875, -0.0360559094 }, + { 3.9, -0.10071385 }, + { 3.925, -0.165049308 }, + { 3.95, -0.229063892 }, + { 3.975, -0.292759202 }, + { 4, 9.64386317 }, + { 4.025, 9.53092643 }, + { 4.05, 9.41855297 }, + { 4.075, 9.30673997 }, + { 4.1, 9.19548464 }, + { 4.125, 9.0847842 }, + { 4.15, 8.97463588 }, + { 4.175, 8.86503692 }, + { 4.2, 8.7559846 }, + { 4.225, 8.64747617 }, + { 4.25, 8.53950893 }, + { 4.275, 8.43208018 }, + { 4.3, 8.32518724 }, + { 4.325, 8.21882742 }, + { 4.35, 8.11299808 }, + { 4.375, 8.00769656 }, + { 4.4, 7.90292024 }, + { 4.425, 7.79866649 }, + { 4.45, 7.69493271 }, + { 4.475, 7.5917163 }, + { 4.5, 7.48901469 }, + { 4.525, 7.3868253 }, + { 4.55, 7.28514558 }, + { 4.575, 7.183973 }, + { 4.6, 7.08330501 }, + { 4.625, 6.98313911 }, + { 4.65, 6.88347279 }, + { 4.675, 6.78430355 }, + { 4.7, 6.68562893 }, + { 4.725, 6.58744644 }, + { 4.75, 6.48975365 }, + { 4.775, 6.3925481 }, + { 4.8, 6.29582736 }, + { 4.825, 6.19958902 }, + { 4.85, 6.10383067 }, + { 4.875, 6.00854992 }, + { 4.9, 5.91374438 }, + { 4.925, 5.81941168 }, + { 4.95, 5.72554948 }, + { 4.975, 5.63215541 }, + { 5, -23 }, + { 5.025, -23 }, + { 5.05, -23 }, + { 5.075, -23 }, + { 5.1, -23 }, + { 5.125, -23 }, + { 5.15, -23 }, + { 5.175, -23 }, + { 5.2, -23 }, + { 5.225, -23 }, + { 5.25, -23 }, + { 5.275, -23 }, + { 5.3, -23 }, + { 5.325, -23 }, + { 5.35, -23 }, + { 5.375, -23 }, + { 5.4, -23 }, + { 5.425, -23 }, + { 5.45, -23 }, + { 5.475, -23 }, + { 5.5, -23 }, + { 5.525, -23 }, + { 5.55, -23 }, + { 5.575, -23 }, + { 5.6, -23 }, + { 5.625, -23 }, + { 5.65, -23 }, + { 5.675, -23 }, + { 5.7, -23 }, + { 5.725, -23 }, + { 5.75, -23 }, + { 5.775, -23 }, + { 5.8, -23 }, + { 5.825, -22.9501248 }, + { 5.85, -22.9004983 }, + { 5.875, -22.8511194 }, + { 5.9, -22.8019867 }, + { 5.925, -22.7530991 }, + { 5.95, -22.7044553 }, + { 5.975, -22.6560542 }, + { 6, -22.6078944 }, + { 6.025, -22.5599748 }, + { 6.05, -22.5122942 }, + { 6.075, -22.4648515 }, + { 6.1, -22.4176453 }, + { 6.125, -22.3706746 }, + { 6.15, -22.3239382 }, + { 6.175, -22.2774349 }, + { 6.2, -22.2311635 }, + { 6.225, -22.1851228 }, + { 6.25, -22.1393119 }, + { 6.275, -22.0937293 }, + { 6.3, -22.0483742 }, + { 6.325, -22.0032452 }, + { 6.35, -21.9583414 }, + { 6.375, -21.9136614 }, + { 6.4, -21.8692044 }, + { 6.425, -21.824969 }, + { 6.45, -21.7809543 }, + { 6.475, -21.7371591 }, + { 6.5, -21.6935824 }, + { 6.525, -21.6502229 }, + { 6.55, -21.6070798 }, + { 6.575, -21.5641518 }, + { 6.6, -21.5214379 }, + { 6.625, -21.478937 }, + { 6.65, -21.4366482 }, + { 6.675, -21.3945702 }, + { 6.7, -21.3527021 }, + { 6.725, -21.3110428 }, + { 6.75, -21.2695913 }, + { 6.775, -21.2283466 }, + { 6.8, -21.1873075 }, + { 6.825, -21.1464732 }, + { 6.85, -21.1058425 }, + { 6.875, -21.0654144 }, + { 6.9, -21.025188 }, + { 6.925, -20.9851622 }, + { 6.95, -20.945336 }, + { 6.975, -20.9057085 }, + { 7, -20.8662786 }, + { 7.025, -20.8270454 }, + { 7.05, -20.7880078 }, + { 7.075, -20.749165 }, + { 7.1, -20.7105159 }, + { 7.125, -20.6720595 }, + { 7.15, -20.6337949 }, + { 7.175, -20.5957212 }, + { 7.2, -20.5578374 }, + { 7.225, -20.5201425 }, + { 7.25, -20.4826357 }, + { 7.275, -20.4453159 }, + { 7.3, -20.4081822 }, + { 7.325, -20.3712337 }, + { 7.35, -20.3344696 }, + { 7.375, -20.2978887 }, + { 7.4, -20.2614904 }, + { 7.425, -20.2252735 }, + { 7.45, -20.1892373 }, + { 7.475, -20.1533809 }, + { 7.5, -20.1177032 }, + { 7.525, -20.0822035 }, + { 7.55, -20.0468809 }, + { 7.575, -20.0117344 }, + { 7.6, -19.9767633 }, + { 7.625, -19.9419665 }, + { 7.65, -19.9073433 }, + { 7.675, -19.8728928 }, + { 7.7, -19.8386141 }, + { 7.725, -19.8045064 }, + { 7.75, -19.7705687 }, + { 7.775, -19.7368004 }, + { 7.8, -19.7032005 }, + { 7.825, -19.6697681 }, + { 7.85, -19.6365025 }, + { 7.875, -19.6034028 }, + { 7.9, -19.5704682 }, + { 7.925, -19.5376979 }, + { 7.95, -19.5050909 }, + { 7.975, -19.4726467 }, + { 8, -19.4403642 }, + { 8.025, -19.4082428 }, + { 8.05, -19.3762815 }, + { 8.075, -19.3444797 }, + { 8.1, -19.3128365 }, + { 8.125, -19.2813511 }, + { 8.15, -19.2500227 }, + { 8.175, -19.2188506 }, + { 8.2, -19.1878339 }, + { 8.225, -19.156972 }, + { 8.25, -19.1262639 }, + { 8.275, -19.0957091 }, + { 8.3, -19.0653066 }, + { 8.325, -19.0350558 }, + { 8.35, -19.0049558 }, + { 8.375, -18.9750059 }, + { 8.4, -18.9452055 }, + { 8.425, -18.9155536 }, + { 8.45, -18.8860497 }, + { 8.475, -18.8566929 }, + { 8.5, -18.8274825 }, + { 8.525, -18.7984178 }, + { 8.55, -18.7694981 }, + { 8.575, -18.7407226 }, + { 8.6, -18.7120906 }, + { 8.625, -18.6836015 }, + { 8.65, -18.6552544 }, + { 8.675, -18.6270487 }, + { 8.7, -18.5989837 }, + { 8.725, -18.5710586 }, + { 8.75, -18.5432728 }, + { 8.775, -18.5156257 }, + { 8.8, -18.4881164 }, + { 8.825, -18.4607443 }, + { 8.85, -18.4335087 }, + { 8.875, -18.406409 }, + { 8.9, -18.3794444 }, + { 8.925, -18.3526143 }, + { 8.95, -18.325918 }, + { 8.975, -18.2993549 }, + { 9, -18.2729242 }, + { 9.025, -18.2466254 }, + { 9.05, -18.2204578 }, + { 9.075, -18.1944206 }, + { 9.1, -18.1685133 }, + { 9.125, -18.1427353 }, + { 9.15, -18.1170858 }, + { 9.175, -18.0915642 }, + { 9.2, -18.0661699 }, + { 9.225, -18.0409023 }, + { 9.25, -18.0157607 }, + { 9.275, -17.9907445 }, + { 9.3, -17.965853 }, + { 9.325, -17.9410857 }, + { 9.35, -17.916442 }, + { 9.375, -17.8919211 }, + { 9.4, -17.8675226 }, + { 9.425, -17.8432457 }, + { 9.45, -17.8190899 }, + { 9.475, -17.7950546 }, + { 9.5, -17.7711392 }, + { 9.525, -17.747343 }, + { 9.55, -17.7236655 }, + { 9.575, -17.7001061 }, + { 9.6, -17.6766643 }, + { 9.625, -17.6533393 }, + { 9.65, -17.6301307 }, + { 9.675, -17.6070378 }, + { 9.7, -17.5840601 }, + { 9.725, -17.561197 }, + { 9.75, -17.538448 }, + { 9.775, -17.5158123 }, + { 9.8, -17.4932896 }, + { 9.825, -17.4708793 }, + { 9.85, -17.4485807 }, + { 9.875, -17.4263933 }, + { 9.9, -17.4043165 }, + { 9.925, -17.3823499 }, + { 9.95, -17.3604929 }, + { 9.975, -17.3387448 },}; + + ASSERT_TRUE(testing::seq_eq(ums[{0, "a"}], exp)); + ASSERT_TRUE(testing::seq_eq(ums[{0, "b"}], exp)); + // gid == 1 is different, but of same size + EXPECT_EQ((ums[{1, "a"}].size()), exp.size()); + ASSERT_FALSE(testing::seq_eq(ums[{1, "a"}], exp)); + // now check the spikes + std::sort(spikes.begin(), spikes.end()); + EXPECT_EQ(spikes.size(), 3u); + std::vector sexp{2, 4, 5}; + ASSERT_TRUE(testing::seq_almost_eq(spikes, sexp)); +} diff --git a/test/unit/test_gathered_vector.cpp b/test/unit/test_gathered_vector.cpp new file mode 100644 index 0000000000..41f60d11eb --- /dev/null +++ b/test/unit/test_gathered_vector.cpp @@ -0,0 +1,26 @@ +#include + +#include + +#include "arbor/common_types.hpp" +#include "communication/gathered_vector.hpp" + + +TEST(gathered_vector, invariants) { + auto sc = std::vector { + {0, 0}, // 0-3 + {0, 1}, + {0, 2}, + {1, 3}, // 3-5 + {1, 4}, + // 5-5 empty! + {3, 5}, // 5-7 + {3, 6} + }; + auto pt = std::vector{0, 3, 5, 5, 7}; + auto gv = arb::gathered_vector{std::move(sc), std::move(pt)}; + auto partition = gv.partition(); + auto values = gv.values(); + ASSERT_TRUE(std::is_sorted(partition.begin(), partition.end())); + arb_assert(partition.back() == values.size()); +} diff --git a/test/unit/test_simulation.cpp b/test/unit/test_simulation.cpp index d67ab5173e..88bdec4072 100644 --- a/test/unit/test_simulation.cpp +++ b/test/unit/test_simulation.cpp @@ -186,7 +186,7 @@ TEST(simulation, restart) { collected.insert(collected.end(), spikes.begin(), spikes.end()); }); - double tfinal = trigger_times.back()+delay*(n/2+0.1); + double tfinal = trigger_times.back()+delay*(0.5*n + 0.1); constexpr double dt = 0.01; auto cut_from = std::partition_point(expected_spikes.begin(), expected_spikes.end(), [tfinal](auto spike) { return spike.time