From a59121462dafabf59aefbb74a4cc0cea966c6499 Mon Sep 17 00:00:00 2001 From: Marcel Stimberg Date: Wed, 27 Feb 2019 15:55:34 +0100 Subject: [PATCH 1/4] Use an epsilon independent of dt when comparing times Fixes #1054 --- brian2/core/clocks.py | 19 ++++++++----------- brian2/core/network.py | 2 +- brian2/tests/test_clocks.py | 26 ++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/brian2/core/clocks.py b/brian2/core/clocks.py index d2f051dee..d6ecdf73c 100644 --- a/brian2/core/clocks.py +++ b/brian2/core/clocks.py @@ -36,8 +36,7 @@ def check_dt(new_dt, old_dt, target_t): ------ ValueError If using the new dt value would lead to a difference in the target - time of more than `Clock.epsilon_dt` times ``new_dt`` (by default, - 0.01% of the new dt). + time of more than `Clock.epsilon_dt` (by default, 10ns). Examples -------- @@ -52,7 +51,7 @@ def check_dt(new_dt, old_dt, target_t): old_t = np.int64(np.round(target_t / old_dt)) * old_dt new_t = np.int64(np.round(target_t / new_dt)) * new_dt error_t = target_t - if abs(new_t - old_t)/new_dt > Clock.epsilon_dt: + if abs(new_t - old_t) > Clock.epsilon_dt: raise ValueError(('Cannot set dt from {old} to {new}, the ' 'time {t} is not a multiple of ' '{new}').format(old=str(old_dt * second), @@ -74,10 +73,8 @@ class Clock(VariableOwner): Notes ----- Clocks are run in the same `Network.run` iteration if `~Clock.t` is the - same. The condition for two - clocks to be considered as having the same time is - ``abs(t1-t2) Date: Tue, 26 Mar 2019 16:12:21 +0100 Subject: [PATCH 2/4] Make standalone consistent with runtime with regard to clocks Also cast integers to 64 bit instead of 32 bit --- brian2/devices/cpp_standalone/brianlib/clocks.h | 10 +++++----- brian2/devices/cpp_standalone/templates/network.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/brian2/devices/cpp_standalone/brianlib/clocks.h b/brian2/devices/cpp_standalone/brianlib/clocks.h index b33168304..1617f98f9 100644 --- a/brian2/devices/cpp_standalone/brianlib/clocks.h +++ b/brian2/devices/cpp_standalone/brianlib/clocks.h @@ -19,7 +19,7 @@ class Clock double *dt; int64_t *timestep; double *t; - Clock(double _epsilon=1e-14) : epsilon(_epsilon) { i_end = 0;}; + Clock(double _epsilon=1e-8) : epsilon(_epsilon) { i_end = 0;}; inline void tick() { timestep[0] += 1; @@ -30,18 +30,18 @@ class Clock { int i_start = fround(start/dt[0]); double t_start = i_start*dt[0]; - if(t_start==start || fabs(t_start-start)<=epsilon*fabs(t_start)) + if(t_start==start || fabs(t_start-start)<=epsilon) { timestep[0] = i_start; } else { - timestep[0] = (int)ceil(start/dt[0]); + timestep[0] = (int64_t)ceil(start/dt[0]); } i_end = fround(end/dt[0]); double t_end = i_end*dt[0]; - if(!(t_end==end || fabs(t_end-end)<=epsilon*fabs(t_end))) + if(!(t_end==end || fabs(t_end-end)<=epsilon)) { - i_end = (int)ceil(end/dt[0]); + i_end = (int64_t)ceil(end/dt[0]); } } private: diff --git a/brian2/devices/cpp_standalone/templates/network.cpp b/brian2/devices/cpp_standalone/templates/network.cpp index 197d84696..6179c38dc 100644 --- a/brian2/devices/cpp_standalone/templates/network.cpp +++ b/brian2/devices/cpp_standalone/templates/network.cpp @@ -7,7 +7,7 @@ #include {{ openmp_pragma('include') }} -#define Clock_epsilon 1e-14 +#define Clock_epsilon 1e-8 double Network::_last_run_time = 0.0; double Network::_last_run_completed_fraction = 0.0; From 7bfa14c894f8b2274b844e879b677d83a52a304a Mon Sep 17 00:00:00 2001 From: Marcel Stimberg Date: Tue, 26 Mar 2019 17:26:40 +0100 Subject: [PATCH 3/4] Add a test for the > 32bit time steps issue fixed with previous commit --- brian2/tests/test_network.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/brian2/tests/test_network.py b/brian2/tests/test_network.py index 175440fe3..bed2b3679 100644 --- a/brian2/tests/test_network.py +++ b/brian2/tests/test_network.py @@ -1372,6 +1372,24 @@ def test_small_runs(): assert_allclose(mon_1.t_[:], mon_2.t_[:]) assert_allclose(mon_1.v_[:], mon_2.v_[:]) +@attr('standalone-compatible') +@with_setup(teardown=reinit_devices) +def test_long_run(): + defaultclock.dt = 0.1 * ms + group = NeuronGroup(1, 'x : 1') + group.run_regularly('x += 1') + # Timesteps are internally stored as 64bit integers, but previous versions + # converted them into 32bit integers along the way. We'll make sure that + # this is not the case and everything runs fine. To not actually run such a + # long simulation we manually advance the clock. + net = Network(group, name='network') + start_step = 2**31-5 + start_time = start_step*defaultclock.dt_ + with catch_logs() as l: + net.t_ = start_time # for runtime + device.insert_code('main', 'network.t = {};'.format(start_time)) # for standalone + net.run(6 * defaultclock.dt) + assert group.x == 6 @attr('codegen-independent') @with_setup(teardown=reinit_devices) @@ -1409,6 +1427,7 @@ def test_multiple_runs_function_change(): device.build(direct_call=False, **device.build_options) assert_equal(mon.v[0], [1, 2, 3, 4]) + if __name__ == '__main__': BrianLogger.log_level_warn() for t in [ @@ -1470,6 +1489,7 @@ def test_multiple_runs_function_change(): test_magic_scope, test_runtime_rounding, test_small_runs, + test_long_run, test_long_run_dt_change, test_multiple_runs_constant_change, test_multiple_runs_function_change From 355cbf98061f2ae4c4afdc1600f363c5d8c0979d Mon Sep 17 00:00:00 2001 From: Marcel Stimberg Date: Tue, 26 Mar 2019 18:40:03 +0100 Subject: [PATCH 4/4] Use the Clock's epsilon in standalone (instead of a hardcoded one) --- brian2/devices/cpp_standalone/templates/network.cpp | 4 +--- brian2/devices/cpp_standalone/templates/objects.cpp | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/brian2/devices/cpp_standalone/templates/network.cpp b/brian2/devices/cpp_standalone/templates/network.cpp index 6179c38dc..fad9301f6 100644 --- a/brian2/devices/cpp_standalone/templates/network.cpp +++ b/brian2/devices/cpp_standalone/templates/network.cpp @@ -7,8 +7,6 @@ #include {{ openmp_pragma('include') }} -#define Clock_epsilon 1e-8 - double Network::_last_run_time = 0.0; double Network::_last_run_completed_fraction = 0.0; @@ -158,7 +156,7 @@ Clock* Network::next_clocks() { Clock *clock = *i; double s = clock->t[0]; - if(s==t || fabs(s-t)<=Clock_epsilon) + if(s==t || fabs(s-t) <= clock->epsilon) curclocks.insert(clock); } return minclock; diff --git a/brian2/devices/cpp_standalone/templates/objects.cpp b/brian2/devices/cpp_standalone/templates/objects.cpp index a6fc2d5a0..c9d9f74a3 100644 --- a/brian2/devices/cpp_standalone/templates/objects.cpp +++ b/brian2/devices/cpp_standalone/templates/objects.cpp @@ -59,7 +59,7 @@ SynapticPathway {{path.name}}( //////////////// clocks /////////////////// {% for clock in clocks | sort(attribute='name') %} -Clock {{clock.name}}; // attributes will be set in run.cpp +Clock {{clock.name}}({{clock.epsilon_dt}}); // attributes will be set in run.cpp {% endfor %} {% if profiled_codeobjects is defined %}