From 781481ce06fc4f6795441f2f8270a689a37021a4 Mon Sep 17 00:00:00 2001
From: Ben Cumming <bcumming@cscs.ch>
Date: Mon, 23 May 2016 11:17:40 +0200
Subject: [PATCH] single compartment validation test

---
 nrn/soma.py             |  8 +++--
 src/fvm.hpp             |  1 -
 src/util.hpp            |  4 +--
 tests/util.hpp          | 79 +++++++++++++++++++++++++++++++++++------
 tests/validate_soma.cpp | 12 +++++--
 5 files changed, 85 insertions(+), 19 deletions(-)

diff --git a/nrn/soma.py b/nrn/soma.py
index 0e73ad62..65661b6f 100644
--- a/nrn/soma.py
+++ b/nrn/soma.py
@@ -1,3 +1,4 @@
+from timeit import default_timer as timer
 import os.path
 from matplotlib import pyplot
 import numpy as np
@@ -47,14 +48,17 @@ if args.plot :
 h.tstop = simdur
 
 # run neuron with multiple dt
+start = timer()
 results = []
-#for dt in [0.02, 0.01, 0.005, 0.0005, 0.0001]:
-for dt in [0.02, 0.0001]:
+for dt in [0.02, 0.01, 0.001, 0.0001]:
     h.dt = dt
     h.run()
     results.append({"dt": dt, "spikes": s_vec.to_python()})
     if args.plot :
         pyplot.plot(t_vec, v_vec, label='neuron ' + str(dt))
+end = timer()
+
+print "took ", end-start, " seconds"
 
 # save the spike info as in json format
 fp = open('soma.json', 'w')
diff --git a/src/fvm.hpp b/src/fvm.hpp
index 833c8cd1..69d23d0d 100644
--- a/src/fvm.hpp
+++ b/src/fvm.hpp
@@ -375,7 +375,6 @@ fvm_cell<T, I>::fvm_cell(nest::mc::cell const& cell)
     // add the stimulii
     for(const auto& stim : cell.stimulii()) {
         auto idx = find_compartment_index(stim.first, graph);
-        std::cout << "adding stimulus at compartment " << idx << "\n";
         stimulii_.push_back( {idx, stim.second} );
     }
 }
diff --git a/src/util.hpp b/src/util.hpp
index 3dae43f8..706ab7da 100644
--- a/src/util.hpp
+++ b/src/util.hpp
@@ -28,7 +28,7 @@ operator << (std::ostream &o, std::vector<T>const& v)
 {
     o << "[";
     for(auto const& i: v) {
-        o << i << ", ";
+        o << i << ",";
     }
     o << "]";
     return o;
@@ -39,7 +39,7 @@ std::ostream& print(std::ostream &o, std::vector<T>const& v)
 {
     o << "[";
     for(auto const& i: v) {
-        o << i << ", ";
+        o << i << ",";
     }
     o << "]";
     return o;
diff --git a/tests/util.hpp b/tests/util.hpp
index 873bc205..19b0bfbb 100644
--- a/tests/util.hpp
+++ b/tests/util.hpp
@@ -3,6 +3,10 @@
 #include <string>
 #include <vector>
 
+#include <cmath>
+
+#include <util.hpp>
+
 [[gnu::unused]] static
 void write_vis_file(const std::string& fname, std::vector<std::vector<double>> values)
 {
@@ -34,24 +38,77 @@ std::vector<T> find_spikes(std::vector<T> const& v, T threshold, T dt)
     if(v.size()<2) {
         return {};
     }
-    bool is_spiking = v[0] >= threshold;
-    auto it = v.begin() + 1;
 
     std::vector<T> times;
     for(auto i=1u; i<v.size(); ++i) {
-        if(is_spiking && v[i]<threshold) {
-            is_spiking = false;
+        if(v[i]>=threshold && v[i-1]<threshold) {
+            auto pos = (threshold-v[i-1]) / (v[i]-v[i-1]);
+            times.push_back((i-1+pos)*dt);
         }
-        else {
-            if(*it>=threshold) {
-                is_spiking = true;
-                auto pos = (threshold-v[i-1]) / (v[i]-v[i-1]);
-                times.push_back((i-1+pos)*dt);
-            }
+    }
+
+    return times;
+}
+
+struct spike_comparison {
+    double min = std::numeric_limits<double>::quiet_NaN();
+    double max = std::numeric_limits<double>::quiet_NaN();
+    double mean = std::numeric_limits<double>::quiet_NaN();
+    double rms = std::numeric_limits<double>::quiet_NaN();
+    std::vector<double> diff;
+
+    // check whether initialized (i.e. has valid results)
+    bool is_valid() const {
+        return min == min;
+    }
+
+    // return maximum relative error
+    double max_relative_error() const {
+        if(!is_valid()) {
+            return std::numeric_limits<double>::quiet_NaN();
         }
+
+        return *std::max_element(diff.begin(), diff.end());
+    }
+};
+
+std::ostream&
+operator<< (std::ostream& o, spike_comparison const& spikes)
+{
+    return o << "[" << spikes.min << ", " << spikes.max << "], "
+             << "mean " << spikes.mean << ", rms " << spikes.rms
+             << ", diffs " << spikes.diff;
+}
+
+template <typename T>
+spike_comparison compare_spikes(std::vector<T> const& spikes, std::vector<T> const& baseline)
+{
+    spike_comparison c;
+
+    // return default initialized (all NaN) if number of spikes differs
+    if(spikes.size() != baseline.size()) {
+        return c;
     }
 
+    c.min  = std::numeric_limits<double>::max();
+    c.max  = 0.;
+    c.mean = 0.;
+    c.rms  = 0.;
 
-    return times;
+    auto n = spikes.size();
+    for(auto i=0u; i<n; ++i) {
+        auto error = std::fabs(spikes[i] - baseline[i]);
+        c.min = std::min(c.min, error);
+        c.max = std::max(c.max, error);
+        c.mean += error;
+        c.rms += error*error;
+        // relative difference
+        c.diff.push_back(error/baseline[i]);
+    }
+
+    c.mean /= n;
+    c.rms = std::sqrt(c.rms/n);
+
+    return c;
 }
 
diff --git a/tests/validate_soma.cpp b/tests/validate_soma.cpp
index 70fe80c3..a90e0fa8 100644
--- a/tests/validate_soma.cpp
+++ b/tests/validate_soma.cpp
@@ -69,9 +69,15 @@ TEST(soma, resolutions)
             v.push_back(model.voltage()[0]);
         }
 
-        std::cout << v << "\n";
-        auto spike_times = find_spikes(v, 0., dt);
-        std::cout << "dt " << dt << " : spikes " << spike_times << "\n";
+        // get the spike times from the NEST MC and NEURON simulations respectively
+        auto nst_spike_times = find_spikes(v, 0., dt);
+        auto nrn_spike_times = run["spikes"].get<std::vector<double>>();
+        auto comparison = compare_spikes(nst_spike_times, nrn_spike_times);
+
+        // Assert that relative error is less than 1%.
+        // For a 100 ms simulation this asserts that the difference between NEST and NEURON
+        // at a given time step size is less than 1 ms.
+        EXPECT_TRUE(comparison.max_relative_error()*100. < 1.);
     }
 }
 
-- 
GitLab