diff --git a/miniapp/miniapp.cpp b/miniapp/miniapp.cpp index 00adf7e5723a35f0b009e07e43d29ae49d38c4f0..898e478c67ff4ed0c76712b79e5604a25dc27236 100644 --- a/miniapp/miniapp.cpp +++ b/miniapp/miniapp.cpp @@ -6,7 +6,7 @@ #include <json/src/json.hpp> -#include <catypes.hpp> +#include <common_types.hpp> #include <cell.hpp> #include <cell_group.hpp> #include <fvm_cell.hpp> diff --git a/miniapp/trace_sampler.hpp b/miniapp/trace_sampler.hpp index 96ad269afed90551f710fe57c3651b58db3304fd..1e20bea2e3bd0a260e5cd829adb77d935f7797d5 100644 --- a/miniapp/trace_sampler.hpp +++ b/miniapp/trace_sampler.hpp @@ -3,7 +3,7 @@ #include <cstdlib> #include <vector> -#include <catypes.hpp> +#include <common_types.hpp> #include <cell.hpp> #include <util/optional.hpp> diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b6de968388dafa466a57c65e960044c36b34625..8e0db8876fadf3d030b65ccac9a653eedc1c8d37 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,7 +2,7 @@ set(HEADERS swcio.hpp ) set(BASE_SOURCES - catypes_io.cpp + common_types_io.cpp cell.cpp parameter_list.cpp profiling/profiler.cpp diff --git a/src/cell.hpp b/src/cell.hpp index effdf9254a22b1a5db20673544274a0784db9fce..746549289f1b718433388e92f5f1ca9d13d433c1 100644 --- a/src/cell.hpp +++ b/src/cell.hpp @@ -5,7 +5,7 @@ #include <thread> #include <vector> -#include "catypes.hpp" +#include "common_types.hpp" #include "cell_tree.hpp" #include "segment.hpp" #include "stimulus.hpp" diff --git a/src/cell_group.hpp b/src/cell_group.hpp index 69a94a98fa3de23890b735a3d52a1c381c70322d..e70d4b0bb5826b6ccde449f63a7fad6dab29c783 100644 --- a/src/cell_group.hpp +++ b/src/cell_group.hpp @@ -4,8 +4,8 @@ #include <functional> #include <vector> -#include <catypes.hpp> #include <cell.hpp> +#include <common_types.hpp> #include <event_queue.hpp> #include <spike.hpp> #include <spike_source.hpp> @@ -40,9 +40,15 @@ public: { initialize_cells(); - source_id_type source_id={gid_base_,0}; + // Create spike detectors and associate them with globally unique source ids, + // as specified by cell gid and cell-local zero-based index. + + cell_gid_type source_gid = gid_base_; + cell_lid_type source_lid = 0u; + for (auto& d : c.detectors()) { - ++source_id.index; + cell_member_type source_id{source_gid, source_lid++}; + spike_sources_.push_back({ source_id, spike_detector_type(cell_, d.location, d.threshold, 0.f) }); diff --git a/src/cell_tree.hpp b/src/cell_tree.hpp index 3bbcb3ef4816ea61dcd8702b1587069f342645be..ce06d69a8b8ac7427b991e7e54ac2bc764a052e7 100644 --- a/src/cell_tree.hpp +++ b/src/cell_tree.hpp @@ -11,7 +11,7 @@ #include <vector/include/Vector.hpp> -#include "catypes.hpp" +#include "common_types.hpp" #include "tree.hpp" #include "util.hpp" diff --git a/src/catypes.hpp b/src/common_types.hpp similarity index 57% rename from src/catypes.hpp rename to src/common_types.hpp index e2e9799be3dbb7d4a91a75e01c8adf2eaa5a565e..da81ed5d65368af60095d8b9dab40aeede29467a 100644 --- a/src/catypes.hpp +++ b/src/common_types.hpp @@ -13,18 +13,35 @@ namespace nest { namespace mc { -// for identifying cells globally +// For identifying cells globally. + using cell_gid_type = std::uint32_t; -// for sizes of collections of cells +// For sizes of collections of cells. + using cell_size_type = typename std::make_unsigned<cell_gid_type>::type; -// for indexes into cell-local data +// For indexes into cell-local data. +// +// Local indices for items within a particular cell-local collection should be +// zero-based and numbered contiguously. + using cell_lid_type = std::uint32_t; -// for counts of cell-local data +// For counts of cell-local data. + using cell_local_size_type = typename std::make_unsigned<cell_lid_type>::type; +// For global identification of an item of cell local data. +// +// Items of cell_member_type must: +// +// * be associated with a unique cell, identified by the member `gid` +// (see: cell_gid_type); +// +// * identify an item within a cell-local collection by the member `index` +// (see: cell_lid_type). + struct cell_member_type { cell_gid_type gid; cell_lid_type index; diff --git a/src/catypes_io.cpp b/src/common_types_io.cpp similarity index 83% rename from src/catypes_io.cpp rename to src/common_types_io.cpp index 4c9d185f0bf68a2cd1c9ba8cbe67df26f614631d..ad6ca540b80e1ce296c8ba2f4eceb52cd9ebfd63 100644 --- a/src/catypes_io.cpp +++ b/src/common_types_io.cpp @@ -1,6 +1,6 @@ #include <iostream> -#include <catypes.hpp> +#include <common_types.hpp> std::ostream& operator<<(std::ostream& O, nest::mc::cell_member_type m) { return O << m.gid << ':' << m.index; diff --git a/src/communication/communicator.hpp b/src/communication/communicator.hpp index d73f4dcdbac22d4986b3d59fcefb6a66e4aae357..2eab80bd0556722d9fd3489b29c5b60d68180565 100644 --- a/src/communication/communicator.hpp +++ b/src/communication/communicator.hpp @@ -6,12 +6,12 @@ #include <random> #include <spike.hpp> -#include <threading/threading.hpp> #include <util/double_buffer.hpp> #include <algorithms.hpp> +#include <connection.hpp> #include <event_queue.hpp> - -#include "connection.hpp" +#include <spike.hpp> +#include <util/debug.hpp> namespace nest { namespace mc { @@ -50,10 +50,6 @@ public: using spike_type = spike<cell_member_type, time_type>; using connection_type = connection<time_type>; - /// thread private storage for accumulating spikes - using local_spike_store_type = - threading::enumerable_thread_specific<std::vector<spike_type>>; - /// per-cell group lists of events to be delivered using event_queue = std::vector<postsynaptic_spike_event<time_type>>; diff --git a/src/communication/mpi_global_policy.hpp b/src/communication/mpi_global_policy.hpp index 54ded32cc059e2bff221a880edbd3ee68c4f272b..0e9ec33370695990e2e2fa0d8d03d4a1e8b26c26 100644 --- a/src/communication/mpi_global_policy.hpp +++ b/src/communication/mpi_global_policy.hpp @@ -9,7 +9,7 @@ #include <vector> #include <algorithms.hpp> -#include <catypes.hpp> +#include <common_types.hpp> #include <communication/mpi.hpp> #include <spike.hpp> diff --git a/src/compartment.hpp b/src/compartment.hpp index c69f622f16ece3638e32d8b95155fa1c31f33a65..da6bbcf57298593a60f7510822832060c7a89846 100644 --- a/src/compartment.hpp +++ b/src/compartment.hpp @@ -3,7 +3,7 @@ #include <iterator> #include <utility> -#include "catypes.hpp" +#include "common_types.hpp" namespace nest { namespace mc { diff --git a/src/connection.hpp b/src/connection.hpp index 3ef99a9c28e1007c88835b9e1df1be4765bc32f5..b9e40fb5baeb83ad69c87c219f65d9f98acc4456 100644 --- a/src/connection.hpp +++ b/src/connection.hpp @@ -2,7 +2,7 @@ #include <cstdint> -#include <catypes.hpp> +#include <common_types.hpp> #include <event_queue.hpp> #include <spike.hpp> diff --git a/src/event_queue.hpp b/src/event_queue.hpp index b78cc0ca821aa2d393c64280164adad50f355e74..31f6a5a10cf6b0c9ff9c0ff049909b801cd25b34 100644 --- a/src/event_queue.hpp +++ b/src/event_queue.hpp @@ -4,7 +4,7 @@ #include <ostream> #include <queue> -#include "catypes.hpp" +#include "common_types.hpp" #include "util/optional.hpp" namespace nest { diff --git a/src/model.hpp b/src/model.hpp index 5479d2ed5150d3e0b4cb965ff2887084ec305f99..29d59db4d110e798ca403eb8dbd07dc2653d926e 100644 --- a/src/model.hpp +++ b/src/model.hpp @@ -1,13 +1,16 @@ +# pragma once + #include <cstdlib> #include <vector> -#include <catypes.hpp> +#include <common_types.hpp> #include <cell.hpp> #include <cell_group.hpp> -#include <communication/communicator.hpp> -#include <communication/global_policy.hpp> #include <fvm_cell.hpp> #include <recipe.hpp> +#include <thread_private_spike_store.hpp> +#include <communication/communicator.hpp> +#include <communication/global_policy.hpp> #include <profiling/profiler.hpp> #include "trace_sampler.hpp" @@ -15,55 +18,6 @@ namespace nest { namespace mc { -template <typename Time> -class thread_private_spike_store { -public : - using id_type = cell_gid_type; - using time_type = Time; - using spike_type = spike<cell_member_type, time_type>; - - std::vector<spike_type> gather() const { - std::vector<spike_type> spikes; - unsigned num_spikes = 0u; - for (auto& b : buffers_) { - num_spikes += b.size(); - } - spikes.reserve(num_spikes); - - for (auto& b : buffers_) { - spikes.insert(spikes.begin(), b.begin(), b.end()); - } - - return spikes; - } - - std::vector<spike_type>& get() { - return buffers_.local(); - } - - const std::vector<spike_type>& get() const { - return buffers_.local(); - } - - void clear() { - for (auto& b : buffers_) { - b.clear(); - } - } - - void insert(const std::vector<spike_type>& spikes) { - auto& buff = get(); - buff.insert(buff.begin(), spikes.begin(), spikes.end()); - } - -private : - /// thread private storage for accumulating spikes - using local_spike_store_type = - threading::enumerable_thread_specific<std::vector<spike_type>>; - - local_spike_store_type buffers_; -}; - template <typename Cell> class model { public: diff --git a/src/segment.hpp b/src/segment.hpp index a7600da948dfec79dfc600d9099b421accfd1aae..248306a6c3a976d123b9d412b93c09156305086d 100644 --- a/src/segment.hpp +++ b/src/segment.hpp @@ -4,7 +4,7 @@ #include <vector> #include "algorithms.hpp" -#include "catypes.hpp" +#include "common_types.hpp" #include "compartment.hpp" #include "math.hpp" #include "parameter_list.hpp" diff --git a/src/thread_private_spike_store.hpp b/src/thread_private_spike_store.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1e14ab104619a42b8aa9f014172bb5716576017c --- /dev/null +++ b/src/thread_private_spike_store.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include <vector> + +#include <common_types.hpp> +#include <spike.hpp> +#include <threading/threading.hpp> + +namespace nest { +namespace mc { + +/// Handles the complexity of managing thread private buffers of spikes. +/// Internally stores one thread private buffer of spikes for each hardware thread. +/// This can be accessed directly using the get() method, which returns a reference to +/// The thread private buffer of the calling thread. +/// The insert() and gather() methods add a vector of spikes to the buffer, +/// and collate all of the buffers into a single vector respectively. +template <typename Time> +class thread_private_spike_store { +public : + using id_type = cell_gid_type; + using time_type = Time; + using spike_type = spike<cell_member_type, time_type>; + + /// Collate all of the individual buffers into a single vector of spikes. + /// Does not modify the buffer contents. + std::vector<spike_type> gather() const { + std::vector<spike_type> spikes; + unsigned num_spikes = 0u; + for (auto& b : buffers_) { + num_spikes += b.size(); + } + spikes.reserve(num_spikes); + + for (auto& b : buffers_) { + spikes.insert(spikes.begin(), b.begin(), b.end()); + } + + return spikes; + } + + /// Return a reference to the thread private buffer of the calling thread + std::vector<spike_type>& get() { + return buffers_.local(); + } + + /// Return a reference to the thread private buffer of the calling thread + const std::vector<spike_type>& get() const { + return buffers_.local(); + } + + /// Clear all of the thread private buffers + void clear() { + for (auto& b : buffers_) { + b.clear(); + } + } + + /// Append the passed spikes to the end of the thread private buffer of the + /// calling thread + void insert(const std::vector<spike_type>& spikes) { + auto& buff = get(); + buff.insert(buff.end(), spikes.begin(), spikes.end()); + } + +private : + /// thread private storage for accumulating spikes + using local_spike_store_type = + threading::enumerable_thread_specific<std::vector<spike_type>>; + + local_spike_store_type buffers_; +}; + +} // namespace mc +} // namespace nest diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 936a0ca45c42e030678c4eadb45f7ab58e00ef87..fb1b9369d4e38498c8953fc0ce2f16c1dbc715dc 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -27,6 +27,7 @@ set(TEST_SOURCES test_probe.cpp test_segment.cpp test_spikes.cpp + test_spike_store.cpp test_stimulus.cpp test_swcio.cpp test_synapses.cpp @@ -46,12 +47,12 @@ foreach(target ${TARGETS}) target_link_libraries(${target} LINK_PUBLIC cellalgo gtest) if(WITH_TBB) - target_link_libraries(${target} LINK_PUBLIC ${TBB_LIBRARIES}) + target_link_libraries(${target} LINK_PUBLIC ${TBB_LIBRARIES}) endif() if(WITH_MPI) - target_link_libraries(${target} LINK_PUBLIC ${MPI_C_LIBRARIES}) - set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS "${MPI_C_LINK_FLAGS}") + target_link_libraries(${target} LINK_PUBLIC ${MPI_C_LIBRARIES}) + set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS "${MPI_C_LINK_FLAGS}") endif() set_target_properties(${target} diff --git a/tests/unit/test_cell_group.cpp b/tests/unit/test_cell_group.cpp index 050974f0bda553a04cff3b932afa7269d3393034..3aed222029f4535a85d9713849cff184a3684531 100644 --- a/tests/unit/test_cell_group.cpp +++ b/tests/unit/test_cell_group.cpp @@ -1,6 +1,6 @@ #include "gtest.h" -#include <catypes.hpp> +#include <common_types.hpp> #include <fvm_cell.hpp> #include <cell_group.hpp> @@ -20,10 +20,8 @@ nest::mc::cell make_cell() { dendrite->mechanism("membrane").set("r_L", 100); - // add stimulus - cell.add_stimulus({1,1}, {5., 80., 0.3}); - - cell.add_detector({0,0}, 0); + cell.add_detector({0, 0}, 0); + cell.add_stimulus({1, 1}, {5., 80., 0.3}); return cell; } @@ -41,3 +39,36 @@ TEST(cell_group, test) EXPECT_EQ(group.spikes().size(), 4u); } +TEST(cell_group, sources) +{ + using namespace nest::mc; + + // TODO: extend to multi-cell cell groups when the time comes + + using cell_group_type = cell_group<fvm::fvm_cell<double, cell_local_size_type>>; + + auto cell = make_cell(); + EXPECT_EQ(cell.detectors().size(), 1u); + // add another detector on the cell to make things more interesting + cell.add_detector({1, 0.3}, 2.3); + + cell_gid_type first_gid = 37u; + auto group = cell_group_type{first_gid, cell}; + + // expect group sources to be lexicographically sorted by source id + // with gids in cell group's range and indices starting from zero + + const auto& sources = group.spike_sources(); + for (unsigned i = 0; i<sources.size(); ++i) { + auto id = sources[i].source_id; + if (i==0) { + EXPECT_EQ(id.gid, first_gid); + EXPECT_EQ(id.index, 0u); + } + else { + auto prev = sources[i-1].source_id; + EXPECT_GT(id, prev); + EXPECT_EQ(id.index, id.gid==prev.gid? prev.index+1: 0u); + } + } +} diff --git a/tests/unit/test_fvm.cpp b/tests/unit/test_fvm.cpp index ea8cd7dae4f1b2cc17af3c39cfd3422169180dca..c0ed438f20841d40a9f2653e38424aeeba4a64ea 100644 --- a/tests/unit/test_fvm.cpp +++ b/tests/unit/test_fvm.cpp @@ -2,7 +2,7 @@ #include "gtest.h" -#include <catypes.hpp> +#include <common_types.hpp> #include <cell.hpp> #include <fvm_cell.hpp> diff --git a/tests/unit/test_lexcmp.cpp b/tests/unit/test_lexcmp.cpp index a02ac0765823f61d54e8acee3cf38611552d9dd7..e2b5c476d7983f7726349953d15e34509aab031c 100644 --- a/tests/unit/test_lexcmp.cpp +++ b/tests/unit/test_lexcmp.cpp @@ -62,8 +62,8 @@ class lexcmp_test_refmemfn { public: explicit lexcmp_test_refmemfn(int foo): foo_(foo) {} - const int &foo() const { return foo_; } - int &foo() { return foo_; } + const int& foo() const { return foo_; } + int& foo() { return foo_; } private: int foo_; diff --git a/tests/unit/test_probe.cpp b/tests/unit/test_probe.cpp index 888e710eec377143b6ac6684e41cc222d0b0c107..5af744d2cc5f7f9b8a403b991b22d260c9ec74d4 100644 --- a/tests/unit/test_probe.cpp +++ b/tests/unit/test_probe.cpp @@ -1,6 +1,6 @@ #include "gtest.h" -#include "catypes.hpp" +#include "common_types.hpp" #include "cell.hpp" #include "fvm_cell.hpp" diff --git a/tests/unit/test_spike_store.cpp b/tests/unit/test_spike_store.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0cddfda3e71f81417716a114221fecd7305ee94c --- /dev/null +++ b/tests/unit/test_spike_store.cpp @@ -0,0 +1,92 @@ +#include "gtest.h" + +#include <thread_private_spike_store.hpp> + +TEST(spike_store, insert) +{ + using store_type = nest::mc::thread_private_spike_store<float>; + + store_type store; + + // insert 3 spike events and check that they were inserted correctly + store.insert({ + {{0,0}, 0.0f}, + {{1,2}, 0.5f}, + {{2,4}, 1.0f} + }); + + { + EXPECT_EQ(store.get().size(), 3u); + auto i = 0u; + for (auto& spike : store.get()) { + EXPECT_EQ(spike.source.gid, i); + EXPECT_EQ(spike.source.index, 2*i); + EXPECT_EQ(spike.time, float(i)/2.f); + ++i; + } + } + + // insert another 3 events, then check that they were appended to the + // original three events correctly + store.insert({ + {{3,6}, 1.5f}, + {{4,8}, 2.0f}, + {{5,10}, 2.5f} + }); + + { + EXPECT_EQ(store.get().size(), 6u); + auto i = 0u; + for (auto& spike : store.get()) { + EXPECT_EQ(spike.source.gid, i); + EXPECT_EQ(spike.source.index, 2*i); + EXPECT_EQ(spike.time, float(i)/2.f); + ++i; + } + } +} + +TEST(spike_store, clear) +{ + using store_type = nest::mc::thread_private_spike_store<float>; + + store_type store; + + // insert 3 spike events + store.insert({ + {{0,0}, 0.0f}, {{1,2}, 0.5f}, {{2,4}, 1.0f} + }); + EXPECT_EQ(store.get().size(), 3u); + store.clear(); + EXPECT_EQ(store.get().size(), 0u); +} + +template <typename I, typename T> +bool test_spike_equality( + nest::mc::spike<I,T> lhs, + nest::mc::spike<I,T> rhs) +{ + return lhs.time==rhs.time; +} + +TEST(spike_store, gather) +{ + using store_type = nest::mc::thread_private_spike_store<float>; + + store_type store; + + auto spikes = std::vector<store_type::spike_type> + { {{0,0}, 0.0f}, {{1,2}, 0.5f}, {{2,4}, 1.0f} }; + + store.insert(spikes); + auto gathered_spikes = store.gather(); + + EXPECT_EQ(gathered_spikes.size(), spikes.size()); + + for(auto i=0u; i<spikes.size(); ++i) { + EXPECT_EQ(spikes[i].source.gid, gathered_spikes[i].source.gid); + EXPECT_EQ(spikes[i].source.index, gathered_spikes[i].source.index); + EXPECT_EQ(spikes[i].time, gathered_spikes[i].time); + } +} + diff --git a/tests/validation/validate_ball_and_stick.cpp b/tests/validation/validate_ball_and_stick.cpp index a819173e937419a87471a79f1644a243b20e7ac0..62d06b4dac85885f935dd88855223551cc33fcce 100644 --- a/tests/validation/validate_ball_and_stick.cpp +++ b/tests/validation/validate_ball_and_stick.cpp @@ -1,7 +1,7 @@ #include <fstream> #include <json/src/json.hpp> -#include <catypes.hpp> +#include <common_types.hpp> #include <cell.hpp> #include <fvm_cell.hpp> diff --git a/tests/validation/validate_soma.cpp b/tests/validation/validate_soma.cpp index 203967014117ca539564aadb35da98ceb1b60054..24a0c1c59f0db24bc6f9c7832909f9f52af7beb7 100644 --- a/tests/validation/validate_soma.cpp +++ b/tests/validation/validate_soma.cpp @@ -1,7 +1,7 @@ #include <fstream> #include <json/src/json.hpp> -#include <catypes.hpp> +#include <common_types.hpp> #include <cell.hpp> #include <fvm_cell.hpp> diff --git a/tests/validation/validate_synapses.cpp b/tests/validation/validate_synapses.cpp index 9bc67172879d14e8aea1aed21311c344fcb9f64a..876b24eb55c9cad0e08c17cef9cdf16595f5bd90 100644 --- a/tests/validation/validate_synapses.cpp +++ b/tests/validation/validate_synapses.cpp @@ -3,7 +3,7 @@ #include <json/src/json.hpp> -#include <catypes.hpp> +#include <common_types.hpp> #include <cell.hpp> #include <cell_group.hpp> #include <fvm_cell.hpp>