From b7bfab53aa2c93fb7741513c47136758e0954529 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Tue, 7 Nov 2023 10:10:08 +0100 Subject: [PATCH 01/13] Add AdEx cells. --- arbor/CMakeLists.txt | 1 + arbor/adex_cell_group.cpp | 273 ++++++++++++++++++++++++ arbor/adex_cell_group.hpp | 83 +++++++ arbor/benchmark_cell_group.hpp | 2 + arbor/cable_cell_group.hpp | 2 + arbor/cell_group.hpp | 1 - arbor/cell_group_factory.cpp | 53 +++-- arbor/cell_group_factory.hpp | 23 +- arbor/common_types_io.cpp | 2 + arbor/include/arbor/adex_cell.hpp | 52 +++++ arbor/include/arbor/common_types.hpp | 1 + arbor/include/arbor/event_generator.hpp | 12 +- arbor/include/arbor/spike.hpp | 1 - arbor/lif_cell_group.hpp | 2 + arbor/partition_load_balance.cpp | 2 - arbor/spike_source_cell_group.hpp | 2 + doc/concepts/adex_cell.rst | 76 +++++++ doc/concepts/probe_sample.rst | 8 +- doc/cpp/adex_cell.rst | 72 +++++++ doc/cpp/lif_cell.rst | 18 +- doc/cpp/probe_sample.rst | 29 +++ doc/python/adex_cell.rst | 70 ++++++ doc/python/lif_cell.rst | 18 +- doc/python/probe_sample.rst | 27 +++ example/CMakeLists.txt | 1 + example/adex/CMakeLists.txt | 3 + example/adex/README.md | 12 ++ example/adex/adex.cpp | 126 +++++++++++ python/cells.cpp | 61 +++++- python/example/adex.py | 49 +++++ python/identifiers.cpp | 2 + python/probes.cpp | 38 ++++ python/recipe.cpp | 7 +- test/unit/CMakeLists.txt | 1 + 34 files changed, 1057 insertions(+), 73 deletions(-) create mode 100644 arbor/adex_cell_group.cpp create mode 100644 arbor/adex_cell_group.hpp create mode 100644 arbor/include/arbor/adex_cell.hpp create mode 100644 doc/concepts/adex_cell.rst create mode 100644 doc/cpp/adex_cell.rst create mode 100644 doc/python/adex_cell.rst create mode 100644 example/adex/CMakeLists.txt create mode 100644 example/adex/README.md create mode 100644 example/adex/adex.cpp create mode 100644 python/example/adex.py diff --git a/arbor/CMakeLists.txt b/arbor/CMakeLists.txt index c456e1e355..88fd506fa2 100644 --- a/arbor/CMakeLists.txt +++ b/arbor/CMakeLists.txt @@ -26,6 +26,7 @@ set(arbor_sources io/serialize_hex.cpp label_resolution.cpp lif_cell_group.cpp + adex_cell_group.cpp cable_cell_group.cpp mechcat.cpp mechinfo.cpp diff --git a/arbor/adex_cell_group.cpp b/arbor/adex_cell_group.cpp new file mode 100644 index 0000000000..6fb3818e62 --- /dev/null +++ b/arbor/adex_cell_group.cpp @@ -0,0 +1,273 @@ +#include "adex_cell_group.hpp" + +#include + +#include "arbor/math.hpp" +#include "util/rangeutil.hpp" +#include "util/span.hpp" +#include "label_resolution.hpp" +#include "profile/profiler_macro.hpp" + +#include + +using namespace arb; + +// Constructor containing gid of first cell in a group and a container of all cells. +adex_cell_group::adex_cell_group(const std::vector& gids, + const recipe& rec, + cell_label_range& cg_sources, + cell_label_range& cg_targets): + gids_(gids) { + + for (auto gid: gids_) { + const auto& cell = util::any_cast(rec.get_cell_description(gid)); + // set up cell state + cells_.push_back(cell); + // tell our caller about this cell's connections + cg_sources.add_cell(); + cg_targets.add_cell(); + cg_sources.add_label(cell.source, {0, 1}); + cg_targets.add_label(cell.target, {0, 1}); + // insert probes where needed + auto probes = rec.get_probes(gid); + for (const auto& probe: probes) { + if (probe.address.type() == typeid(adex_probe_voltage)) { + cell_address_type addr{gid, probe.tag}; + if (probes_.count(addr)) throw dup_cell_probe(cell_kind::adex, gid, probe.tag); + probes_.insert_or_assign(addr, adex_probe_info{adex_probe_kind::voltage, {}}); + } + else if (probe.address.type() == typeid(adex_probe_adaption)) { + cell_address_type addr{gid, probe.tag}; + if (probes_.count(addr)) throw dup_cell_probe(cell_kind::adex, gid, probe.tag); + probes_.insert_or_assign(addr, adex_probe_info{adex_probe_kind::adaption, {}}); + } + else { + throw bad_cell_probe{cell_kind::adex, gid}; + } + } + // set up the internal state + next_update_.push_back(0); + } +} + +cell_kind adex_cell_group::get_cell_kind() const { + return cell_kind::adex; +} + +void adex_cell_group::advance(epoch ep, time_type dt, const event_lane_subrange& event_lanes) { + PE(advance:adex); + for (auto lid: util::make_span(gids_.size())) { + // Advance each cell independently. + advance_cell(ep.t1, dt, lid, event_lanes); + } + PL(); +} + +const std::vector& adex_cell_group::spikes() const { + return spikes_; +} + +void adex_cell_group::clear_spikes() { + spikes_.clear(); +} + +void adex_cell_group::add_sampler(sampler_association_handle h, + cell_member_predicate probeset_ids, + schedule sched, + sampler_function fn) { + std::lock_guard guard(sampler_mex_); + std::vector probeset; + for (const auto& [k, v]: probes_) { + if (probeset_ids(k)) probeset.push_back(k); + } + auto assoc = arb::sampler_association{std::move(sched), + std::move(fn), + std::move(probeset)}; + auto result = samplers_.insert({h, std::move(assoc)}); + arb_assert(result.second); +} + +void adex_cell_group::remove_sampler(sampler_association_handle h) { + std::lock_guard guard(sampler_mex_); + samplers_.erase(h); +} +void adex_cell_group::remove_all_samplers() { + std::lock_guard guard(sampler_mex_); + samplers_.clear(); +} + +void adex_cell_group::reset() { + spikes_.clear(); +} + +void adex_cell_group::advance_cell(time_type tfinal, + time_type dt, + cell_gid_type lid, + const event_lane_subrange& event_lanes) { + auto gid = gids_[lid]; + auto& cell = cells_[lid]; + auto n_events = static_cast(!event_lanes.empty() ? event_lanes[lid].size() : 0); + + // Flattened sampler map + std::vector sample_metadata; + std::vector sample_callbacks; + std::vector> sample_records; + + struct sample_event { + time_type time; + adex_probe_kind kind; + double* data; + }; + + std::vector sample_events; + std::vector sample_data; + + if (!samplers_.empty()) { + auto tlast = time_; + std::vector sample_sizes; + std::size_t total_size = 0; + { + std::lock_guard guard(sampler_mex_); + for (auto& [hdl, assoc]: samplers_) { + // No need to generate events + if (assoc.probeset_ids.empty()) continue; + // Construct sampling times, might give us the last time we sampled, so skip that. + auto times = util::make_range(assoc.sched.events(tlast, tfinal)); + while (!times.empty() && times.front() == tlast) times.left++; + if (times.empty()) continue; + for (unsigned idx = 0; idx < assoc.probeset_ids.size(); ++idx) { + const auto& pid = assoc.probeset_ids[idx]; + if (pid.gid != gid) continue; + const auto& probe = probes_.at(pid); + sample_metadata.push_back({pid, idx, util::any_ptr{&probe.metadata}}); + sample_callbacks.push_back(hdl); + sample_records.emplace_back(); + auto& records = sample_records.back(); + sample_sizes.push_back(times.size()); + total_size += times.size(); + for (auto t: times) { + records.push_back(sample_record{t, nullptr}); + sample_events.push_back(sample_event{t, probe.kind, nullptr}); + } + } + } + } + // Flat list of things to sample + // NOTE: Need to allocate in one go, else reallocation will mess up the pointers! + sample_data.resize(total_size); + auto rx = 0; + for (int ix = 0; ix < sample_sizes.size(); ++ix) { + auto size = sample_sizes[ix]; + for (int kx = 0; kx < size; ++kx) { + sample_records[ix][kx].data = const_cast(sample_data.data() + rx); + sample_events[rx].data = sample_data.data() + rx; + ++rx; + } + } + } + util::sort_by(sample_events, [](const auto& s) { return s.time; }); + auto n_samples = sample_events.size(); + + // integrate until tfinal using the exact solution of membrane voltage differential equation. + // prepare event processing + auto e_idx = 0; + auto s_idx = 0; + for (; time_ < tfinal; time_ += dt) { + auto dE = cell.V_m - cell.E_L; + auto il = cell.g*dE; + auto is = cell.g*cell.delta*exp((cell.V_m - cell.V_th)/cell.delta); + auto dV = (is - il - cell.w)/cell.C_m; + auto V_m = cell.V_m + dt*dV; + + auto dW = (cell.a*dE - cell.w)/cell.tau; + auto w = cell.w + dt*dW; + + auto weight = 0.0; + + // Process events in [time, time + dt) + for (;;) { + auto e_t = e_idx < n_events ? event_lanes[lid][e_idx].time : tfinal; + if (e_t < time_) { + ++e_idx; + continue; + } + auto s_t = s_idx < n_samples ? sample_events[s_idx].time : tfinal; + if (s_t < time_) { + ++s_idx; + continue; + } + auto t = std::min(e_t, s_t); + if (t >= time_ + dt || t >= tfinal) break; + if (t == e_t) { + weight += event_lanes[lid][e_idx].weight; + ++e_idx; + } + else { + auto& [time, kind, ptr] = sample_events[s_idx]; + auto t = (time - time_)/dt; + if (kind == adex_probe_kind::voltage) { + if (next_update_[lid] > time_) { + *ptr = cell.E_R; + } else { + *ptr = math::lerp(cell.V_m, V_m + weight/cell.C_m, t); + } + } + else if (kind == adex_probe_kind::adaption) { + *ptr = math::lerp(cell.w, w, t); + } + else { + // impossible! + throw arbor_internal_error{"Unknown ADEX probe."}; + } + ++s_idx; + } + } + cell.w = w; + // if we are still in refractory period, bail now, before we alter membrane voltage + if (next_update_[lid] > time_) continue; + V_m += weight/cell.C_m; + // enter refractory period and emit spike + if (V_m >= cell.V_th) { + // interpolate the spike time and emit event. + // NOTE: _Do_ the interpolation + auto t_spike = time_; + spikes_.emplace_back(cell_member_type{gid, 0}, t_spike); + // reset membrane potential + V_m = cell.E_R; + // schedule next update + next_update_[lid] = time_ + cell.t_ref; + w += cell.b; + } + cell.V_m = V_m; + } + + auto n_samplers = sample_callbacks.size(); + { + std::lock_guard guard{sampler_mex_}; + for (int s_idx = 0; s_idx < n_samplers; ++s_idx) { + const auto& sd = sample_records[s_idx]; + auto hdl = sample_callbacks[s_idx]; + const auto& fun = samplers_[hdl].sampler; + if (!fun) throw std::runtime_error{"NO sampler"}; + fun(sample_metadata[s_idx], sd.size(), sd.data()); + } + } +} + +void adex_cell_group::t_serialize(serializer& ser, const std::string& k) const { + serialize(ser, k, *this); +} + +void adex_cell_group::t_deserialize(serializer& ser, const std::string& k) { + deserialize(ser, k, *this); +} + +std::vector adex_cell_group::get_probe_metadata(const cell_address_type& key) const { + // SAFETY: Probe associations are fixed after construction, so we do not + // need to grab the mutex. + if (probes_.count(key)) { + return {probe_metadata{key, 0, &probes_.at(key).metadata}}; + } else { + return {}; + } +} diff --git a/arbor/adex_cell_group.hpp b/arbor/adex_cell_group.hpp new file mode 100644 index 0000000000..bc19343fed --- /dev/null +++ b/arbor/adex_cell_group.hpp @@ -0,0 +1,83 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "sampler_map.hpp" +#include "cell_group.hpp" +#include "label_resolution.hpp" + +namespace arb { + +struct ARB_ARBOR_API adex_cell_group: public cell_group { + adex_cell_group() = default; + + // Constructor containing gid of first cell in a group and a container of all cells. + adex_cell_group(const std::vector& gids, const recipe& rec, cell_label_range& cg_sources, cell_label_range& cg_targets); + + cell_kind get_cell_kind() const override; + void reset() override; + void advance(epoch epoch, time_type dt, const event_lane_subrange& events) override; + + virtual const std::vector& spikes() const override; + void clear_spikes() override; + + // Sampler association methods below should be thread-safe, as they might be invoked + // from a sampler call back called from a different cell group running on a different thread. + void add_sampler(sampler_association_handle, cell_member_predicate, schedule, sampler_function) override; + void remove_sampler(sampler_association_handle) override; + void remove_all_samplers() override; + + std::vector get_probe_metadata(const cell_address_type&) const override; + + ARB_SERDES_ENABLE(adex_cell_group, time_, gids_, cells_, spikes_); + + virtual void t_serialize(serializer& ser, const std::string& k) const override; + virtual void t_deserialize(serializer& ser, const std::string& k) override; + + static bool backend_supported(backend_kind kind) { return kind == backend_kind::multicore; } + +private: + enum class adex_probe_kind { voltage, adaption }; + + struct adex_probe_info { + adex_probe_kind kind; + adex_probe_metadata metadata; + }; + + // Advances a single cell (lid) with the exact solution (jumps can be arbitrary). + // Parameter dt is ignored, since we make jumps between two consecutive spikes. + void advance_cell(time_type tfinal, time_type dt, cell_gid_type lid, const event_lane_subrange& event_lane); + + // current time + time_type time_ = 0.0; + + // List of the gids of the cells in the group. + std::vector gids_; + + // Cells that belong to this group. + std::vector cells_; + + // Spikes that are generated (not necessarily sorted). + std::vector spikes_; + + // SAFETY: We need to access samplers_ through a mutex since + // simulation::add_sampler might be called concurrently. + std::mutex sampler_mex_; + sampler_association_map samplers_; // + + // ADEX probe metadata, precalculated to pass to callbacks + std::unordered_map probes_; + + // Time of next possible update to model refractory periods. + std::vector next_update_; +}; + +} // namespace arb diff --git a/arbor/benchmark_cell_group.hpp b/arbor/benchmark_cell_group.hpp index 118165a726..fa46a8a5cf 100644 --- a/arbor/benchmark_cell_group.hpp +++ b/arbor/benchmark_cell_group.hpp @@ -37,6 +37,8 @@ class benchmark_cell_group: public cell_group { void t_serialize(serializer& ser, const std::string& k) const override; void t_deserialize(serializer& ser, const std::string& k) override; + static bool backend_supported(backend_kind kind) { return kind == backend_kind::multicore; } + private: std::vector cells_; std::vector spikes_; diff --git a/arbor/cable_cell_group.hpp b/arbor/cable_cell_group.hpp index 614b563a38..f53315c2cb 100644 --- a/arbor/cable_cell_group.hpp +++ b/arbor/cable_cell_group.hpp @@ -56,6 +56,8 @@ struct ARB_ARBOR_API cable_cell_group: public cell_group { void t_serialize(serializer& ser, const std::string& k) const override; void t_deserialize(serializer& ser, const std::string& k) override; + + static bool backend_supported(backend_kind kind) { return kind == backend_kind::multicore || kind == backend_kind::gpu; } private: // List of the gids of the cells in the group. std::vector gids_; diff --git a/arbor/cell_group.hpp b/arbor/cell_group.hpp index d372f11877..2b95de9d7f 100644 --- a/arbor/cell_group.hpp +++ b/arbor/cell_group.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include diff --git a/arbor/cell_group_factory.cpp b/arbor/cell_group_factory.cpp index d6ced713da..e302b63066 100644 --- a/arbor/cell_group_factory.cpp +++ b/arbor/cell_group_factory.cpp @@ -9,6 +9,7 @@ #include "execution_context.hpp" #include "fvm_lowered_cell.hpp" #include "lif_cell_group.hpp" +#include "adex_cell_group.hpp" #include "cable_cell_group.hpp" #include "spike_source_cell_group.hpp" @@ -19,39 +20,53 @@ cell_group_ptr make_cell_group(Args&&... args) { return cell_group_ptr(new Impl(std::forward(args)...)); } -ARB_ARBOR_API cell_group_factory cell_kind_implementation( - cell_kind ck, backend_kind bk, const execution_context& ctx, arb_seed_type seed) -{ +ARB_ARBOR_API cell_group_factory +cell_kind_implementation(cell_kind ck, + backend_kind bk, + const execution_context& ctx, + arb_seed_type seed) { using gid_vector = std::vector; - switch (ck) { case cell_kind::cable: - return [bk, ctx, seed](const gid_vector& gids, const recipe& rec, cell_label_range& cg_sources, cell_label_range& cg_targets) { + if (!cable_cell_group::backend_supported(bk)) break; + return [bk, ctx, seed](const gid_vector& gids, + const recipe& rec, + cell_label_range& cg_sources, + cell_label_range& cg_targets) { return make_cell_group(gids, rec, cg_sources, cg_targets, make_fvm_lowered_cell(bk, ctx, seed)); }; - case cell_kind::spike_source: - if (bk!=backend_kind::multicore) break; - - return [](const gid_vector& gids, const recipe& rec, cell_label_range& cg_sources, cell_label_range& cg_targets) { + if (!spike_source_cell_group::backend_supported(bk)) break; + return [](const gid_vector& gids, + const recipe& rec, + cell_label_range& cg_sources, + cell_label_range& cg_targets) { return make_cell_group(gids, rec, cg_sources, cg_targets); }; - case cell_kind::lif: - if (bk!=backend_kind::multicore) break; - - return [](const gid_vector& gids, const recipe& rec, cell_label_range& cg_sources, cell_label_range& cg_targets) { + if (!lif_cell_group::backend_supported(bk)) break; + return [](const gid_vector& gids, + const recipe& rec, + cell_label_range& cg_sources, + cell_label_range& cg_targets) { return make_cell_group(gids, rec, cg_sources, cg_targets); }; - case cell_kind::benchmark: - if (bk!=backend_kind::multicore) break; - - return [](const gid_vector& gids, const recipe& rec, cell_label_range& cg_sources, cell_label_range& cg_targets) { + if (!benchmark_cell_group::backend_supported(bk)) break; + return [](const gid_vector& gids, + const recipe& rec, + cell_label_range& cg_sources, + cell_label_range& cg_targets) { return make_cell_group(gids, rec, cg_sources, cg_targets); }; - - default: ; + case cell_kind::adex: + if (!adex_cell_group::backend_supported(bk)) break; + return [](const gid_vector& gids, + const recipe& rec, + cell_label_range& cg_sources, + cell_label_range& cg_targets) { + return make_cell_group(gids, rec, cg_sources, cg_targets); + }; } return cell_group_factory{}; // empty function => not supported diff --git a/arbor/cell_group_factory.hpp b/arbor/cell_group_factory.hpp index 3f784b3f82..87ec96a508 100644 --- a/arbor/cell_group_factory.hpp +++ b/arbor/cell_group_factory.hpp @@ -18,15 +18,20 @@ namespace arb { -using cell_group_factory = std::function< - cell_group_ptr(const std::vector&, const recipe&, cell_label_range& cg_sources, cell_label_range& cg_targets)>; - -ARB_ARBOR_API cell_group_factory cell_kind_implementation( - cell_kind, backend_kind, const execution_context&, std::uint64_t = 0); - -inline bool cell_kind_supported( - cell_kind c, backend_kind b, const execution_context& ctx) -{ +using cell_group_factory = std::function&, + const recipe&, + cell_label_range& cg_sources, + cell_label_range& cg_targets)>; + +ARB_ARBOR_API cell_group_factory +cell_kind_implementation(cell_kind, + backend_kind, + const execution_context&, std::uint64_t = 0); + +inline bool +cell_kind_supported(cell_kind c, + backend_kind b, + const execution_context& ctx) { return static_cast(cell_kind_implementation(c, b, ctx)); } diff --git a/arbor/common_types_io.cpp b/arbor/common_types_io.cpp index 9ade3cd041..051cf96f26 100644 --- a/arbor/common_types_io.cpp +++ b/arbor/common_types_io.cpp @@ -31,6 +31,8 @@ ARB_ARBOR_API std::ostream& operator<<(std::ostream& o, arb::cell_kind k) { return o << "lif"; case arb::cell_kind::benchmark: return o << "benchmark_cell"; + case arb::cell_kind::adex: + return o << "adex_cell"; } return o; } diff --git a/arbor/include/arbor/adex_cell.hpp b/arbor/include/arbor/adex_cell.hpp new file mode 100644 index 0000000000..c91575eb29 --- /dev/null +++ b/arbor/include/arbor/adex_cell.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include + +namespace arb { + +// Model parameters of adaptive exponential cell (AdEx) see +// +// Self-sustained asynchronous irregular states and Up–Down states in thalamic, +// cortical and thalamocortical networks of nonlinear integrate-and-fire neurons +// +// A. Destehxe 2009 +// +// +struct ARB_SYMBOL_VISIBLE adex_cell { + cell_tag_type source; // Label of source + cell_tag_type target; // Label of target + + // Neuronal parameters. + double delta = 2.5; // Steepness parameter [mV] + double V_th = -20; // Firing threshold [mV] + double C_m = 0.28; // Membrane capacitance [pF] + double E_L = -70; // Resting potential [mV] + double E_R = E_L; // Reset potential [mV] + double V_m = E_L; // Initial value of the Membrane potential [mV] + double t_ref = 2.5; // Refractory period [ms] + double g = 0.03; // Leak conductivity [uS] + // Adaption parameters + double tau = 144; // Adaption decaying constant [ms] + double w = 0; // Initial value for adaption parameter [nA] + double a = 0.004; // Adaption dynamics [uS]. + double b = 0.08; // When spikes trigger, increase w by this [nA] + adex_cell() = default; + adex_cell(cell_tag_type source, cell_tag_type target): source(std::move(source)), target(std::move(target)) {} + + ARB_SERDES_ENABLE(adex_cell, source, target, delta, V_th, C_m, E_L, E_R, V_m, t_ref); +}; + +// ADEX probe metadata, to be passed to sampler callbacks. Intentionally left blank. +struct ARB_SYMBOL_VISIBLE adex_probe_metadata {}; + +// Voltage estimate `U` [mV]. +// Sample value type: `double` +struct ARB_SYMBOL_VISIBLE adex_probe_voltage {}; + +// Adapation variable `w` [nA]. +// Sample value type: `double` +struct ARB_SYMBOL_VISIBLE adex_probe_adaption {}; + +} // namespace arb diff --git a/arbor/include/arbor/common_types.hpp b/arbor/include/arbor/common_types.hpp index ea97872fec..2089e2b78f 100644 --- a/arbor/include/arbor/common_types.hpp +++ b/arbor/include/arbor/common_types.hpp @@ -152,6 +152,7 @@ enum class ARB_SYMBOL_VISIBLE cell_kind { lif, // Leaky-integrate and fire neuron. spike_source, // Cell that generates spikes at a user-supplied sequence of time points. benchmark, // Proxy cell used for benchmarking. + adex, // ADaptive EXponential. }; ARB_ARBOR_API std::ostream& operator<<(std::ostream& o, lid_selection_policy m); diff --git a/arbor/include/arbor/event_generator.hpp b/arbor/include/arbor/event_generator.hpp index c378ef7d03..74ea6bdad6 100644 --- a/arbor/include/arbor/event_generator.hpp +++ b/arbor/include/arbor/event_generator.hpp @@ -109,13 +109,11 @@ event_generator empty_generator( // Generate events at integer multiples of dt that lie between tstart and tstop. -inline event_generator regular_generator( - cell_local_label_type target, - float weight, - time_type tstart, - time_type dt, - time_type tstop=terminal_time) -{ +inline event_generator regular_generator(cell_local_label_type target, + float weight, + time_type tstart, + time_type dt, + time_type tstop=terminal_time) { return event_generator(std::move(target), weight, regular_schedule(tstart, dt, tstop)); } diff --git a/arbor/include/arbor/spike.hpp b/arbor/include/arbor/spike.hpp index 04843bcb67..2ce5af8783 100644 --- a/arbor/include/arbor/spike.hpp +++ b/arbor/include/arbor/spike.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include diff --git a/arbor/lif_cell_group.hpp b/arbor/lif_cell_group.hpp index 9572d06bd8..93e26b2a1d 100644 --- a/arbor/lif_cell_group.hpp +++ b/arbor/lif_cell_group.hpp @@ -42,6 +42,8 @@ struct ARB_ARBOR_API lif_cell_group: public cell_group { virtual void t_serialize(serializer& ser, const std::string& k) const override; virtual void t_deserialize(serializer& ser, const std::string& k) override; + static bool backend_supported(backend_kind kind) { return kind == backend_kind::multicore; } + private: enum class lif_probe_kind { voltage }; diff --git a/arbor/partition_load_balance.cpp b/arbor/partition_load_balance.cpp index 8718c743d1..13ded5051e 100644 --- a/arbor/partition_load_balance.cpp +++ b/arbor/partition_load_balance.cpp @@ -11,8 +11,6 @@ #include #include "cell_group_factory.hpp" -#include "execution_context.hpp" -#include "gpu_context.hpp" #include "util/maputil.hpp" #include "util/partition.hpp" #include "util/span.hpp" diff --git a/arbor/spike_source_cell_group.hpp b/arbor/spike_source_cell_group.hpp index 44e89d97c1..148fd2238b 100644 --- a/arbor/spike_source_cell_group.hpp +++ b/arbor/spike_source_cell_group.hpp @@ -40,6 +40,8 @@ class ARB_ARBOR_API spike_source_cell_group: public cell_group { virtual void t_serialize(serializer& ser, const std::string& k) const override; virtual void t_deserialize(serializer& ser, const std::string& k) override; + static bool backend_supported(backend_kind kind) { return kind == backend_kind::multicore; } + private: std::vector spikes_; std::vector gids_; diff --git a/doc/concepts/adex_cell.rst b/doc/concepts/adex_cell.rst new file mode 100644 index 0000000000..647b6c65d3 --- /dev/null +++ b/doc/concepts/adex_cell.rst @@ -0,0 +1,76 @@ +.. _adexcell: + +AdEx cells +=========== + +The Adaptive Exponential point neuron type follows two coupled differential +equations with an explicit refractory period. +See for example + +.. code-block:: + + Self-sustained asynchronous irregular states and Up–Down states + in thalamic, cortical and thalamocortical networks of nonlinear + integrate-and-fire neurons + + A. Destexhe 2009 + +Outside the refractory period the dynamics are + +.. math:: + C_m\partial_t V_m = -g(V_m - E_L) + g \Delta \exp\left(\frac{V_m - V_\mathrm{th}{\Delta}\right) - w + I\\ + \partial_t w = a(V_m - E_L) - w + f + +with the following terms and their default/starting values + +* Membrane potential :math:`V_\mathrm{m}`, by default :math:`V_\mathrm{m}(t=0) = E_\mathrm{L}` +* Resting potential :math:`E_\mathrm{L} = -70\,mV` +* Reset potential :math:`E_\mathrm{R}`, by default :math:`E_\mathrm{R} = E_\mathrm{L}` +* Membrane potential steepness parameter :math:`\Delta = 2.5\,mV` +* Membrane capacitance :math:`C_\mathrm{m} = 0.28\,nF` +* Firing threshold :math:`V_\mathrm{th} = -20\,mV` +* Refractory period :math:`t_\mathrm{ref} = 2.5\,ms` +* Leak conductivity :math:`g = 0.03\,mu S` + +and + +* adaptivity parameter :math:`w = 0\,nA` +* decay time :math:`\tau = 144\,ms` +* :math:`a = 0.004\,\mu S` +* :math:`b = 0.08\,nA` + +Incoming spikes give rise to a instantaneous jump in the membrane potential +:math:`V_m \rightarrow V_m + \frac{\omega}{C_m}`, i.e. :math:`I` is a delta +functional. + +Every time :math:`t_s` the cell emits a spike, :math:`w` is incremented by :math:`b`; thus +:math:`f(t) = b\delta(t-t_s)` and the refractory begins, for a duration of +:math:`t_\mathrm{ref}`. During that time + +.. math:: + V_m = E_R\\ + \partial_t w = a(V_m - E_L) - w + +and all incoming spikes are discarded without effect. + +The morphology of a AdEx cell is implicitly modelled as a single +:term:`compartment `; each cell has one built-in **source** and +one built-in **target** which need to be given labels when the cell is created. +The labels are used to form connections to and from the cell. AdEx cells do +neither support additional **sources** or **targets**, nor **gap junctions**. +They do not support adding density or point mechanisms. + +AdEx cells can be probed to obtain their current membrane potential and +adaptivity parameter values, see :ref:`probesample`. + +.. figure:: ../images/adex.svg + :width: 600 + :align: center + + Plot of :math:`V_m` and :math:`w` over time for an AdEx cell. + +API +--- + +* :ref:`Python ` +* :ref:`C++ ` diff --git a/doc/concepts/probe_sample.rst b/doc/concepts/probe_sample.rst index 9b553eb683..e390d116eb 100644 --- a/doc/concepts/probe_sample.rst +++ b/doc/concepts/probe_sample.rst @@ -3,10 +3,10 @@ Probing and Sampling ==================== -Both cable cells and LIF cells can be probed, see here for more details on cells -:ref:`modelcells`. The LIF cell, however, has a much smaller set of observable -quantities and only offers scalar probes. Thus, the following discussion is -tailored to the cable cell. +Cable, AdEx and LIF cells can be probed, see here for more details on cells +:ref:`modelcells`. The AdEx and LIF cells, however, have a much smaller set of +observable quantities and only offers scalar probes. Thus, the following +discussion is tailored to the cable cell. Definitions *********** diff --git a/doc/cpp/adex_cell.rst b/doc/cpp/adex_cell.rst new file mode 100644 index 0000000000..e43681ff74 --- /dev/null +++ b/doc/cpp/adex_cell.rst @@ -0,0 +1,72 @@ +.. _cppadexcell: + +AdEx cells +=========== + +.. cpp:namespace:: arb + +.. cpp:class:: adex_cell + + An adaptive exponential cell. + + .. function:: adex_cell(source, target) + + Constructor: assigns the label ``source`` to the single built-in source on the cell; and assigns the + label ``target`` to the single built-in target on the cell. + + .. cpp:member:: cell_tag_type source + + The label of the single built-in source on the cell. Used for forming + connections from the cell in the :cpp:class:`recipe` by creating a + :cpp:class:`connection`. + + .. cpp:member:: cell_tag_type target + + The label of the single built-in target on the cell. Used for forming + connections to the cell in the :cpp:class:`recipe` by creating a + :cpp:class:`connection`. + + .. cpp:member:: double V_th + + Firing threshold :math:`V_\mathrm{th} = -20\,mV` [mV]. + + .. cpp:member:: double C_m + + Membrane capacitance :math:`C_\mathrm{m} = 0.28\,nF` [nF]. + + .. cpp:member:: double E_L + + Resting potential :math:`E_\mathrm{L} = -70\,mV` [mV] + + .. cpp:member:: double E_R + + Reset potential :math:`E_\mathrm{R} = -70\,mV` [mV]. + + .. cpp:member:: double V_m + + Initial value of the Membrane potential :math:`V_\mathrm{m} = -70\,mV` [mV]. + + .. cpp:member:: double t_ref + + Refractory period :math:`t_\mathrm{ref} = 2.5\,ms` [ms]. + + .. cpp:member:: double g + + Leak conductivity :math:`g = 0.03\,mu S` [uS]. + + .. cpp:member:: double w + + :math:`w` initial value :math:`w = 0\,mA` [nA]. + + .. cpp:member:: double tau + + :math:`w` decaying constant :math:`\tau = 144\,ms` [ms]. + + .. cpp:member:: double a + + :math:`w` dynamics :math:`a = 0.004\,\mu S` [uS]. + + + .. cpp:member:: double b + + Increase in :math:`w` after emitted spike :math:`b = 0.08\,nA` [nA] diff --git a/doc/cpp/lif_cell.rst b/doc/cpp/lif_cell.rst index 60a72881a4..2d5b15026d 100644 --- a/doc/cpp/lif_cell.rst +++ b/doc/cpp/lif_cell.rst @@ -7,23 +7,25 @@ LIF cells .. cpp:class:: lif_cell - A benchmarking cell (leaky integrate-and-fire), used by Arbor developers to test communication performance, - with neuronal parameters: + A leaky integrate-and-fire cell .. cpp:function:: lif_cell(cell_tag_type source, cell_tag_type target) - Constructor: assigns the label ``source`` to the single built-in source on the cell; and assigns the - label ``target`` to the single built-in target on the cell. + Constructor: assigns the label ``source`` to the single built-in source + on the cell; and assigns the label ``target`` to the single built-in + target on the cell. .. cpp:member:: cell_tag_type source - The label of the single built-in source on the cell. Used for forming connections from the cell in the - :cpp:class:`recipe` by creating a :cpp:class:`connection`. + The label of the single built-in source on the cell. Used for forming + connections from the cell in the :cpp:class:`recipe` by creating a + :cpp:class:`connection`. .. cpp:member:: cell_tag_type target - The label of the single built-in target on the cell. Used for forming connections to the cell in the - :cpp:class:`recipe` by creating a :cpp:class:`connection`. + The label of the single built-in target on the cell. Used for forming + connections to the cell in the :cpp:class:`recipe` by creating a + :cpp:class:`connection`. .. cpp:member:: double tau_m diff --git a/doc/cpp/probe_sample.rst b/doc/cpp/probe_sample.rst index 6e7a21b959..7d46065929 100644 --- a/doc/cpp/probe_sample.rst +++ b/doc/cpp/probe_sample.rst @@ -687,3 +687,32 @@ Queries cell membrane potential. * Sample value: ``double``. Membrane potential (mV). * Metadata: none + +AdEx cell probing and sampling +=============================== + +Membrane voltage +---------------- + +.. code:: + + struct adex_probe_voltage {}; + +Queries cell membrane potential. + +* Sample value: ``double``. Membrane potential (mV). + +* Metadata: none + +Adaption Variable +----------------- + +.. code:: + + struct adex_probe_adaption {}; + +Queries cell adaption variable :math:`w`. + +* Sample value: ``double``. (nA). + +* Metadata: none diff --git a/doc/python/adex_cell.rst b/doc/python/adex_cell.rst new file mode 100644 index 0000000000..cd982072a4 --- /dev/null +++ b/doc/python/adex_cell.rst @@ -0,0 +1,70 @@ +.. _pyadexcell: + +AdEx cells +=========== + +.. currentmodule:: arbor + +.. py:class:: adex_cell + + An adaptive exponential cell. + + .. function:: adex_cell(source, target) + + Constructor: assigns the label ``source`` to the single built-in source on the cell; and assigns the + label ``target`` to the single built-in target on the cell. + + .. attribute:: source + + The label of the single built-in source on the cell. Used for forming connections from the cell in the + :py:class:`arbor.recipe` by creating a :py:class:`arbor.connection`. + + .. attribute:: target + + The label of the single built-in target on the cell. Used for forming connections to the cell in the + :py:class:`arbor.recipe` by creating a :py:class:`arbor.connection`. + + .. attribute:: V_th + + Firing threshold :math:`V_\mathrm{th} = -20\,mV` [mV]. + + .. attribute:: C_m + + Membrane capacitance :math:`C_\mathrm{m} = 0.28\,nF` [nF]. + + .. attribute:: E_L + + Resting potential :math:`E_\mathrm{L} = -70\,mV` [mV] + + .. attribute:: E_R + + Reset potential :math:`E_\mathrm{R} = -70\,mV` [mV]. + + .. attribute:: V_m + + Initial value of the Membrane potential :math:`V_\mathrm{m} = -70\,mV` [mV]. + + .. attribute:: t_ref + + Refractory period :math:`t_\mathrm{ref} = 2.5\,ms` [ms]. + + .. attribute:: g + + Leak conductivity :math:`g = 0.03\,mu S` [uS]. + + .. attribute:: w + + :math:`w` initial value :math:`w = 0\,mA` [nA]. + + .. attribute:: tau + + :math:`w` decaying constant :math:`\tau = 144\,ms` [ms]. + + .. attribute:: a + + :math:`w` dynamics :math:`a = 0.004\,\mu S` [uS]. + + + .. attribute:: b + + Increase in :math:`w` after emitted spike :math:`b = 0.08\,nA` [nA] diff --git a/doc/python/lif_cell.rst b/doc/python/lif_cell.rst index e9450ef75f..0b98c419ba 100644 --- a/doc/python/lif_cell.rst +++ b/doc/python/lif_cell.rst @@ -7,23 +7,25 @@ LIF cells .. py:class:: lif_cell - A benchmarking cell (leaky integrate-and-fire), used by Arbor developers to test communication performance, - with neuronal parameters: + A leaky integrate-and-fire cell .. function:: lif_cell(source, target) - Constructor: assigns the label ``source`` to the single built-in source on the cell; and assigns the - label ``target`` to the single built-in target on the cell. + Constructor: assigns the label ``source`` to the single built-in source + on the cell; and assigns the label ``target`` to the single built-in + target on the cell. .. attribute:: source - The label of the single built-in source on the cell. Used for forming connections from the cell in the - :py:class:`arbor.recipe` by creating a :py:class:`arbor.connection`. + The label of the single built-in source on the cell. Used for forming + connections from the cell in the :py:class:`arbor.recipe` by creating a + :py:class:`arbor.connection`. .. attribute:: target - The label of the single built-in target on the cell. Used for forming connections to the cell in the - :py:class:`arbor.recipe` by creating a :py:class:`arbor.connection`. + The label of the single built-in target on the cell. Used for forming + connections to the cell in the :py:class:`arbor.recipe` by creating a + :py:class:`arbor.connection`. .. attribute:: tau_m diff --git a/doc/python/probe_sample.rst b/doc/python/probe_sample.rst index 107080af79..776c385e83 100644 --- a/doc/python/probe_sample.rst +++ b/doc/python/probe_sample.rst @@ -321,8 +321,35 @@ LIF Cell probing ================ Membrane voltage +---------------- + .. py:function:: lif_probe_voltage() Current cell membrane potential (mV). Metadata: none + +AdEx cell probing and sampling +=============================== + +Membrane voltage +---------------- + + .. py:function:: adex_probe_voltage() + +Queries cell membrane potential. + +* Sample value: ``double``. Membrane potential (mV). + +* Metadata: none + +Adaption Variable +----------------- + + .. py:function:: adex_probe_adaption() + +Queries cell adaption variable :math:`w`. + +* Sample value: ``double``. (nA). + +* Metadata: none diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index dfbe051e49..068d71c2ce 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -2,6 +2,7 @@ # Example executable targets should be added to the 'examples' target as dependencies. add_custom_target(examples DEPENDS) +add_subdirectory(adex) add_subdirectory(busyring) add_subdirectory(drybench) add_subdirectory(dryrun) diff --git a/example/adex/CMakeLists.txt b/example/adex/CMakeLists.txt new file mode 100644 index 0000000000..fe79437183 --- /dev/null +++ b/example/adex/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(adex EXCLUDE_FROM_ALL adex.cpp) +add_dependencies(examples adex) +target_link_libraries(adex PRIVATE arbor arborio arborenv arbor-sup ext-tinyopt) diff --git a/example/adex/README.md b/example/adex/README.md new file mode 100644 index 0000000000..0a688750e9 --- /dev/null +++ b/example/adex/README.md @@ -0,0 +1,12 @@ +# 'single' example. + +Example of simulating a single neuron with AdEx dynamics. + +### Command line options + +| Option | Effect | +|-----------------------|--------| +| -d, --dt TIME | Set the maximum integration time step [ms] | +| -t, --t-end TIME | Set the simulation duration [ms] | +| -w, --weight WEIGHT | Set the synaptic weight [µS] | + diff --git a/example/adex/adex.cpp b/example/adex/adex.cpp new file mode 100644 index 0000000000..52ff7293de --- /dev/null +++ b/example/adex/adex.cpp @@ -0,0 +1,126 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#include + +struct options { + double t_end = 100.0; + double dt = 0.025; + float syn_weight = 0.01; +}; + +options parse_options(int argc, char** argv); + + +std::mutex mex; +std::vector times; +std::vector Um; +std::vector w; + +void sampler(const arb::probe_metadata& pm, std::size_t n, const arb::sample_record* samples) { + auto* meta = arb::util::any_cast(pm.meta); + if(meta == nullptr) { + std::cerr << "Hey metadata is not ADEX metadata!\n"; + throw std::runtime_error{"ADEX metadata type mismatch"}; + } + + if (pm.id.tag == "Um") { + for (std::size_t ix = 0; ix < n; ++ix) { + auto* value = arb::util::any_cast(samples[ix].data); + if(value == nullptr) { + std::cerr << "Hey payload is not const double* at index " << ix << "\n"; + throw std::runtime_error{"ADEX payload type mismatch"}; + } + times.push_back(samples[ix].time); + Um.push_back(*value); + } + } + else { + for (std::size_t ix = 0; ix < n; ++ix) { + auto* value = arb::util::any_cast(samples[ix].data); + if(value == nullptr) { + std::cerr << "Hey payload is not const double* at index " << ix << "\n"; + throw std::runtime_error{"ADEX payload type mismatch"}; + } + w.push_back(*value); + } + } +} + +void print() { + std::cerr << std::fixed << std::setprecision(4); + for (int ix = 0; ix < times.size(); ++ix) { + std::cout << times[ix] << ", " << Um[ix] << ", " << w[ix] << '\n'; + } +} + +struct recipe: public arb::recipe { + arb::cell_size_type num_cells() const override { return 1; } + + std::vector get_probes(arb::cell_gid_type) const override { + return {arb::probe_info{arb::adex_probe_voltage{}, "Um"}, + arb::probe_info{arb::adex_probe_adaption{}, "w"}}; + } + + arb::cell_kind get_cell_kind(arb::cell_gid_type) const override { return arb::cell_kind::adex; } + + arb::util::unique_any get_cell_description(arb::cell_gid_type) const override { + auto cell = arb::adex_cell{"src", "tgt"}; + cell.V_m = -80; + cell.E_R = -90; + return cell; + } + + std::vector event_generators(arb::cell_size_type) const override { + return {arb::regular_generator({"tgt"}, 10, 20, 8, 80)}; + } +}; + +int main(int argc, char** argv) { + options opt = parse_options(argc, argv); + recipe R; + + arb::simulation sim(R); + + sim.add_sampler(arb::all_probes, + arb::regular_schedule(opt.dt), + sampler); + sim.set_global_spike_callback([](const auto& spks) { + for (const auto& spk: spks) { + std::cerr << spk.time << ", " << spk.source.gid << ", " << spk.source.index << '\n'; + } + }); + + sim.run(opt.t_end, opt.dt); + print(); +} + +options parse_options(int argc, char** argv) { + options opt; + for (int ix = 1; ix < argc; ++ix) { + auto arg = argv + ix; + if (auto dt = to::parse(arg, "-d", "--dt")) { + opt.dt = dt.value(); + } + else if (auto t_end = to::parse(arg, "-t", "--t-end")) { + opt.t_end = t_end.value(); + } + else if (auto weight = to::parse(arg, "-w", "--weight")) { + opt.syn_weight = weight.value(); + } + else { + to::usage(argv[0], "[-d|--dt TIME] [-t|--t-end TIME] [-w|--weight WEIGHT]"); + std::exit(1); + } + } + return opt; +} diff --git a/python/cells.cpp b/python/cells.cpp index f46bcf8aaf..ca4d6dab26 100644 --- a/python/cells.cpp +++ b/python/cells.cpp @@ -1,6 +1,4 @@ #include -#include -#include #include #include #include @@ -17,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -34,7 +33,6 @@ #include "error.hpp" #include "proxy.hpp" #include "pybind11/cast.h" -#include "pybind11/stl.h" #include "pybind11/pytypes.h" #include "schedule.hpp" #include "strprintf.hpp" @@ -125,16 +123,17 @@ std::optional maybe_method(pybind11::object method) { return {}; } -// -// string printers -// - -std::string lif_str(const arb::lif_cell& c){ + std::string lif_str(const arb::lif_cell& c){ return util::pprintf( - "", - c.tau_m, c.V_th, c.C_m, c.E_L, c.V_m, c.t_ref); + "", + c.source, c.target, c.tau_m, c.V_th, c.C_m, c.E_L, c.E_R, c.V_m, c.t_ref); } +std::string adex_str(const arb::adex_cell& c){ + return util::pprintf( + "", + c.source, c.target, c.delta, c.V_th, c.C_m, c.E_L, c.E_R, c.V_m, c.t_ref, c.g, c.tau, c.w, c.a, c.b); +} std::string mechanism_desc_str(const arb::mechanism_desc& md) { return util::pprintf("mechanism('{}', {})", @@ -212,7 +211,6 @@ void register_cells(pybind11::module& m) { pybind11::class_ lif_cell(m, "lif_cell", "A leaky integrate-and-fire cell."); - lif_cell .def(pybind11::init<>( [](arb::cell_tag_type source_label, arb::cell_tag_type target_label){ @@ -240,6 +238,47 @@ void register_cells(pybind11::module& m) { .def("__repr__", &lif_str) .def("__str__", &lif_str); + // arb::lif_cell + + pybind11::class_ adex_cell(m, "adex_cell", + "A leaky integrate-and-fire cell."); + adex_cell + .def(pybind11::init<>( + [](arb::cell_tag_type source_label, arb::cell_tag_type target_label){ + return arb::adex_cell(std::move(source_label), std::move(target_label));}), + "source_label"_a, "target_label"_a, + "Construct a adex cell with one source labeled 'source_label', and one target labeled 'target_label'.") + .def_readwrite("delta", &arb::adex_cell::delta, + "Steepness [mV].") + .def_readwrite("g", &arb::adex_cell::g, + "Leak conductivity [uS].") + .def_readwrite("tau", &arb::adex_cell::tau, + "Adaption decay time [ms].") + .def_readwrite("w", &arb::adex_cell::tau, + "Adaption variable [nA].") + .def_readwrite("w", &arb::adex_cell::b, + "Adaption variable increase on spike [nA].") + .def_readwrite("w", &arb::adex_cell::b, + "Adaption variable dynamics [uS].") + .def_readwrite("V_th", &arb::adex_cell::V_th, + "Firing threshold [mV].") + .def_readwrite("C_m", &arb::adex_cell::C_m, + "Membrane capacitance [pF].") + .def_readwrite("E_L", &arb::adex_cell::E_L, + "Resting potential [mV].") + .def_readwrite("E_R", &arb::adex_cell::E_R, + "Reset potential [mV].") + .def_readwrite("V_m", &arb::adex_cell::V_m, + "Initial value of the Membrane potential [mV].") + .def_readwrite("t_ref", &arb::adex_cell::t_ref, + "Refractory period [ms].") + .def_readwrite("source", &arb::adex_cell::source, + "Label of the single build-in source on the cell.") + .def_readwrite("target", &arb::adex_cell::target, + "Label of the single build-in target on the cell.") + .def("__repr__", &adex_str) + .def("__str__", &adex_str); + // arb::label_dict pybind11::class_ label_dict(m, "label_dict", diff --git a/python/example/adex.py b/python/example/adex.py new file mode 100644 index 0000000000..38c53a701e --- /dev/null +++ b/python/example/adex.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import arbor as A +import pandas as pd +import seaborn as sns + +T = 100 +dt = 0.05 + +class recipe(A.recipe): + + def __init__(self): + A.recipe.__init__(self) + self.weight = 10 + + def num_cells(self): + return 1 + + def cell_kind(self, _): + return A.cell_kind.adex + + def cell_description(self, _): + cell = A.adex_cell("src", "tgt") + return cell + + def event_generators(self, _): + return [A.event_generator("tgt", self.weight, A.regular_schedule(8))] + + def probes(self, _): + return [A.adex_probe_voltage("Um"), A.adex_probe_adaption("w")] + +rec = recipe() +sim = A.simulation(rec) + +sch = A.regular_schedule(dt) +hUm = sim.sample((0, "Um"), sch) +hw = sim.sample((0, "w"), sch) + +sim.run(T, dt) + +df_list = [] + +Um, _ = sim.samples(hUm)[0] +W, _ = sim.samples(hw)[0] +df = pd.DataFrame( + {"t/ms": Um[:, 0], "U/mV": Um[:, 1], "W/nA": W[:, 1]} + ) + +sns.relplot(data=df, x='t/ms', y='U/mV', kind="line").savefig("adex_results.pdf") diff --git a/python/identifiers.cpp b/python/identifiers.cpp index b75833da36..0eb5b397ba 100644 --- a/python/identifiers.cpp +++ b/python/identifiers.cpp @@ -140,6 +140,8 @@ void register_identifiers(py::module& m) { "A cell with morphology described by branching 1D cable segments.") .value("lif", arb::cell_kind::lif, "Leaky-integrate and fire neuron.") + .value("adex", arb::cell_kind::adex, + "Adaptive exponential neuron.") .value("spike_source", arb::cell_kind::spike_source, "Proxy cell that generates spikes from a spike sequence provided by the user."); diff --git a/python/probes.cpp b/python/probes.cpp index 161c438be3..8049ee9f50 100644 --- a/python/probes.cpp +++ b/python/probes.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -94,6 +95,24 @@ struct recorder_lif: recorder_base { recorder_lif(const arb::lif_probe_metadata* meta_ptr): recorder_base(meta_ptr, 1) {} }; +struct recorder_adex: recorder_base { + using recorder_base::sample_raw_; + + void record(any_ptr, std::size_t n_sample, const arb::sample_record* records) override { + for (std::size_t i = 0; i(records[i].data)) { + sample_raw_.push_back(records[i].time); + sample_raw_.push_back(*v_ptr); + } + else { + std::string ty = records[i].data.type().name(); + throw arb::arbor_internal_error("ADEX recorder: unexpected sample type " + ty); + } + } + } + + recorder_adex(const arb::adex_probe_metadata* meta_ptr): recorder_base(meta_ptr, 1) {} +}; template struct recorder_cable_vector: recorder_base { @@ -243,6 +262,15 @@ arb::probe_info lif_probe_voltage(const std::string& tag) { } +// ADEX cell probes +arb::probe_info adex_probe_voltage(const std::string& tag) { + return {arb::adex_probe_voltage{}, tag}; +} + +arb::probe_info adex_probe_adaption(const std::string& tag) { + return {arb::adex_probe_adaption{}, tag}; +} + // Add wrappers to module, recorder factories to global data. void register_cable_probes(pybind11::module& m, pyarb_global_ptr global_ptr) { @@ -254,6 +282,9 @@ void register_cable_probes(pybind11::module& m, pyarb_global_ptr global_ptr) { py::class_ lif_probe_metadata(m, "lif_probe_metadata", "Probe metadata associated with a LIF cell probe."); + py::class_ adex_probe_metadata(m, "adex_probe_metadata", + "Probe metadata associated with a AdEx cell probe."); + py::class_ cable_probe_point_info(m, "cable_probe_point_info", "Probe metadata associated with a cable cell probe for point process state."); @@ -274,6 +305,12 @@ void register_cable_probes(pybind11::module& m, pyarb_global_ptr global_ptr) { m.def("lif_probe_voltage", &lif_probe_voltage, "Probe specification for LIF cell membrane voltage.", "tag"_a); + m.def("adex_probe_voltage", &adex_probe_voltage, + "Probe specification for ADEX cell membrane voltage.", + "tag"_a); + m.def("adex_probe_adaption", &adex_probe_adaption, + "Probe specification for ADEX cell adaption variable.", + "tag"_a); m.def("cable_probe_membrane_voltage", &cable_probe_membrane_voltage, "Probe specification for cable cell membrane voltage interpolated at points in a location set.", "where"_a, "tag"_a); @@ -354,6 +391,7 @@ void register_cable_probes(pybind11::module& m, pyarb_global_ptr global_ptr) { register_probe_meta_maps(global_ptr); register_probe_meta_maps, recorder_cable_vector_point_info>(global_ptr); register_probe_meta_maps(global_ptr); + register_probe_meta_maps(global_ptr); } } // namespace pyarb diff --git a/python/recipe.cpp b/python/recipe.cpp index 2649ba6bdf..355f7553bd 100644 --- a/python/recipe.cpp +++ b/python/recipe.cpp @@ -1,4 +1,3 @@ -#include #include #include @@ -9,13 +8,13 @@ #include #include #include +#include #include #include #include #include #include "arbor/cable_cell_param.hpp" -#include "conversion.hpp" #include "error.hpp" #include "event_generator.hpp" #include "strprintf.hpp" @@ -48,7 +47,9 @@ static arb::util::unique_any convert_cell(pybind11::object o) { if (isinstance(o)) { return arb::util::unique_any(cast(o)); } - + if (isinstance(o)) { + return arb::util::unique_any(cast(o)); + } throw pyarb_error("recipe.cell_description returned \"" + std::string(pybind11::str(o)) + "\" which does not describe a known Arbor cell type"); diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 1a85d77360..c4a5875027 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -87,6 +87,7 @@ set(unit_sources test_lexcmp.cpp test_label_resolution.cpp test_lif_cell_group.cpp + test_adex_cell_group.cpp test_local_context.cpp test_maputil.cpp test_mask_stream.cpp From f1a375e383c88fc548d431273c21c577afc0de34 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Tue, 7 Nov 2023 11:12:45 +0100 Subject: [PATCH 02/13] Black --- python/example/adex.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/python/example/adex.py b/python/example/adex.py index 38c53a701e..d8de79165e 100644 --- a/python/example/adex.py +++ b/python/example/adex.py @@ -7,8 +7,8 @@ T = 100 dt = 0.05 -class recipe(A.recipe): +class recipe(A.recipe): def __init__(self): A.recipe.__init__(self) self.weight = 10 @@ -29,12 +29,13 @@ def event_generators(self, _): def probes(self, _): return [A.adex_probe_voltage("Um"), A.adex_probe_adaption("w")] + rec = recipe() sim = A.simulation(rec) sch = A.regular_schedule(dt) -hUm = sim.sample((0, "Um"), sch) -hw = sim.sample((0, "w"), sch) +hUm = sim.sample((0, "Um"), sch) +hw = sim.sample((0, "w"), sch) sim.run(T, dt) @@ -42,8 +43,6 @@ def probes(self, _): Um, _ = sim.samples(hUm)[0] W, _ = sim.samples(hw)[0] -df = pd.DataFrame( - {"t/ms": Um[:, 0], "U/mV": Um[:, 1], "W/nA": W[:, 1]} - ) +df = pd.DataFrame({"t/ms": Um[:, 0], "U/mV": Um[:, 1], "W/nA": W[:, 1]}) -sns.relplot(data=df, x='t/ms', y='U/mV', kind="line").savefig("adex_results.pdf") +sns.relplot(data=df, x="t/ms", y="U/mV", kind="line").savefig("adex_results.pdf") From cc89ad0dfdc15f6e98fcb4db1c113a4b80da018a Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Tue, 7 Nov 2023 10:36:58 +0100 Subject: [PATCH 03/13] The test... --- test/unit/test_adex_cell_group.cpp | 1124 ++++++++++++++++++++++++++++ 1 file changed, 1124 insertions(+) create mode 100644 test/unit/test_adex_cell_group.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)); +} From c147e38df405429af350296f066c7f060f76671e Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Tue, 7 Nov 2023 12:57:00 +0100 Subject: [PATCH 04/13] Cherrypicking --- ext/pybind11 | 2 +- spack/package.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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/spack/package.py b/spack/package.py index 50c1c5ea2b..5838cf0d77 100644 --- a/spack/package.py +++ b/spack/package.py @@ -103,6 +103,7 @@ class Arbor(CMakePackage, CudaPackage): depends_on("py-pybind11@2.6:", type="build") depends_on("py-pybind11@2.8.1:", when="@0.5.3:", type="build") depends_on("py-pybind11@2.10.1:", when="@0.7.1:", type="build") + depends_on("py-pybind11@2.11.1:", when="@0.9.1:", type="build") # sphinx based documentation with when("+doc"): From 5a2ddcb4b71d0aa7d3709fc0c12c21918bdf991a Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Mon, 13 Nov 2023 12:34:24 +0100 Subject: [PATCH 05/13] Add concepts/adex and test --- doc/concepts/adex_cell.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/concepts/adex_cell.rst b/doc/concepts/adex_cell.rst index 647b6c65d3..6c00dbc116 100644 --- a/doc/concepts/adex_cell.rst +++ b/doc/concepts/adex_cell.rst @@ -15,7 +15,7 @@ See for example A. Destexhe 2009 -Outside the refractory period the dynamics are +wOutside the refractory period the dynamics are .. math:: C_m\partial_t V_m = -g(V_m - E_L) + g \Delta \exp\left(\frac{V_m - V_\mathrm{th}{\Delta}\right) - w + I\\ From 2ad3b100a789e0df8c28044563da9c18b5675dc2 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:58:59 +0200 Subject: [PATCH 06/13] Tests, bump units for unary unit negation. --- arbor/adex_cell_group.cpp | 40 ++--- arbor/adex_cell_group.hpp | 53 ++++++- arbor/include/arbor/adex_cell.hpp | 33 ++-- arbor/include/arbor/recipe.hpp | 17 +- arbor/lif_cell_group.hpp | 32 ++-- example/adex/adex.cpp | 12 +- ext/units | 2 +- test/unit/test_adex_cell_group.cpp | 243 ++++++++++++----------------- 8 files changed, 227 insertions(+), 205 deletions(-) diff --git a/arbor/adex_cell_group.cpp b/arbor/adex_cell_group.cpp index 6fb3818e62..b8ca888673 100644 --- a/arbor/adex_cell_group.cpp +++ b/arbor/adex_cell_group.cpp @@ -8,8 +8,6 @@ #include "label_resolution.hpp" #include "profile/profiler_macro.hpp" -#include - using namespace arb; // Constructor containing gid of first cell in a group and a container of all cells. @@ -22,12 +20,12 @@ adex_cell_group::adex_cell_group(const std::vector& gids, for (auto gid: gids_) { const auto& cell = util::any_cast(rec.get_cell_description(gid)); // set up cell state - cells_.push_back(cell); + cells_.emplace_back(cell); // tell our caller about this cell's connections cg_sources.add_cell(); cg_targets.add_cell(); - cg_sources.add_label(cell.source, {0, 1}); - cg_targets.add_label(cell.target, {0, 1}); + cg_sources.add_label(hash_value(cell.source), {0, 1}); + cg_targets.add_label(hash_value(cell.target), {0, 1}); // insert probes where needed auto probes = rec.get_probes(gid); for (const auto& probe: probes) { @@ -63,13 +61,9 @@ void adex_cell_group::advance(epoch ep, time_type dt, const event_lane_subrange& PL(); } -const std::vector& adex_cell_group::spikes() const { - return spikes_; -} +const std::vector& adex_cell_group::spikes() const { return spikes_; } -void adex_cell_group::clear_spikes() { - spikes_.clear(); -} +void adex_cell_group::clear_spikes() { spikes_.clear(); } void adex_cell_group::add_sampler(sampler_association_handle h, cell_member_predicate probeset_ids, @@ -97,7 +91,7 @@ void adex_cell_group::remove_all_samplers() { } void adex_cell_group::reset() { - spikes_.clear(); + clear_spikes(); } void adex_cell_group::advance_cell(time_type tfinal, @@ -129,13 +123,14 @@ void adex_cell_group::advance_cell(time_type tfinal, { std::lock_guard guard(sampler_mex_); for (auto& [hdl, assoc]: samplers_) { + auto n_probeset_ids = assoc.probeset_ids.size(); // No need to generate events - if (assoc.probeset_ids.empty()) continue; + if (0 == n_probeset_ids) continue; // Construct sampling times, might give us the last time we sampled, so skip that. auto times = util::make_range(assoc.sched.events(tlast, tfinal)); - while (!times.empty() && times.front() == tlast) times.left++; + while (!times.empty() && times.front() <= tlast) times.left++; if (times.empty()) continue; - for (unsigned idx = 0; idx < assoc.probeset_ids.size(); ++idx) { + for (unsigned idx = 0; idx < n_probeset_ids; ++idx) { const auto& pid = assoc.probeset_ids[idx]; if (pid.gid != gid) continue; const auto& probe = probes_.at(pid); @@ -164,6 +159,7 @@ void adex_cell_group::advance_cell(time_type tfinal, ++rx; } } + arb_assert(rx == total_size); } util::sort_by(sample_events, [](const auto& s) { return s.time; }); auto n_samples = sample_events.size(); @@ -187,16 +183,11 @@ void adex_cell_group::advance_cell(time_type tfinal, // Process events in [time, time + dt) for (;;) { auto e_t = e_idx < n_events ? event_lanes[lid][e_idx].time : tfinal; - if (e_t < time_) { - ++e_idx; - continue; - } + if (e_t < time_) { ++e_idx; continue; } auto s_t = s_idx < n_samples ? sample_events[s_idx].time : tfinal; - if (s_t < time_) { - ++s_idx; - continue; - } + if (s_t < time_) { ++s_idx; continue; } auto t = std::min(e_t, s_t); + arb_assert(t >= time_); if (t >= time_ + dt || t >= tfinal) break; if (t == e_t) { weight += event_lanes[lid][e_idx].weight; @@ -204,6 +195,7 @@ void adex_cell_group::advance_cell(time_type tfinal, } else { auto& [time, kind, ptr] = sample_events[s_idx]; + arb_assert(nullptr != ptr); auto t = (time - time_)/dt; if (kind == adex_probe_kind::voltage) { if (next_update_[lid] > time_) { @@ -248,7 +240,7 @@ void adex_cell_group::advance_cell(time_type tfinal, const auto& sd = sample_records[s_idx]; auto hdl = sample_callbacks[s_idx]; const auto& fun = samplers_[hdl].sampler; - if (!fun) throw std::runtime_error{"NO sampler"}; + arb_assert(fun); fun(sample_metadata[s_idx], sd.size(), sd.data()); } } diff --git a/arbor/adex_cell_group.hpp b/arbor/adex_cell_group.hpp index bc19343fed..151cb48228 100644 --- a/arbor/adex_cell_group.hpp +++ b/arbor/adex_cell_group.hpp @@ -16,6 +16,57 @@ namespace arb { +#define UNIT_OF(x, u) \ + x = adex.x.value_as(arb::units::u); \ + if (!std::isfinite(x)) throw std::domain_error(#x " must be finite and in [" #u "]"); \ + +struct ARB_SYMBOL_VISIBLE adex_lowered_cell { + cell_tag_type source; // Label of source + cell_tag_type target; // Label of target + + // Neuronal parameters. + double delta = 2.5; // Steepness parameter [mV] + double V_th = -20; // Firing threshold [mV] + double C_m = 0.28; // Membrane capacitance [pF] + double E_L = -70; // Resting potential [mV] + double E_R = E_L; // Reset potential [mV] + double V_m = E_L; // Initial value of the Membrane potential [mV] + double t_ref = 2.5; // Refractory period [ms] + double g = 0.03; // Leak conductivity [uS] + // Adaption parameters + double tau = 144; // Adaption decaying constant [ms] + double w = 0; // Initial value for adaption parameter [nA] + double a = 0.004; // Adaption dynamics [uS]. + double b = 0.08; // When spikes trigger, increase w by this [nA] + + adex_lowered_cell() = default; + adex_lowered_cell(const adex_cell& adex) { + source = adex.source; + target = adex.target; + + UNIT_OF(delta, mV); + UNIT_OF(V_th, mV); + UNIT_OF(C_m, pF); + UNIT_OF(E_L, mV); + UNIT_OF(E_R, mV); + UNIT_OF(V_m, mV); + UNIT_OF(t_ref, ms); + UNIT_OF(g, uS); + UNIT_OF(tau, ms); + UNIT_OF(w, nA); + UNIT_OF(a, uS); + UNIT_OF(b, nA); + + if (t_ref < 0) throw std::domain_error("t_ref must be positive."); + if (t_ref < 0) throw std::domain_error("tau must be positive."); + if (C_m < 0) throw std::domain_error("C_m must be positive."); + } + + ARB_SERDES_ENABLE(adex_lowered_cell, delta, V_th, V_m, C_m, E_L, E_R, t_ref, g, tau, w, a, b); +}; + +#undef UNIT_OF + struct ARB_ARBOR_API adex_cell_group: public cell_group { adex_cell_group() = default; @@ -63,7 +114,7 @@ struct ARB_ARBOR_API adex_cell_group: public cell_group { std::vector gids_; // Cells that belong to this group. - std::vector cells_; + std::vector cells_; // Spikes that are generated (not necessarily sorted). std::vector spikes_; diff --git a/arbor/include/arbor/adex_cell.hpp b/arbor/include/arbor/adex_cell.hpp index 60ed6bb5d8..fc67e57da6 100644 --- a/arbor/include/arbor/adex_cell.hpp +++ b/arbor/include/arbor/adex_cell.hpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace arb { @@ -12,29 +13,31 @@ namespace arb { // cortical and thalamocortical networks of nonlinear integrate-and-fire neurons // // A. Destehxe 2009 -// + +namespace U = arb::units; +using namespace U::literals; + struct ARB_SYMBOL_VISIBLE adex_cell { cell_tag_type source; // Label of source cell_tag_type target; // Label of target // Neuronal parameters. - double delta = 2.5; // Steepness parameter [mV] - double V_th = -20; // Firing threshold [mV] - double C_m = 0.28; // Membrane capacitance [pF] - double E_L = -70; // Resting potential [mV] - double E_R = E_L; // Reset potential [mV] - double V_m = E_L; // Initial value of the Membrane potential [mV] - double t_ref = 2.5; // Refractory period [ms] - double g = 0.03; // Leak conductivity [uS] + U::quantity delta = 2.5_mV; // Steepness parameter [mV] + U::quantity V_th = -1*20_mV; // Firing threshold [mV] + U::quantity C_m = 0.28_pF; // Membrane capacitance [pF] + U::quantity E_L = -1*70_mV; // Resting potential [mV] + U::quantity E_R = E_L; // Reset potential [mV] + U::quantity V_m = E_L; // Initial value of the Membrane potential [mV] + U::quantity t_ref = 2.5_ms; // Refractory period [ms] + U::quantity g = 0.03_uS; // Leak conductivity [uS] // Adaption parameters - double tau = 144; // Adaption decaying constant [ms] - double w = 0; // Initial value for adaption parameter [nA] - double a = 0.004; // Adaption dynamics [uS]. - double b = 0.08; // When spikes trigger, increase w by this [nA] + U::quantity tau = 144_ms; // Adaption decaying constant [ms] + U::quantity w = 0_nA; // Initial value for adaption parameter [nA] + U::quantity a = 0.004_uS; // Adaption dynamics [uS]. + U::quantity b = 0.08_nA; // When spikes trigger, increase w by this [nA] + adex_cell() = default; adex_cell(cell_tag_type source, cell_tag_type target): source(std::move(source)), target(std::move(target)) {} - - ARB_SERDES_ENABLE(adex_cell, source, target, delta, V_th, C_m, E_L, E_R, V_m, t_ref); }; // ADEX probe metadata, to be passed to sampler callbacks. Intentionally left blank. diff --git a/arbor/include/arbor/recipe.hpp b/arbor/include/arbor/recipe.hpp index dbcba5fcde..05fdaa6187 100644 --- a/arbor/include/arbor/recipe.hpp +++ b/arbor/include/arbor/recipe.hpp @@ -49,13 +49,22 @@ struct cell_connection_base { L source; cell_local_label_type target; - float weight; // [()] - float delay; // [ms] + float weight = 0; // [()] + float delay = 0; // [ms] + + cell_connection_base() = default; + cell_connection_base(const cell_connection_base&) = default; + cell_connection_base(cell_connection_base&&) = default; + + cell_connection_base& operator=(const cell_connection_base&) = default; + cell_connection_base& operator=(cell_connection_base&&) = default; cell_connection_base(L src, cell_local_label_type dst, float w, const U::quantity& d): source(std::move(src)), target(std::move(dst)), weight(w), delay(d.value_as(U::ms)) { - if (std::isnan(weight)) throw std::out_of_range("Connection weight must be finite."); - if (std::isnan(delay) || delay < 0) throw std::out_of_range("Connection delay must be non-negative and infinite in units of [ms]."); + if (!std::isfinite(weight)) throw std::out_of_range("Connection weight must be finite."); + std::string err = "Connection delay must be non-negative and finite in units of [ms]. Is: "; + err += std::to_string(delay); + if (!std::isfinite(delay) || delay < 0) throw std::out_of_range(err); } }; diff --git a/arbor/lif_cell_group.hpp b/arbor/lif_cell_group.hpp index 0847e8eaf8..a47dc21a2f 100644 --- a/arbor/lif_cell_group.hpp +++ b/arbor/lif_cell_group.hpp @@ -16,6 +16,11 @@ namespace arb { +#define UNIT_OF(x, u) \ + x = lif.x.value_as(arb::units::u); \ + if (!std::isfinite(x)) throw std::domain_error(#x " must be finite and in [" #u "]"); \ + + // Model parameters of leaky integrate and fire neuron model. struct ARB_SYMBOL_VISIBLE lif_lowered_cell { cell_tag_type source; // Label of source. @@ -35,26 +40,23 @@ struct ARB_SYMBOL_VISIBLE lif_lowered_cell { source = lif.source; target = lif.target; - tau_m = lif.tau_m.value_as(U::ms); - V_th = lif.V_th.value_as(U::mV); - C_m = lif.C_m.value_as(U::pF); - E_L = lif.E_L.value_as(U::mV); - E_R = lif.E_R.value_as(U::mV); - V_m = lif.V_m.value_as(U::mV); - t_ref = lif.t_ref.value_as(U::ms); - - if (std::isnan(V_th)) throw std::out_of_range("V_th must be finite and in [mV]"); - if (std::isnan(tau_m) || tau_m < 0) throw std::out_of_range("tau_m must be positive, finite, and in [ms]"); - if (std::isnan(C_m) || C_m < 0) throw std::out_of_range("C_m must be positive, finite, and in [pF]"); - if (std::isnan(E_L)) throw std::out_of_range("E_L must be finite and in [mV]"); - if (std::isnan(E_R)) throw std::out_of_range("E_R must be finite and in [mV]"); - if (std::isnan(V_m)) throw std::out_of_range("V_m must be finite and in [mV]"); - if (std::isnan(t_ref) || t_ref < 0) throw std::out_of_range("t_ref must be positive, finite, and in [ms]"); + UNIT_OF(tau_m, ms); + UNIT_OF(V_th, mV); + UNIT_OF(C_m, pF); + UNIT_OF(E_L, mV); + UNIT_OF(E_R, mV); + UNIT_OF(V_m, mV); + UNIT_OF(t_ref, ms); + + if (tau_m < 0) throw std::domain_error("tau_m must be positive."); + if (C_m < 0) throw std::domain_error("C_m must be positive."); + if (t_ref < 0) throw std::domain_error("t_ref must be positive."); } ARB_SERDES_ENABLE(lif_lowered_cell, source, target, tau_m, V_th, C_m, E_L, E_R, V_m, t_ref); }; +#undef UNIT_OF struct ARB_ARBOR_API lif_cell_group: public cell_group { lif_cell_group() = default; diff --git a/example/adex/adex.cpp b/example/adex/adex.cpp index 52ff7293de..179eddf499 100644 --- a/example/adex/adex.cpp +++ b/example/adex/adex.cpp @@ -9,6 +9,8 @@ #include #include +namespace U = arb::units; +using namespace U::literals; #include @@ -75,13 +77,13 @@ struct recipe: public arb::recipe { arb::util::unique_any get_cell_description(arb::cell_gid_type) const override { auto cell = arb::adex_cell{"src", "tgt"}; - cell.V_m = -80; - cell.E_R = -90; + cell.V_m = -1*80_mV; + cell.E_R = -1*90_mV; return cell; } std::vector event_generators(arb::cell_size_type) const override { - return {arb::regular_generator({"tgt"}, 10, 20, 8, 80)}; + return {arb::regular_generator({"tgt"}, 10, 20_ms, 8_ms, 80_ms)}; } }; @@ -92,7 +94,7 @@ int main(int argc, char** argv) { arb::simulation sim(R); sim.add_sampler(arb::all_probes, - arb::regular_schedule(opt.dt), + arb::regular_schedule(opt.dt * U::ms), sampler); sim.set_global_spike_callback([](const auto& spks) { for (const auto& spk: spks) { @@ -100,7 +102,7 @@ int main(int argc, char** argv) { } }); - sim.run(opt.t_end, opt.dt); + sim.run(opt.t_end * U::ms, opt.dt * U::ms); print(); } diff --git a/ext/units b/ext/units index e7aff9f8e4..7917f5f2cf 160000 --- a/ext/units +++ b/ext/units @@ -1 +1 @@ -Subproject commit e7aff9f8e4cc1ce19b1ea7e7095036e64123601f +Subproject commit 7917f5f2cfefdcc90b5085ade91761d06df74e59 diff --git a/test/unit/test_adex_cell_group.cpp b/test/unit/test_adex_cell_group.cpp index 24ee097e0a..3bf1b63c13 100644 --- a/test/unit/test_adex_cell_group.cpp +++ b/test/unit/test_adex_cell_group.cpp @@ -11,168 +11,138 @@ #include #include #include +#include -#include "adex_cell_group.hpp" +namespace U = arb::units; +using namespace U::literals; -using namespace arb; +namespace { // 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): +struct ring_recipe: public arb::recipe { + ring_recipe(arb::cell_size_type n_adex_cells, float weight, const U::quantity& delay): n_adex_cells_(n_adex_cells), weight_(weight), delay_(delay) {} - cell_size_type num_cells() const override { - return n_adex_cells_ + 1; - } + arb::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; + arb::cell_kind get_cell_kind(arb::cell_gid_type gid) const override { + if (gid == 0) return arb::cell_kind::spike_source; + return arb::cell_kind::adex; } - std::vector connections_on(cell_gid_type gid) const override { - if (gid == 0) { - return {}; - } + std::vector connections_on(arb::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); + std::vector connections; + connections.emplace_back(arb::cell_global_label_type{src_gid, "src"}, arb::cell_local_label_type{"tgt"}, weight_, delay_); - // If first ADEX cell, then add - // the connection from the last ADEX cell as well + // If first ADEX cell, then connect from the last ADEX cell, too if (gid == 1) { auto src_gid = n_adex_cells_; - cell_connection conn({src_gid, "src"}, {"tgt"}, weight_, delay_); - connections.push_back(conn); + connections.emplace_back(arb::cell_global_label_type{src_gid, "src"}, arb::cell_local_label_type{"tgt"}, weight_, delay_); } - 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; + arb::util::unique_any get_cell_description(arb::cell_gid_type gid) const override { + // regularly spiking cell; produces a single spike at time 0. + if (gid == 0) return arb::spike_source_cell("src", + arb::explicit_schedule({0.0_ms})); + return arb::adex_cell{"src", "tgt"}; } private: - cell_size_type n_adex_cells_; - float weight_, delay_; + arb::cell_size_type n_adex_cells_; + float weight_; + U::quantity delay_ = 42.0_ms; }; // 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) - {} +struct path_recipe: public arb::recipe { + path_recipe(arb::cell_size_type n, float weight, const U::quantity& delay): + ncells_(n), weight_(weight), delay_(delay) {} - cell_size_type num_cells() const override { - return ncells_; - } + arb::cell_size_type num_cells() const override { return ncells_; } + arb::cell_kind get_cell_kind(arb::cell_gid_type gid) const override { return arb::cell_kind::adex; } + arb::util::unique_any get_cell_description(arb::cell_gid_type gid) const override { return arb::adex_cell("src", "tgt"); } - 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; + std::vector connections_on(arb::cell_gid_type gid) const override { + if (gid == 0) return {}; + return {{{gid-1, "src"}, {"tgt"}, weight_, delay_}}; } private: - cell_size_type ncells_; - float weight_, delay_; + arb::cell_size_type ncells_; + float weight_; + U::quantity delay_; }; // ADEX cell with probe -class probe_recipe: public arb::recipe { -public: - probe_recipe(size_t n_conn = 0): n_conn_{n_conn} {} +struct adex_probe_recipe: public arb::recipe { + adex_probe_recipe(std::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; + arb::cell_size_type num_cells() const override { return 2; } + arb::cell_kind get_cell_kind(arb::cell_gid_type gid) const override { return arb::cell_kind::adex; } + std::vector connections_on(arb::cell_gid_type gid) const override { + return {n_conn_, + {arb::cell_global_label_type{0, "src"}, + arb::cell_local_label_type{"tgt"}, + 0.0, + 0.005_ms}}; } - 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); + arb::util::unique_any get_cell_description(arb::cell_gid_type gid) const override { + auto cell = arb::adex_cell("src", "tgt"); + if (0 == gid) { + cell.E_R = -23.0*U::mV; + cell.V_m = -18.0*U::mV; + cell.E_L = -13.0*U::mV; + cell.t_ref = 0.8*U::ms; + cell.tau = 5*U::ms; } - - 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)}; + return cell; } + std::vector get_probes(arb::cell_gid_type gid) const override { return {{arb::adex_probe_voltage{}, "a"}}; } + std::vector event_generators(arb::cell_gid_type) const override { return {arb::regular_generator({"tgt"}, + 200.0, + 2.0_ms, + 1.0_ms, + 6.0_ms)}; } - size_t n_conn_ = 0; + std::size_t n_conn_ = 0; }; +} // namespace TEST(adex_cell_group, throw) { - probe_recipe rec; - auto context = make_context(); + adex_probe_recipe rec; + auto context = arb::make_context(); auto decomp = partition_load_balance(rec, context); - EXPECT_NO_THROW(simulation(rec, context, decomp)); + EXPECT_NO_THROW(arb::simulation(rec, context, decomp)); } + TEST(adex_cell_group, recipe) { - ring_recipe rr(100, 1, 0.1); + ring_recipe rr(100, 1, 0.1_ms); EXPECT_EQ(101u, rr.num_cells()); - EXPECT_EQ(2u, rr.connections_on(1u).size()); + EXPECT_EQ(0u, rr.connections_on(0u).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); + auto conns_gid_1 = rr.connections_on(1); + EXPECT_EQ(2u, conns_gid_1.size()); + EXPECT_EQ(0u, conns_gid_1[0].source.gid); + EXPECT_EQ(100u, conns_gid_1[1].source.gid); } TEST(adex_cell_group, spikes) { // make two adex cells - path_recipe recipe(2, 1000, 0.1); - - auto context = make_context(); + path_recipe recipe(2, 1000, 0.1_ms); - auto decomp = partition_load_balance(recipe, context); - simulation sim(recipe, context, decomp); - - cse_vector events; + arb::simulation sim(recipe); + arb::cse_vector events; // First event to trigger the spike (first neuron). events.push_back({0, {{0, 1, 1000}}}); @@ -187,9 +157,7 @@ TEST(adex_cell_group, spikes) { sim.inject_events(events); - time_type tfinal = 100; - time_type dt = 0.01; - sim.run(tfinal, dt); + sim.run(100_ms, 0.01_ms); // we expect 4 spikes: 2 by both neurons EXPECT_EQ(4u, sim.num_spikes()); @@ -198,32 +166,28 @@ TEST(adex_cell_group, spikes) { TEST(adex_cell_group, ring) { // Total number of ADEX cells. - cell_size_type num_adex_cells = 99; + arb::cell_size_type num_adex_cells = 99; double weight = 1000; - double delay = 1; - - // Total simulation time. - time_type simulation_time = 100; - + auto delay = 1.0_ms; auto recipe = ring_recipe(num_adex_cells, weight, delay); // Creates a simulation with a ring recipe of adex neurons - simulation sim(recipe); + arb::simulation sim(recipe); - std::vector spike_buffer; + std::vector spike_buffer; sim.set_global_spike_callback( - [&spike_buffer](const std::vector& spikes) { + [&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); + sim.run(100_ms, 0.01_ms); // 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) { + for (auto& spike: spike_buffer) { // Assumes that delay = 1 // We expect that Regular Spiking Cell spiked at time 0s. if (spike.source.gid == 0) { @@ -252,28 +216,27 @@ struct Um_type { }; TEST(adex_cell_group, probe) { - auto ums = std::unordered_map>{}; - auto fun = [&ums](probe_metadata pm, + auto ums = std::unordered_map>{}; + auto fun = [&ums](arb::probe_metadata pm, std::size_t n, - const sample_record* samples) { + const arb::sample_record* samples) { for (std::size_t ix = 0; ix < n; ++ix) { const auto& [t, v] = samples[ix]; - double u = *util::any_cast(v); + EXPECT_NE(arb::util::any_cast(v), nullptr); + double u = *arb::util::any_cast(v); ums[pm.id].push_back({t, u}); } }; - auto rec = probe_recipe{}; - auto sim = simulation(rec); + auto rec = adex_probe_recipe{}; + auto sim = arb::simulation(rec); - sim.add_sampler(all_probes, regular_schedule(0.025), fun); + sim.add_sampler(arb::all_probes, arb::regular_schedule(0.025_ms), 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.set_global_spike_callback([&spikes](const std::vector& spk) { for (const auto& s: spk) spikes.push_back(s.time); }); - sim.run(10, 0.005); + sim.run(10.0_ms, 0.005_ms); std::vector exp = {{ 0, -18 }, { 0.025, -17.9750624 }, { 0.05, -17.9502492 }, @@ -688,28 +651,28 @@ TEST(adex_cell_group, probe) { } TEST(adex_cell_group, probe_with_connections) { - auto ums = std::unordered_map>{}; - auto fun = [&ums](probe_metadata pm, + auto ums = std::unordered_map>{}; + auto fun = [&ums](arb::probe_metadata pm, std::size_t n, - const sample_record* samples) { + const arb::sample_record* samples) { for (std::size_t ix = 0; ix < n; ++ix) { const auto& [t, v] = samples[ix]; - double u = *util::any_cast(v); + double u = *arb::util::any_cast(v); ums[pm.id].push_back({t, u}); } }; - auto rec = probe_recipe{5}; - auto sim = simulation(rec); + auto rec = adex_probe_recipe{5}; + auto sim = arb::simulation(rec); - sim.add_sampler(all_probes, regular_schedule(0.025), fun); + sim.add_sampler(arb::all_probes, arb::regular_schedule(0.025_ms), fun); std::vector spikes; sim.set_global_spike_callback( - [&spikes](const std::vector& spk) { for (const auto& s: spk) spikes.push_back(s.time); } + [&spikes](const std::vector& spk) { for (const auto& s: spk) spikes.push_back(s.time); } ); - sim.run(10, 0.005); + sim.run(10.0_ms, 0.005_ms); std::vector exp = {{ 0, -18 }, { 0.025, -17.9750624 }, { 0.05, -17.9502492 }, From 14b6ff29f7d42d5636a9722e16ec198b2b485341 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Thu, 4 Apr 2024 13:10:18 +0200 Subject: [PATCH 07/13] Clean up Adex cell support, produce example. --- arbor/adex_cell_group.cpp | 79 +- arbor/adex_cell_group.hpp | 32 +- arbor/include/arbor/adex_cell.hpp | 3 +- arbor/include/arbor/units.hpp | 5 +- doc/images/adex.svg | 1089 ++++++++++++++++++++++++++++ example/adex/adex.cpp | 4 +- python/cells.cpp | 4 +- python/example/adex.py | 32 +- test/unit/test_adex_cell_group.cpp | 34 +- 9 files changed, 1204 insertions(+), 78 deletions(-) create mode 100644 doc/images/adex.svg diff --git a/arbor/adex_cell_group.cpp b/arbor/adex_cell_group.cpp index b8ca888673..39100266a8 100644 --- a/arbor/adex_cell_group.cpp +++ b/arbor/adex_cell_group.cpp @@ -20,7 +20,7 @@ adex_cell_group::adex_cell_group(const std::vector& gids, for (auto gid: gids_) { const auto& cell = util::any_cast(rec.get_cell_description(gid)); // set up cell state - cells_.emplace_back(cell); + cells_.push_back(cell); // tell our caller about this cell's connections cg_sources.add_cell(); cg_targets.add_cell(); @@ -45,6 +45,7 @@ adex_cell_group::adex_cell_group(const std::vector& gids, } // set up the internal state next_update_.push_back(0); + current_time_.push_back(0); } } @@ -61,9 +62,13 @@ void adex_cell_group::advance(epoch ep, time_type dt, const event_lane_subrange& PL(); } -const std::vector& adex_cell_group::spikes() const { return spikes_; } +const std::vector& adex_cell_group::spikes() const { + return spikes_; +} -void adex_cell_group::clear_spikes() { spikes_.clear(); } +void adex_cell_group::clear_spikes() { + spikes_.clear(); +} void adex_cell_group::add_sampler(sampler_association_handle h, cell_member_predicate probeset_ids, @@ -91,13 +96,14 @@ void adex_cell_group::remove_all_samplers() { } void adex_cell_group::reset() { - clear_spikes(); + spikes_.clear(); } void adex_cell_group::advance_cell(time_type tfinal, time_type dt, cell_gid_type lid, const event_lane_subrange& event_lanes) { + auto time = current_time_[lid]; auto gid = gids_[lid]; auto& cell = cells_[lid]; auto n_events = static_cast(!event_lanes.empty() ? event_lanes[lid].size() : 0); @@ -117,20 +123,19 @@ void adex_cell_group::advance_cell(time_type tfinal, std::vector sample_data; if (!samplers_.empty()) { - auto tlast = time_; + auto tlast = time; std::vector sample_sizes; std::size_t total_size = 0; { std::lock_guard guard(sampler_mex_); for (auto& [hdl, assoc]: samplers_) { - auto n_probeset_ids = assoc.probeset_ids.size(); // No need to generate events - if (0 == n_probeset_ids) continue; + if (assoc.probeset_ids.empty()) continue; // Construct sampling times, might give us the last time we sampled, so skip that. auto times = util::make_range(assoc.sched.events(tlast, tfinal)); - while (!times.empty() && times.front() <= tlast) times.left++; + // while (!times.empty() && times.front() == tlast) times.left++; if (times.empty()) continue; - for (unsigned idx = 0; idx < n_probeset_ids; ++idx) { + for (unsigned idx = 0; idx < assoc.probeset_ids.size(); ++idx) { const auto& pid = assoc.probeset_ids[idx]; if (pid.gid != gid) continue; const auto& probe = probes_.at(pid); @@ -159,7 +164,6 @@ void adex_cell_group::advance_cell(time_type tfinal, ++rx; } } - arb_assert(rx == total_size); } util::sort_by(sample_events, [](const auto& s) { return s.time; }); auto n_samples = sample_events.size(); @@ -168,7 +172,7 @@ void adex_cell_group::advance_cell(time_type tfinal, // prepare event processing auto e_idx = 0; auto s_idx = 0; - for (; time_ < tfinal; time_ += dt) { + for (; time < tfinal; time += dt) { auto dE = cell.V_m - cell.E_L; auto il = cell.g*dE; auto is = cell.g*cell.delta*exp((cell.V_m - cell.V_th)/cell.delta); @@ -182,23 +186,23 @@ void adex_cell_group::advance_cell(time_type tfinal, // Process events in [time, time + dt) for (;;) { - auto e_t = e_idx < n_events ? event_lanes[lid][e_idx].time : tfinal; - if (e_t < time_) { ++e_idx; continue; } - auto s_t = s_idx < n_samples ? sample_events[s_idx].time : tfinal; - if (s_t < time_) { ++s_idx; continue; } + auto e_t = e_idx < n_events ? event_lanes[lid][e_idx].time : tfinal; + if (e_t < time) { + ++e_idx; + continue; + } + auto s_t = s_idx < n_samples ? sample_events[s_idx].time : tfinal; auto t = std::min(e_t, s_t); - arb_assert(t >= time_); - if (t >= time_ + dt || t >= tfinal) break; + if (t >= time + dt || t >= tfinal) break; if (t == e_t) { weight += event_lanes[lid][e_idx].weight; ++e_idx; } - else { + if (t == s_t) { auto& [time, kind, ptr] = sample_events[s_idx]; - arb_assert(nullptr != ptr); - auto t = (time - time_)/dt; + auto t = (time - time)/dt; if (kind == adex_probe_kind::voltage) { - if (next_update_[lid] > time_) { + if (next_update_[lid] > time) { *ptr = cell.E_R; } else { *ptr = math::lerp(cell.V_m, V_m + weight/cell.C_m, t); @@ -214,23 +218,28 @@ void adex_cell_group::advance_cell(time_type tfinal, ++s_idx; } } - cell.w = w; // if we are still in refractory period, bail now, before we alter membrane voltage - if (next_update_[lid] > time_) continue; - V_m += weight/cell.C_m; - // enter refractory period and emit spike - if (V_m >= cell.V_th) { - // interpolate the spike time and emit event. - // NOTE: _Do_ the interpolation - auto t_spike = time_; - spikes_.emplace_back(cell_member_type{gid, 0}, t_spike); - // reset membrane potential - V_m = cell.E_R; - // schedule next update - next_update_[lid] = time_ + cell.t_ref; - w += cell.b; + if (next_update_[lid] <= time) { + V_m += weight/cell.C_m; + // enter refractory period and emit spike + if (V_m >= cell.V_th) { + // interpolate the spike time and emit event. + // NOTE: _Do_ the interpolation + auto t_spike = time; + spikes_.emplace_back(cell_member_type{gid, 0}, t_spike); + // reset membrane potential + V_m = cell.E_R; + // schedule next update + next_update_[lid] = time + cell.t_ref; + w += cell.b; + } + else { + next_update_[lid] = time + dt; + } } + cell.w = w; cell.V_m = V_m; + current_time_[lid] = time; } auto n_samplers = sample_callbacks.size(); diff --git a/arbor/adex_cell_group.hpp b/arbor/adex_cell_group.hpp index 151cb48228..8229ce7518 100644 --- a/arbor/adex_cell_group.hpp +++ b/arbor/adex_cell_group.hpp @@ -25,21 +25,21 @@ struct ARB_SYMBOL_VISIBLE adex_lowered_cell { cell_tag_type target; // Label of target // Neuronal parameters. - double delta = 2.5; // Steepness parameter [mV] - double V_th = -20; // Firing threshold [mV] - double C_m = 0.28; // Membrane capacitance [pF] - double E_L = -70; // Resting potential [mV] - double E_R = E_L; // Reset potential [mV] - double V_m = E_L; // Initial value of the Membrane potential [mV] - double t_ref = 2.5; // Refractory period [ms] - double g = 0.03; // Leak conductivity [uS] + double delta; // Steepness parameter [mV] + double V_th; // Firing threshold [mV] + double C_m; // Membrane capacitance [pF] + double E_L; // Resting potential [mV] + double E_R; // Reset potential [mV] + double V_m; // Initial value of the Membrane potential [mV] + double t_ref; // Refractory period [ms] + double g; // Leak conductivity [uS] // Adaption parameters - double tau = 144; // Adaption decaying constant [ms] - double w = 0; // Initial value for adaption parameter [nA] - double a = 0.004; // Adaption dynamics [uS]. - double b = 0.08; // When spikes trigger, increase w by this [nA] + double tau; // Adaption decaying constant [ms] + double w; // Initial value for adaption parameter [nA] + double a; // Adaption dynamics [uS]. + double b; // When spikes trigger, increase w by this [nA] - adex_lowered_cell() = default; + adex_lowered_cell() { *this = adex_cell{}; }; adex_lowered_cell(const adex_cell& adex) { source = adex.source; target = adex.target; @@ -88,7 +88,7 @@ struct ARB_ARBOR_API adex_cell_group: public cell_group { std::vector get_probe_metadata(const cell_address_type&) const override; - ARB_SERDES_ENABLE(adex_cell_group, time_, gids_, cells_, spikes_); + ARB_SERDES_ENABLE(adex_cell_group, current_time_, gids_, cells_, spikes_); virtual void t_serialize(serializer& ser, const std::string& k) const override; virtual void t_deserialize(serializer& ser, const std::string& k) override; @@ -107,8 +107,7 @@ struct ARB_ARBOR_API adex_cell_group: public cell_group { // Parameter dt is ignored, since we make jumps between two consecutive spikes. void advance_cell(time_type tfinal, time_type dt, cell_gid_type lid, const event_lane_subrange& event_lane); - // current time - time_type time_ = 0.0; + // List of the gids of the cells in the group. std::vector gids_; @@ -129,6 +128,7 @@ struct ARB_ARBOR_API adex_cell_group: public cell_group { // Time of next possible update to model refractory periods. std::vector next_update_; + std::vector current_time_; }; } // namespace arb diff --git a/arbor/include/arbor/adex_cell.hpp b/arbor/include/arbor/adex_cell.hpp index fc67e57da6..1290c8e9b3 100644 --- a/arbor/include/arbor/adex_cell.hpp +++ b/arbor/include/arbor/adex_cell.hpp @@ -15,7 +15,8 @@ namespace arb { // A. Destehxe 2009 namespace U = arb::units; -using namespace U::literals; +using namespace arb::units::literals; + struct ARB_SYMBOL_VISIBLE adex_cell { cell_tag_type source; // Label of source diff --git a/arbor/include/arbor/units.hpp b/arbor/include/arbor/units.hpp index c361209a56..82d7828d8d 100644 --- a/arbor/include/arbor/units.hpp +++ b/arbor/include/arbor/units.hpp @@ -6,8 +6,6 @@ namespace arb::units { using quantity = ::units::precise_measurement; -// Allow unary minus on quantities. Seemingly doesn't catch literals such as -10_mV -inline quantity operator-(const quantity& q) { return (-1*q); } using unit = ::units::precise_unit; using ::units::to_string; @@ -187,3 +185,6 @@ constexpr inline quantity operator ""_mM(unsigned long long v) { return v*mM; } constexpr inline quantity operator ""_C(unsigned long long v) { return v*C; } } // literals } // units + +// Allow unary minus on quantities. Seemingly doesn't catch literals such as -10_mV +inline arb::units::quantity operator-(const arb::units::quantity& q) { return (-1*q); } diff --git a/doc/images/adex.svg b/doc/images/adex.svg new file mode 100644 index 0000000000..7cdc810587 --- /dev/null +++ b/doc/images/adex.svg @@ -0,0 +1,1089 @@ + + + + + + + + 2024-04-04T13:06:48.819439 + image/svg+xml + + + Matplotlib v3.8.3, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/adex/adex.cpp b/example/adex/adex.cpp index 179eddf499..f487e82fce 100644 --- a/example/adex/adex.cpp +++ b/example/adex/adex.cpp @@ -77,8 +77,8 @@ struct recipe: public arb::recipe { arb::util::unique_any get_cell_description(arb::cell_gid_type) const override { auto cell = arb::adex_cell{"src", "tgt"}; - cell.V_m = -1*80_mV; - cell.E_R = -1*90_mV; + cell.V_m = -1*80.0_mV; + cell.E_R = -1*90.0_mV; return cell; } diff --git a/python/cells.cpp b/python/cells.cpp index cb20d83db9..415a6ec4a4 100644 --- a/python/cells.cpp +++ b/python/cells.cpp @@ -324,9 +324,9 @@ void register_cells(py::module& m) { "Adaption decay time [ms].") .def_readwrite("w", &arb::adex_cell::tau, "Adaption variable [nA].") - .def_readwrite("w", &arb::adex_cell::b, + .def_readwrite("b", &arb::adex_cell::b, "Adaption variable increase on spike [nA].") - .def_readwrite("w", &arb::adex_cell::b, + .def_readwrite("a", &arb::adex_cell::a, "Adaption variable dynamics [uS].") .def_readwrite("V_th", &arb::adex_cell::V_th, "Firing threshold [mV].") diff --git a/python/example/adex.py b/python/example/adex.py index d8de79165e..19273f5afb 100644 --- a/python/example/adex.py +++ b/python/example/adex.py @@ -1,11 +1,13 @@ #!/usr/bin/env python3 import arbor as A -import pandas as pd +from arbor import units as U +import numpy as np +import matplotlib.pylab as plt import seaborn as sns -T = 100 -dt = 0.05 +T = 100 * U.ms +dt = 0.05 * U.ms class recipe(A.recipe): @@ -21,10 +23,11 @@ def cell_kind(self, _): def cell_description(self, _): cell = A.adex_cell("src", "tgt") + cell.b = 0.1 * U.nA return cell def event_generators(self, _): - return [A.event_generator("tgt", self.weight, A.regular_schedule(8))] + return [A.event_generator("tgt", self.weight, A.regular_schedule(8 * U.ms))] def probes(self, _): return [A.adex_probe_voltage("Um"), A.adex_probe_adaption("w")] @@ -37,12 +40,27 @@ def probes(self, _): hUm = sim.sample((0, "Um"), sch) hw = sim.sample((0, "w"), sch) +sim.record(A.spike_recording.all) + sim.run(T, dt) -df_list = [] +spikes = sim.spikes() Um, _ = sim.samples(hUm)[0] W, _ = sim.samples(hw)[0] -df = pd.DataFrame({"t/ms": Um[:, 0], "U/mV": Um[:, 1], "W/nA": W[:, 1]}) -sns.relplot(data=df, x="t/ms", y="U/mV", kind="line").savefig("adex_results.pdf") +colors = sns.color_palette() + +fg, ax = plt.subplots() +V_th = rec.cell_description(0).V_th.value_as(U.mV) +ax.plot(Um[:, 0], Um[:, 1], label='$U_m/mV$', color=colors[0]) +ax.scatter(spikes['time'], V_th*np.ones_like(spikes['time']), color=colors[0]) +ax.set_ylabel('$U_m/mV$', color=colors[0]) +ax.tick_params(axis='y', labelcolor=colors[0]) +ax.set_xlabel('$t/ms$') +ax.set_xlim(0, T.value) +ax = ax.twinx() +ax.set_ylabel('$W/nA$', color=colors[2]) +ax.plot(W[:, 0], W[:, 1], label='$w/nA$', color=colors[2]) +ax.tick_params(axis='y', labelcolor=colors[2]) +fg.savefig("adex_results.pdf") diff --git a/test/unit/test_adex_cell_group.cpp b/test/unit/test_adex_cell_group.cpp index 3bf1b63c13..3e485a1850 100644 --- a/test/unit/test_adex_cell_group.cpp +++ b/test/unit/test_adex_cell_group.cpp @@ -97,21 +97,28 @@ struct adex_probe_recipe: public arb::recipe { } arb::util::unique_any get_cell_description(arb::cell_gid_type gid) const override { auto cell = arb::adex_cell("src", "tgt"); + cell.V_th = 10_mV; if (0 == gid) { - cell.E_R = -23.0*U::mV; - cell.V_m = -18.0*U::mV; - cell.E_L = -13.0*U::mV; - cell.t_ref = 0.8*U::ms; + cell.E_R = -1*23.0_mV; + cell.V_m = -1*18.0_mV; + cell.E_L = -1*13.0_mV; + cell.t_ref = 0.8_ms; cell.tau = 5*U::ms; } return cell; } - std::vector get_probes(arb::cell_gid_type gid) const override { return {{arb::adex_probe_voltage{}, "a"}}; } - std::vector event_generators(arb::cell_gid_type) const override { return {arb::regular_generator({"tgt"}, - 200.0, - 2.0_ms, - 1.0_ms, - 6.0_ms)}; } + std::vector get_probes(arb::cell_gid_type gid) const override { + return {{arb::adex_probe_voltage{}, "a"}}; + } + std::vector event_generators(arb::cell_gid_type) const override { + return { + arb::regular_generator({"tgt"}, + 200.0, + 2.0_ms, + 1.0_ms, + 6.0_ms) + }; + } std::size_t n_conn_ = 0; }; @@ -234,7 +241,9 @@ TEST(adex_cell_group, probe) { std::vector spikes; - sim.set_global_spike_callback([&spikes](const std::vector& spk) { for (const auto& s: spk) spikes.push_back(s.time); }); + sim.set_global_spike_callback([&spikes](const std::vector& spk) { + for (const auto& s: spk) spikes.push_back(s.time); + }); sim.run(10.0_ms, 0.005_ms); std::vector exp = {{ 0, -18 }, @@ -639,7 +648,6 @@ TEST(adex_cell_group, probe) { { 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)); @@ -1074,8 +1082,8 @@ TEST(adex_cell_group, probe_with_connections) { { 9.95, -17.3604929 }, { 9.975, -17.3387448 },}; + EXPECT_EQ((ums[{0, "a"}].size()), exp.size()); 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)); From fd3e8075131da066c7b5ad1801f10a4b1806f18d Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Thu, 4 Apr 2024 13:14:50 +0200 Subject: [PATCH 08/13] Lint, docs, and stubs. --- doc/index.rst | 1 + python/example/adex.py | 16 +- python/stubs/arbor/__init__.pyi | 30 +--- python/stubs/arbor/_arbor/__init__.pyi | 228 +++++++++---------------- 4 files changed, 97 insertions(+), 178 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index a7d623b98c..64d572974e 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -163,6 +163,7 @@ A full list of our software attributions can be found `here MechCatValueIterator: ... def __next__(self) -> mechanism_info: ... -class asc_color: +class asc_morphology: """ - Neurolucida color tag. + The morphology and label dictionary meta-data loaded from a Neurolucida ASCII (.asc) file. """ @property - def blue(self) -> int: ... - @property - def green(self) -> int: ... - @property - def red(self) -> int: ... - -class asc_marker: - """ - Neurolucida marker type. - - Members: - - dot - - cross - - circle - - none - """ - - __members__: typing.ClassVar[ - dict[str, asc_marker] - ] # value = {'dot': , 'cross': , 'circle': , 'none': } - circle: typing.ClassVar[asc_marker] # value = - cross: typing.ClassVar[asc_marker] # value = - dot: typing.ClassVar[asc_marker] # value = - none: typing.ClassVar[asc_marker] # value = - def __eq__(self, other: typing.Any) -> bool: ... - def __getstate__(self) -> int: ... - def __hash__(self) -> int: ... - def __index__(self) -> int: ... - def __init__(self, value: int) -> None: ... - def __int__(self) -> int: ... - def __ne__(self, other: typing.Any) -> bool: ... - def __repr__(self) -> str: ... - def __setstate__(self, state: int) -> None: ... - def __str__(self) -> str: ... - @property - def name(self) -> str: ... - @property - def value(self) -> int: ... - -class asc_marker_set: - """ - Neurolucida marker set type. - """ - - @property - def color(self) -> asc_color: ... - @property - def locations(self) -> list[mpoint]: ... - @property - def marker(self) -> asc_marker: ... - @property - def name(self) -> str: ... - -class asc_metadata: - """ - Neurolucida metadata type: Spines and marker sets. - """ + def labels(self) -> label_dict: + """ + The four canonical regions are labeled 'soma', 'axon', 'dend' and 'apic'. + """ @property - def markers(self) -> list[asc_marker_set]: ... - @property - def spines(self) -> list[asc_spine]: ... - -class asc_spine: - """ - Neurolucida spine marker. - """ + def morphology(self) -> morphology: + """ + The cable cell morphology. + """ @property - def location(self) -> mpoint: ... - @property - def name(self) -> str: ... + def segment_tree(self) -> segment_tree: + """ + The raw segment tree. + """ class axial_resistivity: """ @@ -265,9 +199,9 @@ class backend: __members__: typing.ClassVar[ dict[str, backend] - ] # value = {'gpu': , 'multicore': } - gpu: typing.ClassVar[backend] # value = - multicore: typing.ClassVar[backend] # value = + ] # value = {'gpu': , 'multicore': } + gpu: typing.ClassVar[backend] # value = + multicore: typing.ClassVar[backend] # value = def __eq__(self, other: typing.Any) -> bool: ... def __getstate__(self) -> int: ... def __hash__(self) -> int: ... @@ -653,21 +587,13 @@ class cell_global_label: """ @typing.overload - def __init__(self, arg0: tuple[int, cell_local_label]) -> None: + def __init__(self, arg0: tuple) -> None: """ Construct a cell_global_label identifier with tuple argument (gid, label): gid: The global identifier of the cell. label: The cell_local_label representing the label and selection policy of an item on the cell. """ - @typing.overload - def __init__(self, arg0: tuple[int, str]) -> None: - """ - Construct a cell_global_label identifier with tuple argument (gid, label): - gid: The global identifier of the cell. - label: The tag of an item on the cell. - """ - def __repr__(self) -> str: ... def __str__(self) -> str: ... @property @@ -749,15 +675,7 @@ class cell_local_label: """ @typing.overload - def __init__(self, arg0: tuple[str, selection_policy]) -> None: - """ - Construct a cell_local_label identifier with tuple argument (label, policy): - label: The identifier of a group of one or more items on a cell. - policy: The policy for selecting one of possibly multiple items associated with the label. - """ - - @typing.overload - def __init__(self, arg0: tuple[str, selection_policy]) -> None: + def __init__(self, arg0: tuple) -> None: """ Construct a cell_local_label identifier with tuple argument (label, policy): label: The identifier of a group of one or more items on a cell. @@ -1637,7 +1555,7 @@ class label_dict: """ @staticmethod - def append(*args, **kwargs) -> label_dict: + def append(*args, **kwargs) -> None: """ Import the entries of a another label dictionary with an optional prefix. """ @@ -1684,7 +1602,7 @@ class label_dict: def items(self) -> typing.Iterator: ... def keys(self) -> typing.Iterator: ... - def update(self, other: label_dict) -> label_dict: + def update(self, other: label_dict) -> None: """ The label_dict to be importedImport the entries of a another label dictionary. """ @@ -1809,35 +1727,6 @@ class lif_probe_metadata: Probe metadata associated with a LIF cell probe. """ -class loaded_morphology: - """ - The morphology and label dictionary meta-data loaded from file. - """ - - @property - def labels(self) -> label_dict: - """ - Any labels defined by the loaded file. - """ - - @property - def metadata(self) -> swc_metadata | asc_metadata | nml_metadata: - """ - File type specific metadata. - """ - - @property - def morphology(self) -> morphology: - """ - The cable cell morphology. - """ - - @property - def segment_tree(self) -> segment_tree: - """ - The raw segment tree. - """ - class location: """ A location on a cable cell. @@ -2046,6 +1935,40 @@ class morphology: A cell morphology. """ + def __init__(self, arg0: segment_tree) -> None: ... + def __str__(self) -> str: ... + def branch_children(self, i: int) -> list[int]: + """ + The child branches of branch i. + """ + + def branch_parent(self, i: int) -> int: + """ + The parent branch of branch i. + """ + + def branch_segments(self, i: int) -> list[msegment]: + """ + A list of the segments in branch i, ordered from proximal to distal ends of the branch. + """ + + def to_segment_tree(self) -> segment_tree: + """ + Convert this morphology to a segment_tree. + """ + + @property + def empty(self) -> bool: + """ + Whether the morphology is empty. + """ + + @property + def num_branches(self) -> int: + """ + The number of branches in the morphology. + """ + class morphology_provider: def __init__(self, morphology: morphology) -> None: """ @@ -2072,7 +1995,7 @@ class mpoint: """ @typing.overload - def __init__(self, arg0: tuple[float, float, float, float]) -> None: + def __init__(self, arg0: tuple) -> None: """ Create an mpoint object from a tuple (x, y, z, radius), specified in µm. """ @@ -2135,14 +2058,14 @@ class neuroml: def cell_morphology( self, cell_id: str, allow_spherical_root: bool = False - ) -> loaded_morphology | None: + ) -> neuroml_morph_data | None: """ Retrieve nml_morph_data associated with cell_id. """ def morphology( self, morph_id: str, allow_spherical_root: bool = False - ) -> loaded_morphology | None: + ) -> neuroml_morph_data | None: """ Retrieve top-level nml_morph_data associated with morph_id. """ @@ -2152,7 +2075,7 @@ class neuroml: Query top-level standalone morphologies. """ -class nml_metadata: +class neuroml_morph_data: def groups(self) -> label_dict: """ Label dictionary containing one region expression for each segmentGroup id. @@ -2186,6 +2109,12 @@ class nml_metadata: Morphology id. """ + @property + def morphology(self) -> morphology: + """ + Morphology constructed from a signle NeuroML element. + """ + class partition_hint: """ Provide a hint on how the cell groups should be partitioned. @@ -2961,11 +2890,6 @@ class spike_source_cell: def __repr__(self) -> str: ... def __str__(self) -> str: ... -class swc_metadata: - """ - SWC metadata type: empty. - """ - class synapse: """ For placing a synaptic mechanism on a locset. @@ -3220,9 +3144,11 @@ def lif_probe_voltage(tag: str) -> probe: Probe specification for LIF cell membrane voltage. """ -def load_asc(filename_or_stream: typing.Any) -> loaded_morphology: +def load_asc( + filename_or_stream: typing.Any, raw: bool = False +) -> segment_tree | asc_morphology: """ - Load a morphology or segment_tree and meta data from a Neurolucida ASCII .asc file. + Load a morphology or segment_tree (raw=True) and meta data from a Neurolucida ASCII .asc file. """ def load_catalogue(arg0: typing.Any) -> catalogue: ... @@ -3231,9 +3157,11 @@ def load_component(filename_or_descriptor: typing.Any) -> cable_component: Load arbor-component (decor, morphology, label_dict, cable_cell) from file. """ -def load_swc_arbor(filename_or_stream: typing.Any) -> loaded_morphology: +def load_swc_arbor( + filename_or_stream: typing.Any, raw: bool = False +) -> segment_tree | morphology: """ - Generate a morphology/segment_tree from an SWC file following the rules prescribed by Arbor. + Generate a morphology/segment_tree (raw=False/True) from an SWC file following the rules prescribed by Arbor. Specifically: * Single-segment somas are disallowed. * There are no special rules related to somata. They can be one or multiple branches @@ -3242,9 +3170,11 @@ def load_swc_arbor(filename_or_stream: typing.Any) -> loaded_morphology: are no gaps in the resulting morphology. """ -def load_swc_neuron(filename_or_stream: typing.Any) -> loaded_morphology: +def load_swc_neuron( + filename_or_stream: typing.Any, raw: bool = False +) -> segment_tree | morphology: """ - Generate a morphology from an SWC file following the rules prescribed by NEURON. + Generate a morphology/segment_tree (raw=False/True) from an SWC file following the rules prescribed by NEURON. See the documentation https://docs.arbor-sim.org/en/latest/fileformat/swc.html for a detailed description of the interpretation. """ From 5c42c01494f79c3bbd20b60d9a51434f0b483a83 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Thu, 4 Apr 2024 13:20:10 +0200 Subject: [PATCH 09/13] Fix LaTex in adex docs --- doc/concepts/adex_cell.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/concepts/adex_cell.rst b/doc/concepts/adex_cell.rst index 6c00dbc116..d129f79614 100644 --- a/doc/concepts/adex_cell.rst +++ b/doc/concepts/adex_cell.rst @@ -18,7 +18,7 @@ See for example wOutside the refractory period the dynamics are .. math:: - C_m\partial_t V_m = -g(V_m - E_L) + g \Delta \exp\left(\frac{V_m - V_\mathrm{th}{\Delta}\right) - w + I\\ + C_m\partial_t V_m = -g(V_m - E_L) + g \Delta \exp\left(\frac{V_m - V_\mathrm{th}}{\Delta}\right) - w + I\\ \partial_t w = a(V_m - E_L) - w + f with the following terms and their default/starting values @@ -44,7 +44,7 @@ Incoming spikes give rise to a instantaneous jump in the membrane potential functional. Every time :math:`t_s` the cell emits a spike, :math:`w` is incremented by :math:`b`; thus -:math:`f(t) = b\delta(t-t_s)` and the refractory begins, for a duration of +:math:`f(t) = b\delta(t-t_s)` and the refractory period begins, for a duration of :math:`t_\mathrm{ref}`. During that time .. math:: From 08a9b34b4091e716e9d430b82d54d361b0d8ca52 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Thu, 4 Apr 2024 13:22:10 +0200 Subject: [PATCH 10/13] Polish adex docs --- doc/concepts/adex_cell.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/concepts/adex_cell.rst b/doc/concepts/adex_cell.rst index d129f79614..f978c0e383 100644 --- a/doc/concepts/adex_cell.rst +++ b/doc/concepts/adex_cell.rst @@ -30,14 +30,14 @@ with the following terms and their default/starting values * Membrane capacitance :math:`C_\mathrm{m} = 0.28\,nF` * Firing threshold :math:`V_\mathrm{th} = -20\,mV` * Refractory period :math:`t_\mathrm{ref} = 2.5\,ms` -* Leak conductivity :math:`g = 0.03\,mu S` +* Leak conductivity :math:`g = 0.03\,\mu S` and * adaptivity parameter :math:`w = 0\,nA` * decay time :math:`\tau = 144\,ms` -* :math:`a = 0.004\,\mu S` -* :math:`b = 0.08\,nA` +* dynamics :math:`a = 0.004\,\mu S` +* spike response :math:`b = 0.08\,nA` Incoming spikes give rise to a instantaneous jump in the membrane potential :math:`V_m \rightarrow V_m + \frac{\omega}{C_m}`, i.e. :math:`I` is a delta From 4c0ea6e66b9e58e2bcf236462bcb4618076ed666 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Thu, 4 Apr 2024 13:26:17 +0200 Subject: [PATCH 11/13] Typo removed. --- doc/concepts/adex_cell.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/concepts/adex_cell.rst b/doc/concepts/adex_cell.rst index f978c0e383..8432fb5cfb 100644 --- a/doc/concepts/adex_cell.rst +++ b/doc/concepts/adex_cell.rst @@ -15,7 +15,7 @@ See for example A. Destexhe 2009 -wOutside the refractory period the dynamics are +Outside the refractory period the dynamics are .. math:: C_m\partial_t V_m = -g(V_m - E_L) + g \Delta \exp\left(\frac{V_m - V_\mathrm{th}}{\Delta}\right) - w + I\\ From c1010050b446730b8980baa598237aca0bc9841e Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Fri, 5 Apr 2024 10:07:18 +0200 Subject: [PATCH 12/13] Remove inject events, subsumed by generators. --- test/unit/test_adex_cell_group.cpp | 28 +++++++++++----------------- test/unit/test_lif_cell_group.cpp | 8 +------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/test/unit/test_adex_cell_group.cpp b/test/unit/test_adex_cell_group.cpp index 3e485a1850..7433a7f889 100644 --- a/test/unit/test_adex_cell_group.cpp +++ b/test/unit/test_adex_cell_group.cpp @@ -76,6 +76,17 @@ struct path_recipe: public arb::recipe { return {{{gid-1, "src"}, {"tgt"}, weight_, delay_}}; } + std::vector event_generators(arb::cell_gid_type gid) const override { + if (gid != 0) return {}; + return {arb::explicit_generator_from_milliseconds({"tgt"}, + 1000.0, // use a large weight to trigger spikes + std::vector{ 1.0, // First event to trigger the spike + 1.1, // inside refractory period; should be ignored + 50.0 // long after previous event; should trigger new spike + })}; + } + + private: arb::cell_size_type ncells_; float weight_; @@ -147,25 +158,8 @@ TEST(adex_cell_group, recipe) TEST(adex_cell_group, spikes) { // make two adex cells path_recipe recipe(2, 1000, 0.1_ms); - arb::simulation sim(recipe); - arb::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); - sim.run(100_ms, 0.01_ms); - // we expect 4 spikes: 2 by both neurons EXPECT_EQ(4u, sim.num_spikes()); } diff --git a/test/unit/test_lif_cell_group.cpp b/test/unit/test_lif_cell_group.cpp index 72d79d858a..c0a46854f6 100644 --- a/test/unit/test_lif_cell_group.cpp +++ b/test/unit/test_lif_cell_group.cpp @@ -181,14 +181,8 @@ TEST(lif_cell_group, recipe) TEST(lif_cell_group, spikes) { // make two lif cells path_recipe recipe(2, 1000, 0.1); - - auto context = make_context(); - - auto decomp = partition_load_balance(recipe, context); - simulation sim(recipe, context, decomp); - + simulation sim(recipe); sim.run(100*U::ms, 0.01*U::ms); - // we expect 4 spikes: 2 by both neurons EXPECT_EQ(4u, sim.num_spikes()); } From aa8d426e5b9afc9e5432288b89baaf76533847ab Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:57:22 +0200 Subject: [PATCH 13/13] Re-work overly complicated adex sampling. --- arbor/adex_cell_group.cpp | 158 +-- arbor/include/arbor/math.hpp | 1 - doc/concepts/adex_cell.rst | 2 +- test/unit/test_adex_cell_group.cpp | 1491 ++++++++++++++-------------- 4 files changed, 831 insertions(+), 821 deletions(-) diff --git a/arbor/adex_cell_group.cpp b/arbor/adex_cell_group.cpp index 39100266a8..3c730b6bd4 100644 --- a/arbor/adex_cell_group.cpp +++ b/arbor/adex_cell_group.cpp @@ -8,6 +8,8 @@ #include "label_resolution.hpp" #include "profile/profiler_macro.hpp" +#include + using namespace arb; // Constructor containing gid of first cell in a group and a container of all cells. @@ -99,15 +101,50 @@ void adex_cell_group::reset() { spikes_.clear(); } -void adex_cell_group::advance_cell(time_type tfinal, +// integrate a single cell's state from current time `cur` to final time `end`. +// Extra parameters +// * the cell cannot be updated until time `nxt`, which might be in the past or future. +// +// We can be in three states: +// 1. nxt <= cur: we can simply update the cell without further consideration +// 2. cur < nxt <= end: we perform two steps: +// a. cur - nxt: refractory period, just manipulate w +// b. nxt - end: normal dynamics, add spike +// 3. nxt > end. Skip everything +void integrate_until(adex_lowered_cell& cell, const time_type end, const time_type& nxt, time_type& cur) { + // perform pre-step to skip refractory period. This _might_ put cell state beyond the epoch end. + if (nxt > cur) cur = std::min(nxt, end); + // if we still have time left, perform the integration. + if (nxt > end) return; + auto delta = end - cur; + auto dE = cell.V_m - cell.E_L; + auto il = cell.g*dE; + auto is = cell.g*cell.delta*exp((cell.V_m - cell.V_th)/cell.delta); + auto dV = (is - il - cell.w)/cell.C_m; + cell.V_m += delta*dV; + + auto dW = (cell.a*dE - cell.w)/cell.tau; + cell.w += delta*dW; + cur = end; +} + +void check_spike(adex_lowered_cell& cell, const time_type time, time_type& nxt, const cell_gid_type gid, std::vector& spikes) { + if (time > nxt && cell.V_m >= cell.V_th) { + spikes.emplace_back(cell_member_type{gid, 0}, time); + // reset membrane potential + cell.V_m = cell.E_R; + // schedule next update + nxt = time + cell.t_ref; + cell.w += cell.b; + } +} + +void adex_cell_group::advance_cell(time_type t_fin, time_type dt, cell_gid_type lid, const event_lane_subrange& event_lanes) { auto time = current_time_[lid]; auto gid = gids_[lid]; - auto& cell = cells_[lid]; - auto n_events = static_cast(!event_lanes.empty() ? event_lanes[lid].size() : 0); - // Flattened sampler map std::vector sample_metadata; std::vector sample_callbacks; @@ -132,7 +169,7 @@ void adex_cell_group::advance_cell(time_type tfinal, // No need to generate events if (assoc.probeset_ids.empty()) continue; // Construct sampling times, might give us the last time we sampled, so skip that. - auto times = util::make_range(assoc.sched.events(tlast, tfinal)); + auto times = util::make_range(assoc.sched.events(tlast, t_fin)); // while (!times.empty() && times.front() == tlast) times.left++; if (times.empty()) continue; for (unsigned idx = 0; idx < assoc.probeset_ids.size(); ++idx) { @@ -156,7 +193,7 @@ void adex_cell_group::advance_cell(time_type tfinal, // NOTE: Need to allocate in one go, else reallocation will mess up the pointers! sample_data.resize(total_size); auto rx = 0; - for (int ix = 0; ix < sample_sizes.size(); ++ix) { + for (unsigned ix = 0; ix < sample_sizes.size(); ++ix) { auto size = sample_sizes[ix]; for (int kx = 0; kx < size; ++kx) { sample_records[ix][kx].data = const_cast(sample_data.data() + rx); @@ -168,80 +205,51 @@ void adex_cell_group::advance_cell(time_type tfinal, util::sort_by(sample_events, [](const auto& s) { return s.time; }); auto n_samples = sample_events.size(); - // integrate until tfinal using the exact solution of membrane voltage differential equation. - // prepare event processing - auto e_idx = 0; - auto s_idx = 0; - for (; time < tfinal; time += dt) { - auto dE = cell.V_m - cell.E_L; - auto il = cell.g*dE; - auto is = cell.g*cell.delta*exp((cell.V_m - cell.V_th)/cell.delta); - auto dV = (is - il - cell.w)/cell.C_m; - auto V_m = cell.V_m + dt*dV; - - auto dW = (cell.a*dE - cell.w)/cell.tau; - auto w = cell.w + dt*dW; - - auto weight = 0.0; - + auto& cell = cells_[lid]; + auto n_events = static_cast(!event_lanes.empty() ? event_lanes[lid].size() : 0); + auto evt_idx = 0; + auto spl_idx = 0; + while (time < t_fin) { + auto t_end = std::min(t_fin, time + dt); + // forward progress? + arb_assert(t_end > time); + auto V_0 = cell.V_m; + auto W_0 = cell.w; // Process events in [time, time + dt) - for (;;) { - auto e_t = e_idx < n_events ? event_lanes[lid][e_idx].time : tfinal; - if (e_t < time) { - ++e_idx; - continue; - } - auto s_t = s_idx < n_samples ? sample_events[s_idx].time : tfinal; - auto t = std::min(e_t, s_t); - if (t >= time + dt || t >= tfinal) break; - if (t == e_t) { - weight += event_lanes[lid][e_idx].weight; - ++e_idx; - } - if (t == s_t) { - auto& [time, kind, ptr] = sample_events[s_idx]; - auto t = (time - time)/dt; - if (kind == adex_probe_kind::voltage) { - if (next_update_[lid] > time) { - *ptr = cell.E_R; - } else { - *ptr = math::lerp(cell.V_m, V_m + weight/cell.C_m, t); - } - } - else if (kind == adex_probe_kind::adaption) { - *ptr = math::lerp(cell.w, w, t); - } - else { - // impossible! - throw arbor_internal_error{"Unknown ADEX probe."}; - } - ++s_idx; - } + // delivering each at the exact time + for (;; ++evt_idx) { + if (evt_idx >= n_events) break; + if (event_lanes[lid][evt_idx].time >= t_end) break; + + const auto& evt = event_lanes[lid][evt_idx]; + integrate_until(cell, evt.time, next_update_[lid], current_time_[lid]); + // NOTE we _could check here instead or in addition. + // check_spike(cell, evt.time, next_update_[lid], gid, spikes_); + if (next_update_[lid] <= evt.time) cell.V_m += evt.weight/cell.C_m; + check_spike(cell, evt.time, next_update_[lid], gid, spikes_); } - // if we are still in refractory period, bail now, before we alter membrane voltage - if (next_update_[lid] <= time) { - V_m += weight/cell.C_m; - // enter refractory period and emit spike - if (V_m >= cell.V_th) { - // interpolate the spike time and emit event. - // NOTE: _Do_ the interpolation - auto t_spike = time; - spikes_.emplace_back(cell_member_type{gid, 0}, t_spike); - // reset membrane potential - V_m = cell.E_R; - // schedule next update - next_update_[lid] = time + cell.t_ref; - w += cell.b; - } - else { - next_update_[lid] = time + dt; - } + // if there's time left before t_end, integrate until that + integrate_until(cell, t_end, next_update_[lid], current_time_[lid]); + check_spike(cell, t_end, next_update_[lid], gid, spikes_); + + // now process the sampling events + for (;; ++spl_idx) { + if (spl_idx >= n_samples) break; + const auto& evt = sample_events[spl_idx]; + if (evt.time > t_end) break; + // interpolation paramter + auto t = (evt.time - time)/dt; + if (evt.kind == adex_probe_kind::voltage) *evt.data = math::lerp(V_0, cell.V_m, t); + if (evt.kind == adex_probe_kind::adaption) *evt.data = math::lerp(W_0, cell.w, t); } - cell.w = w; - cell.V_m = V_m; - current_time_[lid] = time; + + time = t_end; } + arb_assert(time == t_fin); + arb_assert(evt_idx == n_events); + arb_assert(spl_idx == n_samples); + auto n_samplers = sample_callbacks.size(); { std::lock_guard guard{sampler_mex_}; diff --git a/arbor/include/arbor/math.hpp b/arbor/include/arbor/math.hpp index d9483f3501..0b7c52ca90 100644 --- a/arbor/include/arbor/math.hpp +++ b/arbor/include/arbor/math.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include diff --git a/doc/concepts/adex_cell.rst b/doc/concepts/adex_cell.rst index 8432fb5cfb..ef1fcb36ad 100644 --- a/doc/concepts/adex_cell.rst +++ b/doc/concepts/adex_cell.rst @@ -49,7 +49,7 @@ Every time :math:`t_s` the cell emits a spike, :math:`w` is incremented by :math .. math:: V_m = E_R\\ - \partial_t w = a(V_m - E_L) - w + \partial_t w = 0 and all incoming spikes are discarded without effect. diff --git a/test/unit/test_adex_cell_group.cpp b/test/unit/test_adex_cell_group.cpp index 7433a7f889..c9478f5dee 100644 --- a/test/unit/test_adex_cell_group.cpp +++ b/test/unit/test_adex_cell_group.cpp @@ -233,214 +233,214 @@ TEST(adex_cell_group, probe) { sim.add_sampler(arb::all_probes, arb::regular_schedule(0.025_ms), fun); - std::vector spikes; + std::vector spikes; sim.set_global_spike_callback([&spikes](const std::vector& spk) { - for (const auto& s: spk) spikes.push_back(s.time); + for (const auto& s: spk) spikes.push_back(s); }); sim.run(10.0_ms, 0.005_ms); 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 }, + { 0.025, -17.9866178 }, + { 0.05, -17.9732626 }, + { 0.075, -17.9599343 }, + { 0.1, -17.946633 }, + { 0.125, -17.9333585 }, + { 0.15, -17.920111 }, + { 0.175, -17.9068904 }, + { 0.2, -17.8936968 }, + { 0.225, -17.88053 }, + { 0.25, -17.8673901 }, + { 0.275, -17.8542771 }, + { 0.3, -17.841191 }, + { 0.325, -17.8281318 }, + { 0.35, -17.8150995 }, + { 0.375, -17.802094 }, + { 0.4, -17.7891153 }, + { 0.425, -17.7761635 }, + { 0.45, -17.7632386 }, + { 0.475, -17.7503404 }, + { 0.5, -17.7374691 }, + { 0.525, -17.7246246 }, + { 0.55, -17.7118069 }, + { 0.575, -17.6990159 }, + { 0.6, -17.6862517 }, + { 0.625, -17.6735143 }, + { 0.65, -17.6608036 }, + { 0.675, -17.6481197 }, + { 0.7, -17.6354625 }, + { 0.725, -17.622832 }, + { 0.75, -17.6102282 }, + { 0.775, -17.597651 }, + { 0.8, -17.5851005 }, + { 0.825, -17.5725767 }, + { 0.85, -17.5600796 }, + { 0.875, -17.547609 }, + { 0.9, -17.5351651 }, + { 0.925, -17.5227477 }, + { 0.95, -17.5103569 }, + { 0.975, -17.4979927 }, + { 1, -17.4856551 }, + { 1.025, -17.4733439 }, + { 1.05, -17.4610593 }, + { 1.075, -17.4488012 }, + { 1.1, -17.4365696 }, + { 1.125, -17.4243644 }, + { 1.15, -17.4121856 }, + { 1.175, -17.4000333 }, + { 1.2, -17.3879074 }, + { 1.225, -17.3758079 }, + { 1.25, -17.3637347 }, + { 1.275, -17.3516879 }, + { 1.3, -17.3396675 }, + { 1.325, -17.3276733 }, + { 1.35, -17.3157055 }, + { 1.375, -17.3037639 }, + { 1.4, -17.2918485 }, + { 1.425, -17.2799594 }, + { 1.45, -17.2680965 }, + { 1.475, -17.2562598 }, + { 1.5, -17.2444492 }, + { 1.525, -17.2326649 }, + { 1.55, -17.2209066 }, + { 1.575, -17.2091744 }, + { 1.6, -17.1974683 }, + { 1.625, -17.1857883 }, + { 1.65, -17.1741343 }, + { 1.675, -17.1625063 }, + { 1.7, -17.1509043 }, + { 1.725, -17.1393283 }, + { 1.75, -17.1277782 }, + { 1.775, -17.116254 }, + { 1.8, -17.1047557 }, + { 1.825, -17.0932833 }, + { 1.85, -17.0818368 }, + { 1.875, -17.070416 }, + { 1.9, -17.0590211 }, + { 1.925, -17.0476519 }, + { 1.95, -17.0363085 }, + { 1.975, -17.0249908 }, + { 2, -17.0136988 }, + { 2.025, -23 }, + { 2.05, -23 }, + { 2.075, -23 }, + { 2.1, -23 }, + { 2.125, -23 }, + { 2.15, -23 }, + { 2.175, -23 }, + { 2.2, -23 }, + { 2.225, -23 }, + { 2.25, -23 }, + { 2.275, -23 }, + { 2.3, -23 }, + { 2.325, -23 }, + { 2.35, -23 }, + { 2.375, -23 }, + { 2.4, -23 }, + { 2.425, -23 }, + { 2.45, -23 }, + { 2.475, -23 }, + { 2.5, -23 }, + { 2.525, -23 }, + { 2.55, -23 }, + { 2.575, -23 }, + { 2.6, -23 }, + { 2.625, -23 }, + { 2.65, -23 }, + { 2.675, -23 }, + { 2.7, -23 }, + { 2.725, -23 }, + { 2.75, -23 }, + { 2.775, -23 }, + { 2.8, -23 }, + { 2.825, -22.9798329 }, + { 2.85, -22.9596691 }, + { 2.875, -22.9395089 }, + { 2.9, -22.9193525 }, + { 2.925, -22.8992003 }, + { 2.95, -22.8790525 }, + { 2.975, -22.8589094 }, + { 3, -22.8387712 }, + { 3.025, -23 }, + { 3.05, -23 }, + { 3.075, -23 }, + { 3.1, -23 }, + { 3.125, -23 }, + { 3.15, -23 }, + { 3.175, -23 }, + { 3.2, -23 }, + { 3.225, -23 }, + { 3.25, -23 }, + { 3.275, -23 }, + { 3.3, -23 }, + { 3.325, -23 }, + { 3.35, -23 }, + { 3.375, -23 }, + { 3.4, -23 }, + { 3.425, -23 }, + { 3.45, -23 }, + { 3.475, -23 }, + { 3.5, -23 }, + { 3.525, -23 }, + { 3.55, -23 }, + { 3.575, -23 }, + { 3.6, -23 }, + { 3.625, -23 }, + { 3.65, -23 }, + { 3.675, -23 }, + { 3.7, -23 }, + { 3.725, -23 }, + { 3.75, -23 }, + { 3.775, -23 }, + { 3.8, -23 }, + { 3.825, -22.9865565 }, + { 3.85, -22.9730647 }, + { 3.875, -22.9595252 }, + { 3.9, -22.9459387 }, + { 3.925, -22.9323056 }, + { 3.95, -22.9186265 }, + { 3.975, -22.904902 }, + { 4, -22.8911327 }, + { 4.025, -23 }, + { 4.05, -23 }, + { 4.075, -23 }, + { 4.1, -23 }, + { 4.125, -23 }, + { 4.15, -23 }, + { 4.175, -23 }, + { 4.2, -23 }, + { 4.225, -23 }, + { 4.25, -23 }, + { 4.275, -23 }, + { 4.3, -23 }, + { 4.325, -23 }, + { 4.35, -23 }, + { 4.375, -23 }, + { 4.4, -23 }, + { 4.425, -23 }, + { 4.45, -23 }, + { 4.475, -23 }, + { 4.5, -23 }, + { 4.525, -23 }, + { 4.55, -23 }, + { 4.575, -23 }, + { 4.6, -23 }, + { 4.625, -23 }, + { 4.65, -23 }, + { 4.675, -23 }, + { 4.7, -23 }, + { 4.725, -23 }, + { 4.75, -23 }, + { 4.775, -23 }, + { 4.8, -23 }, + { 4.825, -22.9930159 }, + { 4.85, -22.9859341 }, + { 4.875, -22.9787553 }, + { 4.9, -22.9714804 }, + { 4.925, -22.9641104 }, + { 4.95, -22.9566459 }, + { 4.975, -22.9490879 }, + { 5, -22.9414372 }, { 5.025, -23 }, { 5.05, -23 }, { 5.075, -23 }, @@ -473,173 +473,173 @@ TEST(adex_cell_group, probe) { { 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 },}; + { 5.825, -22.9992216 }, + { 5.85, -22.9982979 }, + { 5.875, -22.9972299 }, + { 5.9, -22.9960188 }, + { 5.925, -22.9946657 }, + { 5.95, -22.9931718 }, + { 5.975, -22.991538 }, + { 6, -22.9897656 }, + { 6.025, -22.9878556 }, + { 6.05, -22.985809 }, + { 6.075, -22.983627 }, + { 6.1, -22.9813106 }, + { 6.125, -22.9788609 }, + { 6.15, -22.976279 }, + { 6.175, -22.9735658 }, + { 6.2, -22.9707225 }, + { 6.225, -22.9677501 }, + { 6.25, -22.9646496 }, + { 6.275, -22.961422 }, + { 6.3, -22.9580684 }, + { 6.325, -22.9545897 }, + { 6.35, -22.9509871 }, + { 6.375, -22.9472614 }, + { 6.4, -22.9434137 }, + { 6.425, -22.939445 }, + { 6.45, -22.9353563 }, + { 6.475, -22.9311485 }, + { 6.5, -22.9268226 }, + { 6.525, -22.9223796 }, + { 6.55, -22.9178205 }, + { 6.575, -22.9131462 }, + { 6.6, -22.9083576 }, + { 6.625, -22.9034558 }, + { 6.65, -22.8984416 }, + { 6.675, -22.893316 }, + { 6.7, -22.8880799 }, + { 6.725, -22.8827342 }, + { 6.75, -22.8772799 }, + { 6.775, -22.8717179 }, + { 6.8, -22.866049 }, + { 6.825, -22.8602743 }, + { 6.85, -22.8543945 }, + { 6.875, -22.8484106 }, + { 6.9, -22.8423234 }, + { 6.925, -22.8361339 }, + { 6.95, -22.8298429 }, + { 6.975, -22.8234514 }, + { 7, -22.81696 }, + { 7.025, -22.8103698 }, + { 7.05, -22.8036816 }, + { 7.075, -22.7968962 }, + { 7.1, -22.7900145 }, + { 7.125, -22.7830373 }, + { 7.15, -22.7759654 }, + { 7.175, -22.7687998 }, + { 7.2, -22.7615411 }, + { 7.225, -22.7541903 }, + { 7.25, -22.7467481 }, + { 7.275, -22.7392154 }, + { 7.3, -22.7315929 }, + { 7.325, -22.7238815 }, + { 7.35, -22.716082 }, + { 7.375, -22.7081951 }, + { 7.4, -22.7002217 }, + { 7.425, -22.6921625 }, + { 7.45, -22.6840183 }, + { 7.475, -22.6757899 }, + { 7.5, -22.6674781 }, + { 7.525, -22.6590836 }, + { 7.55, -22.6506071 }, + { 7.575, -22.6420494 }, + { 7.6, -22.6334114 }, + { 7.625, -22.6246937 }, + { 7.65, -22.615897 }, + { 7.675, -22.6070221 }, + { 7.7, -22.5980698 }, + { 7.725, -22.5890407 }, + { 7.75, -22.5799357 }, + { 7.775, -22.5707553 }, + { 7.8, -22.5615004 }, + { 7.825, -22.5521716 }, + { 7.85, -22.5427696 }, + { 7.875, -22.5332952 }, + { 7.9, -22.523749 }, + { 7.925, -22.5141317 }, + { 7.95, -22.5044441 }, + { 7.975, -22.4946868 }, + { 8, -22.4848605 }, + { 8.025, -22.4749658 }, + { 8.05, -22.4650035 }, + { 8.075, -22.4549742 }, + { 8.1, -22.4448785 }, + { 8.125, -22.4347172 }, + { 8.15, -22.4244909 }, + { 8.175, -22.4142002 }, + { 8.2, -22.4038458 }, + { 8.225, -22.3934283 }, + { 8.25, -22.3829484 }, + { 8.275, -22.3724067 }, + { 8.3, -22.3618039 }, + { 8.325, -22.3511405 }, + { 8.35, -22.3404173 }, + { 8.375, -22.3296347 }, + { 8.4, -22.3187935 }, + { 8.425, -22.3078942 }, + { 8.45, -22.2969374 }, + { 8.475, -22.2859239 }, + { 8.5, -22.2748541 }, + { 8.525, -22.2637286 }, + { 8.55, -22.2525481 }, + { 8.575, -22.2413131 }, + { 8.6, -22.2300243 }, + { 8.625, -22.2186822 }, + { 8.65, -22.2072874 }, + { 8.675, -22.1958404 }, + { 8.7, -22.1843419 }, + { 8.725, -22.1727924 }, + { 8.75, -22.1611925 }, + { 8.775, -22.1495427 }, + { 8.8, -22.1378436 }, + { 8.825, -22.1260957 }, + { 8.85, -22.1142997 }, + { 8.875, -22.102456 }, + { 8.9, -22.0905651 }, + { 8.925, -22.0786277 }, + { 8.95, -22.0666443 }, + { 8.975, -22.0546154 }, + { 9, -22.0425415 }, + { 9.025, -22.0304232 }, + { 9.05, -22.0182609 }, + { 9.075, -22.0060553 }, + { 9.1, -21.9938067 }, + { 9.125, -21.9815158 }, + { 9.15, -21.969183 }, + { 9.175, -21.9568089 }, + { 9.2, -21.944394 }, + { 9.225, -21.9319387 }, + { 9.25, -21.9194435 }, + { 9.275, -21.906909 }, + { 9.3, -21.8943356 }, + { 9.325, -21.8817238 }, + { 9.35, -21.8690742 }, + { 9.375, -21.8563871 }, + { 9.4, -21.8436631 }, + { 9.425, -21.8309026 }, + { 9.45, -21.8181062 }, + { 9.475, -21.8052742 }, + { 9.5, -21.7924072 }, + { 9.525, -21.7795056 }, + { 9.55, -21.7665699 }, + { 9.575, -21.7536005 }, + { 9.6, -21.7405979 }, + { 9.625, -21.7275625 }, + { 9.65, -21.7144948 }, + { 9.675, -21.7013952 }, + { 9.7, -21.6882642 }, + { 9.725, -21.6751022 }, + { 9.75, -21.6619096 }, + { 9.775, -21.6486869 }, + { 9.8, -21.6354345 }, + { 9.825, -21.6221529 }, + { 9.85, -21.6088424 }, + { 9.875, -21.5955035 }, + { 9.9, -21.5821366 }, + { 9.925, -21.5687421 }, + { 9.95, -21.5553205 }, + { 9.975, -21.5418721 },}; ASSERT_TRUE(testing::seq_eq(ums[{0, "a"}], exp)); // gid == 1 is different, but of same size @@ -647,9 +647,10 @@ TEST(adex_cell_group, probe) { 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)); + EXPECT_EQ(spikes.size(), 6u); + std::vector sexp{{{0, 0}, 2}, {{0, 0}, 3}, {{0, 0}, 4}, {{0, 0}, 5}, + {{1, 0}, 2}, {{1, 0}, 5}, }; + ASSERT_EQ(spikes, sexp); } TEST(adex_cell_group, probe_with_connections) { @@ -668,214 +669,215 @@ TEST(adex_cell_group, probe_with_connections) { sim.add_sampler(arb::all_probes, arb::regular_schedule(0.025_ms), fun); - std::vector spikes; + std::vector spikes; sim.set_global_spike_callback( - [&spikes](const std::vector& spk) { for (const auto& s: spk) spikes.push_back(s.time); } + [&spikes](const std::vector& spk) { for (const auto& s: spk) spikes.push_back(s); } ); sim.run(10.0_ms, 0.005_ms); + 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 }, + { 0.025, -17.9866192 }, + { 0.05, -17.9732653 }, + { 0.075, -17.9599383 }, + { 0.1, -17.9466383 }, + { 0.125, -17.9333653 }, + { 0.15, -17.9201191 }, + { 0.175, -17.9068998 }, + { 0.2, -17.8937075 }, + { 0.225, -17.8805421 }, + { 0.25, -17.8674036 }, + { 0.275, -17.8542919 }, + { 0.3, -17.8412072 }, + { 0.325, -17.8281493 }, + { 0.35, -17.8151183 }, + { 0.375, -17.8021141 }, + { 0.4, -17.7891368 }, + { 0.425, -17.7761864 }, + { 0.45, -17.7632627 }, + { 0.475, -17.7503659 }, + { 0.5, -17.7374959 }, + { 0.525, -17.7246527 }, + { 0.55, -17.7118363 }, + { 0.575, -17.6990467 }, + { 0.6, -17.6862839 }, + { 0.625, -17.6735478 }, + { 0.65, -17.6608384 }, + { 0.675, -17.6481558 }, + { 0.7, -17.6354999 }, + { 0.725, -17.6228707 }, + { 0.75, -17.6102682 }, + { 0.775, -17.5976924 }, + { 0.8, -17.5851432 }, + { 0.825, -17.5726207 }, + { 0.85, -17.5601248 }, + { 0.875, -17.5476556 }, + { 0.9, -17.5352129 }, + { 0.925, -17.5227969 }, + { 0.95, -17.5104074 }, + { 0.975, -17.4980445 }, + { 1, -17.4857081 }, + { 1.025, -17.4733983 }, + { 1.05, -17.461115 }, + { 1.075, -17.4488581 }, + { 1.1, -17.4366278 }, + { 1.125, -17.4244239 }, + { 1.15, -17.4122464 }, + { 1.175, -17.4000954 }, + { 1.2, -17.3879707 }, + { 1.225, -17.3758725 }, + { 1.25, -17.3638006 }, + { 1.275, -17.3517551 }, + { 1.3, -17.3397359 }, + { 1.325, -17.327743 }, + { 1.35, -17.3157764 }, + { 1.375, -17.3038361 }, + { 1.4, -17.291922 }, + { 1.425, -17.2800341 }, + { 1.45, -17.2681725 }, + { 1.475, -17.256337 }, + { 1.5, -17.2445277 }, + { 1.525, -17.2327445 }, + { 1.55, -17.2209875 }, + { 1.575, -17.2092566 }, + { 1.6, -17.1975517 }, + { 1.625, -17.1858729 }, + { 1.65, -17.1742201 }, + { 1.675, -17.1625934 }, + { 1.7, -17.1509926 }, + { 1.725, -17.1394178 }, + { 1.75, -17.1278689 }, + { 1.775, -17.1163459 }, + { 1.8, -17.1048489 }, + { 1.825, -17.0933777 }, + { 1.85, -17.0819323 }, + { 1.875, -17.0705128 }, + { 1.9, -17.059119 }, + { 1.925, -17.047751 }, + { 1.95, -17.0364088 }, + { 1.975, -17.0250923 }, + { 2, -17.013855 }, + { 2.025, -23 }, + { 2.05, -23 }, + { 2.075, -23 }, + { 2.1, -23 }, + { 2.125, -23 }, + { 2.15, -23 }, + { 2.175, -23 }, + { 2.2, -23 }, + { 2.225, -23 }, + { 2.25, -23 }, + { 2.275, -23 }, + { 2.3, -23 }, + { 2.325, -23 }, + { 2.35, -23 }, + { 2.375, -23 }, + { 2.4, -23 }, + { 2.425, -23 }, + { 2.45, -23 }, + { 2.475, -23 }, + { 2.5, -23 }, + { 2.525, -23 }, + { 2.55, -23 }, + { 2.575, -23 }, + { 2.6, -23 }, + { 2.625, -23 }, + { 2.65, -23 }, + { 2.675, -23 }, + { 2.7, -23 }, + { 2.725, -23 }, + { 2.75, -23 }, + { 2.775, -23 }, + { 2.8, -23 }, + { 2.825, -22.9798333 }, + { 2.85, -22.9596698 }, + { 2.875, -22.93951 }, + { 2.9, -22.9193541 }, + { 2.925, -22.8992023 }, + { 2.95, -22.8790549 }, + { 2.975, -22.8589123 }, + { 3, -22.8387768 }, + { 3.025, -23 }, + { 3.05, -23 }, + { 3.075, -23 }, + { 3.1, -23 }, + { 3.125, -23 }, + { 3.15, -23 }, + { 3.175, -23 }, + { 3.2, -23 }, + { 3.225, -23 }, + { 3.25, -23 }, + { 3.275, -23 }, + { 3.3, -23 }, + { 3.325, -23 }, + { 3.35, -23 }, + { 3.375, -23 }, + { 3.4, -23 }, + { 3.425, -23 }, + { 3.45, -23 }, + { 3.475, -23 }, + { 3.5, -23 }, + { 3.525, -23 }, + { 3.55, -23 }, + { 3.575, -23 }, + { 3.6, -23 }, + { 3.625, -23 }, + { 3.65, -23 }, + { 3.675, -23 }, + { 3.7, -23 }, + { 3.725, -23 }, + { 3.75, -23 }, + { 3.775, -23 }, + { 3.8, -23 }, + { 3.825, -22.9865544 }, + { 3.85, -22.9730605 }, + { 3.875, -22.959519 }, + { 3.9, -22.9459305 }, + { 3.925, -22.9322955 }, + { 3.95, -22.9186145 }, + { 3.975, -22.9048882 }, + { 4, -22.8911191 }, + { 4.025, -23 }, + { 4.05, -23 }, + { 4.075, -23 }, + { 4.1, -23 }, + { 4.125, -23 }, + { 4.15, -23 }, + { 4.175, -23 }, + { 4.2, -23 }, + { 4.225, -23 }, + { 4.25, -23 }, + { 4.275, -23 }, + { 4.3, -23 }, + { 4.325, -23 }, + { 4.35, -23 }, + { 4.375, -23 }, + { 4.4, -23 }, + { 4.425, -23 }, + { 4.45, -23 }, + { 4.475, -23 }, + { 4.5, -23 }, + { 4.525, -23 }, + { 4.55, -23 }, + { 4.575, -23 }, + { 4.6, -23 }, + { 4.625, -23 }, + { 4.65, -23 }, + { 4.675, -23 }, + { 4.7, -23 }, + { 4.725, -23 }, + { 4.75, -23 }, + { 4.775, -23 }, + { 4.8, -23 }, + { 4.825, -22.9930115 }, + { 4.85, -22.9859252 }, + { 4.875, -22.9787422 }, + { 4.9, -22.9714631 }, + { 4.925, -22.9640889 }, + { 4.95, -22.9566204 }, + { 4.975, -22.9490585 }, + { 5, -22.9414052 }, { 5.025, -23 }, { 5.05, -23 }, { 5.075, -23 }, @@ -908,173 +910,173 @@ TEST(adex_cell_group, probe_with_connections) { { 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 },}; + { 5.825, -22.999215 }, + { 5.85, -22.9982847 }, + { 5.875, -22.9972104 }, + { 5.9, -22.995993 }, + { 5.925, -22.9946337 }, + { 5.95, -22.9931337 }, + { 5.975, -22.9914939 }, + { 6, -22.9897156 }, + { 6.025, -22.9877998 }, + { 6.05, -22.9857476 }, + { 6.075, -22.98356 }, + { 6.1, -22.9812381 }, + { 6.125, -22.978783 }, + { 6.15, -22.9761958 }, + { 6.175, -22.9734774 }, + { 6.2, -22.970629 }, + { 6.225, -22.9676516 }, + { 6.25, -22.9645462 }, + { 6.275, -22.9613138 }, + { 6.3, -22.9579554 }, + { 6.325, -22.9544722 }, + { 6.35, -22.950865 }, + { 6.375, -22.9471348 }, + { 6.4, -22.9432828 }, + { 6.425, -22.9393098 }, + { 6.45, -22.9352169 }, + { 6.475, -22.931005 }, + { 6.5, -22.9266751 }, + { 6.525, -22.9222281 }, + { 6.55, -22.9176652 }, + { 6.575, -22.9129871 }, + { 6.6, -22.9081948 }, + { 6.625, -22.9032894 }, + { 6.65, -22.8982716 }, + { 6.675, -22.8931426 }, + { 6.7, -22.8879031 }, + { 6.725, -22.8825541 }, + { 6.75, -22.8770966 }, + { 6.775, -22.8715315 }, + { 6.8, -22.8658595 }, + { 6.825, -22.8600818 }, + { 6.85, -22.8541991 }, + { 6.875, -22.8482124 }, + { 6.9, -22.8421225 }, + { 6.925, -22.8359303 }, + { 6.95, -22.8296367 }, + { 6.975, -22.8232425 }, + { 7, -22.8167487 }, + { 7.025, -22.8101561 }, + { 7.05, -22.8034656 }, + { 7.075, -22.7966779 }, + { 7.1, -22.789794 }, + { 7.125, -22.7828146 }, + { 7.15, -22.7757407 }, + { 7.175, -22.768573 }, + { 7.2, -22.7613125 }, + { 7.225, -22.7539598 }, + { 7.25, -22.7465158 }, + { 7.275, -22.7389813 }, + { 7.3, -22.7313572 }, + { 7.325, -22.7236442 }, + { 7.35, -22.7158431 }, + { 7.375, -22.7079547 }, + { 7.4, -22.6999799 }, + { 7.425, -22.6919193 }, + { 7.45, -22.6837738 }, + { 7.475, -22.6755441 }, + { 7.5, -22.6672311 }, + { 7.525, -22.6588354 }, + { 7.55, -22.6503578 }, + { 7.575, -22.6417992 }, + { 7.6, -22.6331601 }, + { 7.625, -22.6244415 }, + { 7.65, -22.6156439 }, + { 7.675, -22.6067683 }, + { 7.7, -22.5978152 }, + { 7.725, -22.5887854 }, + { 7.75, -22.5796797 }, + { 7.775, -22.5704987 }, + { 7.8, -22.5612432 }, + { 7.825, -22.5519139 }, + { 7.85, -22.5425115 }, + { 7.875, -22.5330367 }, + { 7.9, -22.5234901 }, + { 7.925, -22.5138726 }, + { 7.95, -22.5041847 }, + { 7.975, -22.4944272 }, + { 8, -22.4846007 }, + { 8.025, -22.4747059 }, + { 8.05, -22.4647435 }, + { 8.075, -22.4547141 }, + { 8.1, -22.4446185 }, + { 8.125, -22.4344573 }, + { 8.15, -22.424231 }, + { 8.175, -22.4139405 }, + { 8.2, -22.4035863 }, + { 8.225, -22.3931691 }, + { 8.25, -22.3826894 }, + { 8.275, -22.3721481 }, + { 8.3, -22.3615456 }, + { 8.325, -22.3508826 }, + { 8.35, -22.3401598 }, + { 8.375, -22.3293777 }, + { 8.4, -22.318537 }, + { 8.425, -22.3076382 }, + { 8.45, -22.2966821 }, + { 8.475, -22.2856692 }, + { 8.5, -22.2746 }, + { 8.525, -22.2634753 }, + { 8.55, -22.2522955 }, + { 8.575, -22.2410613 }, + { 8.6, -22.2297733 }, + { 8.625, -22.2184321 }, + { 8.65, -22.2070381 }, + { 8.675, -22.1955921 }, + { 8.7, -22.1840945 }, + { 8.725, -22.172546 }, + { 8.75, -22.1609471 }, + { 8.775, -22.1492983 }, + { 8.8, -22.1376003 }, + { 8.825, -22.1258536 }, + { 8.85, -22.1140587 }, + { 8.875, -22.1022161 }, + { 8.9, -22.0903265 }, + { 8.925, -22.0783904 }, + { 8.95, -22.0664082 }, + { 8.975, -22.0543806 }, + { 9, -22.042308 }, + { 9.025, -22.030191 }, + { 9.05, -22.0180302 }, + { 9.075, -22.0058259 }, + { 9.1, -21.9935788 }, + { 9.125, -21.9812894 }, + { 9.15, -21.9689581 }, + { 9.175, -21.9565855 }, + { 9.2, -21.9441721 }, + { 9.225, -21.9317183 }, + { 9.25, -21.9192248 }, + { 9.275, -21.9066919 }, + { 9.3, -21.8941201 }, + { 9.325, -21.88151 }, + { 9.35, -21.8688621 }, + { 9.375, -21.8561767 }, + { 9.4, -21.8434545 }, + { 9.425, -21.8306958 }, + { 9.45, -21.8179011 }, + { 9.475, -21.805071 }, + { 9.5, -21.7922058 }, + { 9.525, -21.779306 }, + { 9.55, -21.7663722 }, + { 9.575, -21.7534047 }, + { 9.6, -21.740404 }, + { 9.625, -21.7273705 }, + { 9.65, -21.7143048 }, + { 9.675, -21.7012071 }, + { 9.7, -21.6880781 }, + { 9.725, -21.6749181 }, + { 9.75, -21.6617276 }, + { 9.775, -21.648507 }, + { 9.8, -21.6352567 }, + { 9.825, -21.6219771 }, + { 9.85, -21.6086687 }, + { 9.875, -21.595332 }, + { 9.9, -21.5819672 }, + { 9.925, -21.5685749 }, + { 9.95, -21.5551554 }, + { 9.975, -21.5417092 },}; EXPECT_EQ((ums[{0, "a"}].size()), exp.size()); ASSERT_TRUE(testing::seq_eq(ums[{0, "a"}], exp)); @@ -1083,7 +1085,8 @@ TEST(adex_cell_group, probe_with_connections) { 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)); + EXPECT_EQ(spikes.size(), 6u); + std::vector sexp{{{0, 0}, 2}, {{0, 0}, 3}, {{0, 0}, 4}, {{0, 0}, 5}, + {{1, 0}, 2}, {{1, 0}, 5}, }; + ASSERT_EQ(spikes, sexp); }