Skip to content
Snippets Groups Projects
Commit 6f6a2db6 authored by Ben Cumming's avatar Ben Cumming Committed by Sam Yates
Browse files

Move event_binner out of cell_group.hpp (#229)

* Move `event_binner` class to own header file `event_binner.hpp` with implementation in `event_binner.cpp`.
* Move `event_binner` unit tests to own source file `test_event_binner.cpp`.
parent c579fa19
No related branches found
No related tags found
No related merge requests found
set(BASE_SOURCES
common_types_io.cpp
cell.cpp
event_binner.cpp
morphology.cpp
parameter_list.cpp
profiling/memory_meter.cpp
......
......@@ -8,6 +8,7 @@
#include <algorithms.hpp>
#include <cell.hpp>
#include <common_types.hpp>
#include <event_binner.hpp>
#include <event_queue.hpp>
#include <spike.hpp>
#include <util/debug.hpp>
......@@ -19,76 +20,6 @@
namespace nest {
namespace mc {
enum class binning_kind {
none,
regular, // => round time down to multiple of binning interval.
following, // => round times down to previous event if within binning interval.
};
class event_binner {
public:
using time_type = spike::time_type;
void reset() {
last_event_times_.clear();
}
event_binner(): policy_(binning_kind::none), bin_interval_(0) {}
event_binner(binning_kind policy, time_type bin_interval):
policy_(policy), bin_interval_(bin_interval)
{}
// Determine binned time for an event based on policy.
// If `t_min` is specified, the binned time will be no lower than `t_min`.
// Otherwise the returned binned time will be less than or equal to the parameter `t`,
// and within `bin_interval_`.
time_type bin(cell_gid_type id, time_type t, time_type t_min = std::numeric_limits<time_type>::lowest()) {
time_type t_binned = t;
switch (policy_) {
case binning_kind::none:
break;
case binning_kind::regular:
if (bin_interval_>0) {
t_binned = std::floor(t/bin_interval_)*bin_interval_;
}
break;
case binning_kind::following:
if (auto last_t = last_event_time(id)) {
if (t-*last_t<bin_interval_) {
t_binned = *last_t;
}
}
update_last_event_time(id, t_binned);
break;
default:
throw std::logic_error("unrecognized binning policy");
}
return std::max(t_binned, t_min);
}
private:
binning_kind policy_;
// Interval in which event times can be aliased.
time_type bin_interval_;
// (Consider replacing this with a vector-backed store.)
std::unordered_map<cell_gid_type, time_type> last_event_times_;
util::optional<time_type> last_event_time(cell_gid_type id) {
auto it = last_event_times_.find(id);
return it==last_event_times_.end()? util::nothing: util::just(it->second);
}
void update_last_event_time(cell_gid_type id, time_type t) {
last_event_times_[id] = t;
}
};
template <typename LoweredCell>
class cell_group {
public:
......
#include <algorithm>
#include <cmath>
#include <limits>
#include <stdexcept>
#include <unordered_map>
#include <common_types.hpp>
#include <event_binner.hpp>
#include <spike.hpp>
#include <util/optional.hpp>
namespace nest {
namespace mc {
void event_binner::reset() {
last_event_times_.clear();
}
event_binner::time_type
event_binner::bin(cell_gid_type id, time_type t, time_type t_min) {
time_type t_binned = t;
switch (policy_) {
case binning_kind::none:
break;
case binning_kind::regular:
if (bin_interval_>0) {
t_binned = std::floor(t/bin_interval_)*bin_interval_;
}
break;
case binning_kind::following:
if (auto last_t = last_event_time(id)) {
if (t-*last_t<bin_interval_) {
t_binned = *last_t;
}
}
update_last_event_time(id, t_binned);
break;
default:
throw std::logic_error("unrecognized binning policy");
}
return std::max(t_binned, t_min);
}
util::optional<event_binner::time_type>
event_binner::last_event_time(cell_gid_type id) {
auto it = last_event_times_.find(id);
return it==last_event_times_.end()? util::nothing: util::just(it->second);
}
void event_binner::update_last_event_time(cell_gid_type id, time_type t) {
last_event_times_[id] = t;
}
} // namespace mc
} // namespace nest
#pragma once
#include <limits>
#include <unordered_map>
#include <common_types.hpp>
#include <spike.hpp>
#include <util/optional.hpp>
namespace nest {
namespace mc {
enum class binning_kind {
none,
regular, // => round time down to multiple of binning interval.
following, // => round times down to previous event if within binning interval.
};
class event_binner {
public:
using time_type = spike::time_type;
event_binner(): policy_(binning_kind::none), bin_interval_(0) {}
event_binner(binning_kind policy, time_type bin_interval):
policy_(policy), bin_interval_(bin_interval)
{}
void reset();
// Determine binned time for an event based on policy.
// If `t_min` is specified, the binned time will be no lower than `t_min`.
// Otherwise the returned binned time will be less than or equal to the parameter `t`,
// and within `bin_interval_`.
time_type bin(cell_gid_type id,
time_type t,
time_type t_min = std::numeric_limits<time_type>::lowest());
private:
binning_kind policy_;
// Interval in which event times can be aliased.
time_type bin_interval_;
// (Consider replacing this with a vector-backed store.)
std::unordered_map<cell_gid_type, time_type> last_event_times_;
util::optional<time_type> last_event_time(cell_gid_type id);
void update_last_event_time(cell_gid_type id, time_type t);
};
} // namespace mc
} // namespace nest
......@@ -38,6 +38,7 @@ set(TEST_SOURCES
test_cycle.cpp
test_either.cpp
test_event_queue.cpp
test_event_binner.cpp
test_filter.cpp
test_fvm_multi.cpp
test_cell_group.cpp
......
......@@ -60,101 +60,3 @@ TEST(cell_group, sources) {
}
}
}
TEST(cell_group, event_binner) {
using testing::seq_almost_eq;
std::pair<cell_gid_type, float> binning_test_data[] = {
{ 11, 0.50 },
{ 12, 0.70 },
{ 14, 0.73 },
{ 11, 1.80 },
{ 12, 1.83 },
{ 11, 1.90 },
{ 11, 2.00 },
{ 14, 2.00 },
{ 11, 2.10 },
{ 14, 2.30 }
};
std::unordered_map<cell_gid_type, std::vector<float>> ev_times;
std::vector<float> expected;
auto run_binner = [&](event_binner&& binner) {
ev_times.clear();
for (auto p: binning_test_data) {
ev_times[p.first].push_back(binner.bin(p.first, p.second));
}
};
run_binner(event_binner{binning_kind::none, 0});
EXPECT_TRUE(seq_almost_eq<float>(ev_times[11], (float []){0.50, 1.80, 1.90, 2.00, 2.10}));
EXPECT_TRUE(seq_almost_eq<float>(ev_times[12], (float []){0.70, 1.83}));
EXPECT_TRUE(ev_times[13].empty());
EXPECT_TRUE(seq_almost_eq<float>(ev_times[14], (float []){0.73, 2.00, 2.30}));
run_binner(event_binner{binning_kind::regular, 0.25});
EXPECT_TRUE(seq_almost_eq<float>(ev_times[11], (float []){0.50, 1.75, 1.75, 2.00, 2.00}));
EXPECT_TRUE(seq_almost_eq<float>(ev_times[12], (float []){0.50, 1.75}));
EXPECT_TRUE(ev_times[13].empty());
EXPECT_TRUE(seq_almost_eq<float>(ev_times[14], (float []){0.50, 2.00, 2.25}));
run_binner(event_binner{binning_kind::following, 0.25});
EXPECT_TRUE(seq_almost_eq<float>(ev_times[11], (float []){0.50, 1.80, 1.80, 1.80, 2.10}));
EXPECT_TRUE(seq_almost_eq<float>(ev_times[12], (float []){0.70, 1.83}));
EXPECT_TRUE(ev_times[13].empty());
EXPECT_TRUE(seq_almost_eq<float>(ev_times[14], (float []){0.73, 2.00, 2.30}));
}
TEST(cell_group, event_binner_with_min) {
using testing::seq_almost_eq;
struct test_time {
float time;
float t_min;
};
test_time test_data[] = {
{0.8f, 1.0f},
{1.6f, 1.0f},
{1.9f, 1.8f},
{2.0f, 1.8f},
{2.2f, 1.8f}
};
std::vector<float> times;
auto run_binner = [&](event_binner&& binner, bool use_min) {
times.clear();
for (auto p: test_data) {
if (use_min) {
times.push_back(binner.bin(0, p.time, p.t_min));
}
else {
times.push_back(binner.bin(0, p.time));
}
}
};
// 'none' binning
run_binner(event_binner{binning_kind::none, 0.5}, false);
EXPECT_TRUE(seq_almost_eq<float>(times, (float []){0.8, 1.6, 1.9, 2.0, 2.2}));
run_binner(event_binner{binning_kind::none, 0.5}, true);
EXPECT_TRUE(seq_almost_eq<float>(times, (float []){1.0, 1.6, 1.9, 2.0, 2.2}));
// 'regular' binning
run_binner(event_binner{binning_kind::regular, 0.5}, false);
EXPECT_TRUE(seq_almost_eq<float>(times, (float []){0.5, 1.5, 1.5, 2.0, 2.0}));
run_binner(event_binner{binning_kind::regular, 0.5}, true);
EXPECT_TRUE(seq_almost_eq<float>(times, (float []){1.0, 1.5, 1.8, 2.0, 2.0}));
// 'following' binning
run_binner(event_binner{binning_kind::following, 0.5}, false);
EXPECT_TRUE(seq_almost_eq<float>(times, (float []){0.8, 1.6, 1.6, 1.6, 2.2}));
run_binner(event_binner{binning_kind::following, 0.5}, true);
EXPECT_TRUE(seq_almost_eq<float>(times, (float []){1.0, 1.6, 1.8, 1.8, 2.2}));
}
#include "../gtest.h"
#include <event_binner.hpp>
#include "common.hpp"
using namespace nest::mc;
TEST(event_binner, basic) {
using testing::seq_almost_eq;
std::pair<cell_gid_type, float> binning_test_data[] = {
{ 11, 0.50 },
{ 12, 0.70 },
{ 14, 0.73 },
{ 11, 1.80 },
{ 12, 1.83 },
{ 11, 1.90 },
{ 11, 2.00 },
{ 14, 2.00 },
{ 11, 2.10 },
{ 14, 2.30 }
};
std::unordered_map<cell_gid_type, std::vector<float>> ev_times;
std::vector<float> expected;
auto run_binner = [&](event_binner&& binner) {
ev_times.clear();
for (auto p: binning_test_data) {
ev_times[p.first].push_back(binner.bin(p.first, p.second));
}
};
run_binner(event_binner{binning_kind::none, 0});
EXPECT_TRUE(seq_almost_eq<float>(ev_times[11], (float []){0.50, 1.80, 1.90, 2.00, 2.10}));
EXPECT_TRUE(seq_almost_eq<float>(ev_times[12], (float []){0.70, 1.83}));
EXPECT_TRUE(ev_times[13].empty());
EXPECT_TRUE(seq_almost_eq<float>(ev_times[14], (float []){0.73, 2.00, 2.30}));
run_binner(event_binner{binning_kind::regular, 0.25});
EXPECT_TRUE(seq_almost_eq<float>(ev_times[11], (float []){0.50, 1.75, 1.75, 2.00, 2.00}));
EXPECT_TRUE(seq_almost_eq<float>(ev_times[12], (float []){0.50, 1.75}));
EXPECT_TRUE(ev_times[13].empty());
EXPECT_TRUE(seq_almost_eq<float>(ev_times[14], (float []){0.50, 2.00, 2.25}));
run_binner(event_binner{binning_kind::following, 0.25});
EXPECT_TRUE(seq_almost_eq<float>(ev_times[11], (float []){0.50, 1.80, 1.80, 1.80, 2.10}));
EXPECT_TRUE(seq_almost_eq<float>(ev_times[12], (float []){0.70, 1.83}));
EXPECT_TRUE(ev_times[13].empty());
EXPECT_TRUE(seq_almost_eq<float>(ev_times[14], (float []){0.73, 2.00, 2.30}));
}
TEST(event_binner, with_min) {
using testing::seq_almost_eq;
struct test_time {
float time;
float t_min;
};
test_time test_data[] = {
{0.8f, 1.0f},
{1.6f, 1.0f},
{1.9f, 1.8f},
{2.0f, 1.8f},
{2.2f, 1.8f}
};
std::vector<float> times;
auto run_binner = [&](event_binner&& binner, bool use_min) {
times.clear();
for (auto p: test_data) {
if (use_min) {
times.push_back(binner.bin(0, p.time, p.t_min));
}
else {
times.push_back(binner.bin(0, p.time));
}
}
};
// 'none' binning
run_binner(event_binner{binning_kind::none, 0.5}, false);
EXPECT_TRUE(seq_almost_eq<float>(times, (float []){0.8, 1.6, 1.9, 2.0, 2.2}));
run_binner(event_binner{binning_kind::none, 0.5}, true);
EXPECT_TRUE(seq_almost_eq<float>(times, (float []){1.0, 1.6, 1.9, 2.0, 2.2}));
// 'regular' binning
run_binner(event_binner{binning_kind::regular, 0.5}, false);
EXPECT_TRUE(seq_almost_eq<float>(times, (float []){0.5, 1.5, 1.5, 2.0, 2.0}));
run_binner(event_binner{binning_kind::regular, 0.5}, true);
EXPECT_TRUE(seq_almost_eq<float>(times, (float []){1.0, 1.5, 1.8, 2.0, 2.0}));
// 'following' binning
run_binner(event_binner{binning_kind::following, 0.5}, false);
EXPECT_TRUE(seq_almost_eq<float>(times, (float []){0.8, 1.6, 1.6, 1.6, 2.2}));
run_binner(event_binner{binning_kind::following, 0.5}, true);
EXPECT_TRUE(seq_almost_eq<float>(times, (float []){1.0, 1.6, 1.8, 1.8, 2.2}));
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment