From 6ba39a922326a9b36bb2f472e4343895d706375f Mon Sep 17 00:00:00 2001 From: Ben Cumming <bcumming@cscs.ch> Date: Fri, 22 Jun 2018 17:29:03 +0200 Subject: [PATCH] Benchmark cell type (#500) Add a new cell type, and corresponding cell_group implementation, for benchmarking the simulator library architecture. Add an benchmark_cell_group, where each cell in the group generates a spike train prescribed by a time_seq takes a prescribed time interval per cell to perform the cell_group::advance method. With this cell type, one can easily build arbitrary networks with prescribed spiking and cell update overheads. A miniapp that uses this cell type to build a benchmark model is implemented in example/bench. Fixes #493 Fixes #501 --- example/CMakeLists.txt | 1 + example/bench/CMakeLists.txt | 15 ++++ example/bench/bench.cpp | 78 +++++++++++++++++++ example/bench/parameters.cpp | 111 ++++++++++++++++++++++++++++ example/bench/parameters.hpp | 43 +++++++++++ example/bench/readme.md | 57 ++++++++++++++ example/bench/recipe.cpp | 71 ++++++++++++++++++ example/bench/recipe.hpp | 23 ++++++ example/miniapp/miniapp.cpp | 1 - example/miniapp/miniapp_recipes.cpp | 2 +- src/CMakeLists.txt | 1 + src/benchmark_cell.hpp | 21 ++++++ src/benchmark_cell_group.cpp | 90 ++++++++++++++++++++++ src/benchmark_cell_group.hpp | 41 ++++++++++ src/cell_group_factory.cpp | 4 + src/common_types.hpp | 7 +- src/common_types_io.cpp | 2 + src/spike_source_cell.hpp | 14 ++++ tests/unit/test_lif_cell_group.cpp | 2 +- 19 files changed, 578 insertions(+), 6 deletions(-) create mode 100644 example/bench/CMakeLists.txt create mode 100644 example/bench/bench.cpp create mode 100644 example/bench/parameters.cpp create mode 100644 example/bench/parameters.hpp create mode 100644 example/bench/readme.md create mode 100644 example/bench/recipe.cpp create mode 100644 example/bench/recipe.hpp create mode 100644 src/benchmark_cell.hpp create mode 100644 src/benchmark_cell_group.cpp create mode 100644 src/benchmark_cell_group.hpp create mode 100644 src/spike_source_cell.hpp diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 31e71224..0238e1f1 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(miniapp) add_subdirectory(generators) add_subdirectory(brunel) +add_subdirectory(bench) diff --git a/example/bench/CMakeLists.txt b/example/bench/CMakeLists.txt new file mode 100644 index 00000000..e5bf57c6 --- /dev/null +++ b/example/bench/CMakeLists.txt @@ -0,0 +1,15 @@ +add_executable(bench.exe bench.cpp recipe.cpp parameters.cpp) + +target_link_libraries(bench.exe LINK_PUBLIC ${ARB_LIBRARIES}) +target_link_libraries(bench.exe LINK_PUBLIC ${EXTERNAL_LIBRARIES}) + +if(ARB_WITH_MPI) + target_link_libraries(bench.exe LINK_PUBLIC ${MPI_C_LIBRARIES}) + set_property(TARGET bench.exe APPEND_STRING PROPERTY LINK_FLAGS "${MPI_C_LINK_FLAGS}") +endif() + +set_target_properties( + bench.exe + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/example" +) diff --git a/example/bench/bench.cpp b/example/bench/bench.cpp new file mode 100644 index 00000000..58b80633 --- /dev/null +++ b/example/bench/bench.cpp @@ -0,0 +1,78 @@ +/* + * Miniapp that uses the artificial benchmark cell type to test + * the simulator infrastructure. + */ +#include <fstream> +#include <iomanip> +#include <iostream> + +#include <json/json.hpp> + +#include <common_types.hpp> +#include <communication/distributed_context.hpp> +#include <hardware/node_info.hpp> +#include <load_balance.hpp> +#include <profiling/meter_manager.hpp> +#include <simulation.hpp> +#include <util/ioutil.hpp> + +#include "recipe.hpp" + +using namespace arb; + +int main(int argc, char** argv) { + try { + distributed_context context; + #ifdef ARB_HAVE_MPI + mpi::scoped_guard guard(&argc, &argv); + context = mpi_context(MPI_COMM_WORLD); + #endif + const bool is_root = context.id()==0; + + std::cout << util::mask_stream(is_root); + + bench_params params = read_options(argc, argv); + + std::cout << params << "\n"; + + util::meter_manager meters(&context); + meters.start(); + + // Create an instance of our recipe. + bench_recipe recipe(params); + meters.checkpoint("recipe-build"); + + // Make the domain decomposition for the model + auto node = arb::hw::get_node_info(); + auto decomp = arb::partition_load_balance(recipe, node, &context); + meters.checkpoint("domain-decomp"); + + // Construct the model. + arb::simulation sim(recipe, decomp, &context); + meters.checkpoint("model-build"); + + // Run the simulation for 100 ms, with time steps of 0.01 ms. + sim.run(params.duration, 0.01); + meters.checkpoint("model-run"); + + // write meters + auto report = util::make_meter_report(meters); + std::cout << report << "\n"; + + if (is_root==0) { + std::ofstream fid; + fid.exceptions(std::ios_base::badbit | std::ios_base::failbit); + fid.open("meters.json"); + fid << std::setw(1) << util::to_json(report) << "\n"; + } + + // output profile and diagnostic feedback + auto profile = util::profiler_summary(); + std::cout << profile << "\n"; + + std::cout << "there were " << sim.num_spikes() << " spikes\n"; + } + catch (std::exception& e) { + std::cerr << "exception caught running benchmark miniapp:\n" << e.what() << std::endl; + } +} diff --git a/example/bench/parameters.cpp b/example/bench/parameters.cpp new file mode 100644 index 00000000..dea52a70 --- /dev/null +++ b/example/bench/parameters.cpp @@ -0,0 +1,111 @@ +#include <exception> +#include <fstream> +#include <string> + +#include <util/optional.hpp> + +#include <json/json.hpp> + +#include "parameters.hpp" + +double bench_params::expected_advance_time() const { + return cell.realtime_ratio * duration*1e-3 * num_cells; +} +unsigned bench_params::expected_spikes() const { + return num_cells * duration*1e-3 * cell.spike_freq_hz; +} +unsigned bench_params::expected_spikes_per_interval() const { + return num_cells * network.min_delay*1e-3/2 * cell.spike_freq_hz; +} +unsigned bench_params::expected_events() const { + return expected_spikes() * network.fan_in; +} +unsigned bench_params::expected_events_per_interval() const { + return expected_spikes_per_interval() * network.fan_in; +} + +std::ostream& operator<<(std::ostream& o, const bench_params& p) { + o << "benchmark parameters:\n" + << " name: " << p.name << "\n" + << " num cells: " << p.num_cells << "\n" + << " duration: " << p.duration << " ms\n" + << " fan in: " << p.network.fan_in << " connections/cell\n" + << " min delay: " << p.network.min_delay << " ms\n" + << " spike freq: " << p.cell.spike_freq_hz << " Hz\n" + << " cell overhead: " << p.cell.realtime_ratio << " ms to advance 1 ms\n"; + o << "expected:\n" + << " cell advance: " << p.expected_advance_time() << " s\n" + << " spikes: " << p.expected_spikes() << "\n" + << " events: " << p.expected_events() << "\n" + << " spikes: " << p.expected_spikes_per_interval() << " per interval\n" + << " events: " << p.expected_events_per_interval()/p.num_cells << " per cell per interval"; + return o; +} + +using arb::util::optional; +using nlohmann::json; + +// Search a json object for an entry with a given name. +// If found, return the value and remove from json object. +template<typename T> +optional<T> extract(const char* name, json& j) { + auto it = j.find(name); + if (it==j.end()) { + return arb::util::nullopt; + } + T value = std::move(*it); + j.erase(name); + return std::move(value); +} + +bench_params read_options(int argc, char** argv) { + bench_params params; + if (argc<2) { + std::cout << "Using default parameters.\n"; + return params; + } + if (argc>2) { + throw std::runtime_error("More than command line one option not permitted."); + } + + std::string fname = argv[1]; + std::cout << "Loading parameters from file: " << fname << "\n"; + std::ifstream f(fname); + + if (!f.good()) { + throw std::runtime_error("Unable to open input parameter file: "+fname); + } + + nlohmann::json json; + json << f; + + if (auto o = extract<std::string>("name", json)) { + params.name = *o; + } + if (auto o = extract<unsigned>("num-cells", json)) { + params.num_cells = *o; + } + if (auto o = extract<double>("duration", json)) { + params.duration = *o; + } + if (auto o = extract<double>("min-delay", json)) { + params.network.min_delay = *o; + } + if (auto o = extract<unsigned>("fan-in", json)) { + params.network.fan_in = *o; + } + if (auto o = extract<double>("realtime-ratio", json)) { + params.cell.realtime_ratio = *o; + } + if (auto o = extract<double>("spike-frequency", json)) { + params.cell.spike_freq_hz = *o; + } + + for (auto it=json.begin(); it!=json.end(); ++it) { + std::cout << " Warning: unused input parameter: \"" << it.key() << "\"\n"; + } + std::cout << "\n"; + + return params; +} + diff --git a/example/bench/parameters.hpp b/example/bench/parameters.hpp new file mode 100644 index 00000000..52c0d164 --- /dev/null +++ b/example/bench/parameters.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include <ostream> +#include <string> + +#include <common_types.hpp> + +using arb::time_type; + +struct bench_params { + struct cell_params { + double spike_freq_hz = 10; // Frequency in hz that cell will generate (poisson) spikes. + double realtime_ratio = 0.1; // Integration speed relative to real time, e.g. 10 implies + // that a cell is integrated 10 times slower than real time. + }; + struct network_params { + unsigned fan_in = 5000; // Number of incoming connections on each cell. + double min_delay = 10; // Used as the delay on all connections. + }; + std::string name = "default"; // Name of the model. + unsigned num_cells = 1000; // Number of cells in model. + time_type duration = 100; // Simulation duration in ms. + + cell_params cell; // Cell parameters for all cells in model. + network_params network; // Description of the network. + + // Expected simulation performance properties based on model parameters. + + // Time to finish simulation if only cell overheads are counted. + double expected_advance_time() const; + // Total expected number of spikes generated by simulation. + unsigned expected_spikes() const; + // Expected number of spikes generated per min_delay/2 interval. + unsigned expected_spikes_per_interval() const; + // Expected number of post-synaptic events delivered over simulation. + unsigned expected_events() const; + // Expected number of post-synaptic events delivered per min_delay/2 interval. + unsigned expected_events_per_interval() const; +}; + +bench_params read_options(int argc, char** argv); + +std::ostream& operator<<(std::ostream& o, const bench_params& p); diff --git a/example/bench/readme.md b/example/bench/readme.md new file mode 100644 index 00000000..c6dbc2f9 --- /dev/null +++ b/example/bench/readme.md @@ -0,0 +1,57 @@ +# Artificial Infrastructure Benchmark + +This miniapp uses the `arb::benchmark_cell` to build a network of cells that +have predictable cell integration run time and spike generation patterns. It is +for benchmarking purposes only. + +The main application of this miniapp is to benchmark the Arbor simulation +architecture. The architecture is everything that isn't cell state update, +including + * Spike exchange. + * Event sorting and merging. + * Threading and MPI infrastructure. + +For example, the scaling behavior of the spike exchange can be studied as +factors such as fan-in, network minimum delay, spiking frequency and spiking +pattern are varied, without having to tweak parameters on a model that uses +"biologically realistic" cells such as LIF cells. + +## Usage + +The model can be configured using a json configuration file: + +``` +./bench.exe params.json +``` + +An example parameter file is: +``` +{ + "name": "small test", + "num-cells": 2000, + "duration": 100, + "fan-in": 10000, + "min-delay": 10, + "spike-frequency": 20, + "realtime-ratio": 0.1 +} +``` + +The parameters in the file: + * `name`: a string with a name for the benchmark. + * `num-cells`: the total number of cells in the model. The cell population + is assumed to be homogoneous, that is the `spike-frequency` and + `cell-overhead` parameters are the same for all cells. + * `duration`: the length of the simulated time interval, in ms. + * `fan-in`: the number of incoming connections on each cell. + * `min-delay`: the minimum delay of the network. + * `spike-frequency`: frequency of the independent Poisson processes that + generate spikes for each cell. + * `realtime-ratio`: the ratio between time taken to advance a single cell in + the simulation and the simulated time. For example, a value of 1 indicates + that the cell is simulated in real time, while a value of 0.1 indicates + that 10s can be simulated in a single second. + +The network is randomly connected with no self-connections and `fan-in` +incoming connections on each cell, with every connection having delay of +`min-delay`. diff --git a/example/bench/recipe.cpp b/example/bench/recipe.cpp new file mode 100644 index 00000000..c3358c22 --- /dev/null +++ b/example/bench/recipe.cpp @@ -0,0 +1,71 @@ +#include <random> + +#include <benchmark_cell.hpp> +#include <common_types.hpp> +#include <time_sequence.hpp> + +#include "recipe.hpp" + +cell_size_type bench_recipe::num_cells() const { + return params_.num_cells; +} + +arb::util::unique_any bench_recipe::get_cell_description(cell_gid_type gid) const { + using rng_type = std::mt19937_64; + arb::benchmark_cell cell; + cell.realtime_ratio = params_.cell.realtime_ratio; + + // The time_sequence of the cell produces the series of time points at + // which it will spike. We use a poisson_time_seq with a random sequence + // seeded with the gid. In this way, a cell's random stream depends only + // on its gid, and will hence give reproducable results when run with + // different MPI ranks and threads. + cell.time_sequence = + arb::poisson_time_seq<rng_type>( + rng_type(gid), 0, 1e-3*params_.cell.spike_freq_hz); + + return std::move(cell); +} + +arb::cell_kind bench_recipe::get_cell_kind(arb::cell_gid_type gid) const { + return arb::cell_kind::benchmark; +} + +std::vector<arb::cell_connection> bench_recipe::connections_on(cell_gid_type gid) const { + const auto n = params_.network.fan_in; + std::vector<arb::cell_connection> cons; + cons.reserve(n); + using rng_type = std::mt19937_64; + rng_type rng(gid); + + // Generate n incoming connections on this cell with random sources, where + // the source can't equal gid (i.e. no self-connections). + // We want a random distribution that will uniformly draw values from the + // union of the two ranges: [0, gid-1] AND [gid+1, num_cells-1]. + // To do this, we draw random values in the range [0, num_cells-2], then + // add 1 to values ≥ gid. + + std::uniform_int_distribution<cell_gid_type> dist(0, params_.num_cells-2); + for (unsigned i=0; i<n; ++i) { + // Draw random source and adjust to avoid self-connections if neccesary. + cell_gid_type src = dist(rng); + if (src>=gid) ++src; + // Note: target is {gid, 0}, i.e. the first (and only) target on the cell. + arb::cell_connection con({src, 0}, {gid, 0}, 1.f, params_.network.min_delay); + cons.push_back(con); + } + + return cons; +} + +cell_size_type bench_recipe::num_targets(cell_gid_type gid) const { + // Only one target, to which all incoming connections connect. + // This could be parameterized, in which case the connections + // generated in connections_on should end on random cell-local targets. + return 1; +} + +// one spike source per cell +cell_size_type bench_recipe::num_sources(cell_gid_type gid) const { + return 1; +} diff --git a/example/bench/recipe.hpp b/example/bench/recipe.hpp new file mode 100644 index 00000000..76a1a84f --- /dev/null +++ b/example/bench/recipe.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include <common_types.hpp> +#include <recipe.hpp> + +#include "parameters.hpp" + +using arb::cell_kind; +using arb::cell_gid_type; +using arb::cell_size_type; + +class bench_recipe: public arb::recipe { + bench_params params_; +public: + bench_recipe(bench_params p): params_(std::move(p)) {} + cell_size_type num_cells() const override; + arb::util::unique_any get_cell_description(cell_gid_type gid) const override; + arb::cell_kind get_cell_kind(arb::cell_gid_type gid) const override; + cell_size_type num_targets(cell_gid_type gid) const override; + cell_size_type num_sources(cell_gid_type gid) const override; + std::vector<arb::cell_connection> connections_on(cell_gid_type) const override; +}; + diff --git a/example/miniapp/miniapp.cpp b/example/miniapp/miniapp.cpp index 2aecfeed..943ad04d 100644 --- a/example/miniapp/miniapp.cpp +++ b/example/miniapp/miniapp.cpp @@ -8,7 +8,6 @@ #include <json/json.hpp> #include <common_types.hpp> -#include <communication/communicator.hpp> #include <communication/distributed_context.hpp> #include <cell.hpp> #include <hardware/gpu.hpp> diff --git a/example/miniapp/miniapp_recipes.cpp b/example/miniapp/miniapp_recipes.cpp index 16c88939..17e852cb 100644 --- a/example/miniapp/miniapp_recipes.cpp +++ b/example/miniapp/miniapp_recipes.cpp @@ -6,7 +6,7 @@ #include <cell.hpp> #include <event_generator.hpp> #include <morphology.hpp> -#include <spike_source_cell_group.hpp> +#include <spike_source_cell.hpp> #include <time_sequence.hpp> #include <util/debug.hpp> diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 11fe3e27..aad5e5ed 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ set(arbor_cxx_sources backends/multicore/mechanism.cpp backends/multicore/shared_state.cpp backends/multicore/stimulus.cpp + benchmark_cell_group.cpp builtin_mechanisms.cpp cell_group_factory.cpp common_types_io.cpp diff --git a/src/benchmark_cell.hpp b/src/benchmark_cell.hpp new file mode 100644 index 00000000..6d3c928c --- /dev/null +++ b/src/benchmark_cell.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include <time_sequence.hpp> + +namespace arb { + +// Cell description returned by recipe::cell_description(gid) for cells with +// recipe::cell_kind(gid) returning cell_kind::benchmark + +struct benchmark_cell { + // Describes the time points at which spikes are to be generated. + time_seq time_sequence; + + // Time taken in ms to advance the cell one ms of simulation time. + // If equal to 1, then a single cell can be advanced in realtime + double realtime_ratio; +}; + +} // namespace arb + + diff --git a/src/benchmark_cell_group.cpp b/src/benchmark_cell_group.cpp new file mode 100644 index 00000000..cd2237ba --- /dev/null +++ b/src/benchmark_cell_group.cpp @@ -0,0 +1,90 @@ +#include <chrono> +#include <exception> + +#include <cell_group.hpp> +#include <profiling/profiler.hpp> +#include <recipe.hpp> +#include <benchmark_cell.hpp> +#include <benchmark_cell_group.hpp> +#include <time_sequence.hpp> + +namespace arb { + +benchmark_cell_group::benchmark_cell_group(std::vector<cell_gid_type> gids, + const recipe& rec): + gids_(std::move(gids)) +{ + cells_.reserve(gids_.size()); + for (auto gid: gids_) { + cells_.push_back(util::any_cast<benchmark_cell>(rec.get_cell_description(gid))); + } + + reset(); +} + +void benchmark_cell_group::reset() { + t_ = 0; + + for (auto& c: cells_) { + c.time_sequence.reset(); + } + + clear_spikes(); +} + +cell_kind benchmark_cell_group::get_cell_kind() const { + return cell_kind::benchmark; +} + +void benchmark_cell_group::advance(epoch ep, + time_type dt, + const event_lane_subrange& event_lanes) +{ + using std::chrono::high_resolution_clock; + using duration_type = std::chrono::duration<double, std::micro>; + + PE(advance_bench_cell); + // Micro-seconds to advance in this epoch. + auto us = 1e3*(ep.tfinal-t_); + for (auto i: util::make_span(0, gids_.size())) { + auto& tseq = cells_[i].time_sequence; + // Expected time to complete epoch in micro seconds. + const double duration_us = cells_[i].realtime_ratio*us; + const auto gid = gids_[i]; + + // Start timer. + auto start = high_resolution_clock::now(); + + while (tseq.front()<ep.tfinal) { + spikes_.push_back({{gid, 0u}, tseq.front()}); + tseq.pop(); + } + + // Wait until the expected time to advance has elapsed. Use a busy-wait + // so that the resources of this thread are tied up until the interval + // has elapsed, to emulate a "real" cell. + while (duration_type(high_resolution_clock::now()-start).count() < duration_us); + } + t_ = ep.tfinal; + + PL(); +}; + +const std::vector<spike>& benchmark_cell_group::spikes() const { + return spikes_; +} + +void benchmark_cell_group::clear_spikes() { + spikes_.clear(); +} + +void benchmark_cell_group::add_sampler(sampler_association_handle h, + cell_member_predicate probe_ids, + schedule sched, + sampler_function fn, + sampling_policy policy) +{ + std::logic_error("A benchmark_cell group doen't support sampling of internal state!"); +} + +} // namespace arb diff --git a/src/benchmark_cell_group.hpp b/src/benchmark_cell_group.hpp new file mode 100644 index 00000000..536a7fa1 --- /dev/null +++ b/src/benchmark_cell_group.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include <benchmark_cell.hpp> +#include <cell_group.hpp> +#include <recipe.hpp> +#include <time_sequence.hpp> + +namespace arb { + +class benchmark_cell_group: public cell_group { +public: + benchmark_cell_group(std::vector<cell_gid_type> gids, const recipe& rec); + + cell_kind get_cell_kind() const override; + + void advance(epoch ep, time_type dt, const event_lane_subrange& event_lanes) override; + + void reset() override; + + void set_binning_policy(binning_kind policy, time_type bin_interval) override {} + + const std::vector<spike>& spikes() const override; + + void clear_spikes() override; + + void add_sampler(sampler_association_handle h, cell_member_predicate probe_ids, schedule sched, sampler_function fn, sampling_policy policy) override; + + void remove_sampler(sampler_association_handle h) override {} + + void remove_all_samplers() override {} + +private: + time_type t_; + + std::vector<benchmark_cell> cells_; + std::vector<spike> spikes_; + std::vector<cell_gid_type> gids_; +}; + +} // namespace arb + diff --git a/src/cell_group_factory.cpp b/src/cell_group_factory.cpp index 862dfdb0..4c613695 100644 --- a/src/cell_group_factory.cpp +++ b/src/cell_group_factory.cpp @@ -1,6 +1,7 @@ #include <vector> #include <backends.hpp> +#include <benchmark_cell_group.hpp> #include <cell_group.hpp> #include <domain_decomposition.hpp> #include <fvm_lowered_cell.hpp> @@ -23,6 +24,9 @@ cell_group_ptr cell_group_factory(const recipe& rec, const group_description& gr case cell_kind::lif_neuron: return make_cell_group<lif_cell_group>(group.gids, rec); + case cell_kind::benchmark: + return make_cell_group<benchmark_cell_group>(group.gids, rec); + default: throw std::runtime_error("unknown cell kind"); } diff --git a/src/common_types.hpp b/src/common_types.hpp index d74bf31e..c365883c 100644 --- a/src/common_types.hpp +++ b/src/common_types.hpp @@ -68,9 +68,10 @@ using sample_size_type = std::int32_t; // group equal kinds in the same cell group. enum class cell_kind { - cable1d_neuron, // Our own special mc neuron - lif_neuron, // Leaky-integrate and fire neuron - spike_source, // Cell that generates spikes at a user-supplied sequence of time points + cable1d_neuron, // Our own special mc neuron. + lif_neuron, // 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. }; } // namespace arb diff --git a/src/common_types_io.cpp b/src/common_types_io.cpp index 91360c0f..ada86a91 100644 --- a/src/common_types_io.cpp +++ b/src/common_types_io.cpp @@ -15,6 +15,8 @@ std::ostream& operator<<(std::ostream& o, arb::cell_kind k) { return o << "cable1d_neuron"; case arb::cell_kind::lif_neuron: return o << "lif_neuron"; + case arb::cell_kind::benchmark: + return o << "benchmark_cell"; } return o; } diff --git a/src/spike_source_cell.hpp b/src/spike_source_cell.hpp new file mode 100644 index 00000000..489d1300 --- /dev/null +++ b/src/spike_source_cell.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include <time_sequence.hpp> + +namespace arb { + +// Cell description returned by recipe::cell_description(gid) for cells with +// recipe::cell_kind(gid) returning cell_kind::spike_source + +struct spike_source_cell { + time_seq seq; +}; + +} // namespace arb diff --git a/tests/unit/test_lif_cell_group.cpp b/tests/unit/test_lif_cell_group.cpp index 43785e12..7c54b74d 100644 --- a/tests/unit/test_lif_cell_group.cpp +++ b/tests/unit/test_lif_cell_group.cpp @@ -6,7 +6,7 @@ #include <lif_cell_group.hpp> #include <load_balance.hpp> #include <simulation.hpp> -#include <spike_source_cell_group.hpp> +#include <spike_source_cell.hpp> #include <recipe.hpp> using namespace arb; -- GitLab