diff --git a/src/cell_group.hpp b/src/cell_group.hpp index a52dc36d953bc35a557ad0688b9d94f7be46639c..b0bbccbcb46757afe72695413cbc525b0d86eb9a 100644 --- a/src/cell_group.hpp +++ b/src/cell_group.hpp @@ -9,7 +9,7 @@ #include <event_queue.hpp> #include <spike.hpp> #include <spike_source.hpp> -//#include <util/singleton.hpp> +#include <util/singleton.hpp> #include <profiling/profiler.hpp> diff --git a/src/fvm_cell.hpp b/src/fvm_cell.hpp index 5335c8129a5963d8d288094116442c431279a2f7..83fd8b769974f8447b3930d8f2477a0732d139fe 100644 --- a/src/fvm_cell.hpp +++ b/src/fvm_cell.hpp @@ -82,7 +82,7 @@ * * Resets state to initial conditiions and sets * internal simulation time to 0. - * + * * `lowered.advance(value_type dt)` * * Advanece simulation state by `dt` (value in @@ -325,7 +325,7 @@ void fvm_cell<T, I>::initialize( face_alpha_ = vector_type{ncomp, T{0}}; cv_capacitance_ = vector_type{ncomp, T{0}}; current_ = vector_type{ncomp, T{0}}; - voltage_ = vector_type{ncomp, T{0}}; + voltage_ = vector_type{ncomp, T{resting_potential_}}; using util::left; using util::right; diff --git a/src/util/debug.cpp b/src/util/debug.cpp index 51c646bbcb38b1722e40766f4886a957094bd1bb..f2f039708a860c7fcb9ec65db7345b608a019dd9 100644 --- a/src/util/debug.cpp +++ b/src/util/debug.cpp @@ -14,8 +14,11 @@ namespace util { std::mutex global_debug_cerr_mutex; -bool failed_assertion(const char* assertion, const char* file, - int line, const char* func) +bool abort_on_failed_assertion( + const char* assertion, + const char* file, + int line, + const char* func) { // Explicit flush, as we can't assume default buffering semantics on stderr/cerr, // and abort() might not flush streams. @@ -26,8 +29,13 @@ bool failed_assertion(const char* assertion, const char* file, return false; } -std::ostream& debug_emit_trace_leader(std::ostream& out, const char* file, - int line, const char* varlist) +failed_assertion_handler_t global_failed_assertion_handler = abort_on_failed_assertion; + +std::ostream& debug_emit_trace_leader( + std::ostream& out, + const char* file, + int line, + const char* varlist) { iosfmt_guard guard(out); diff --git a/src/util/debug.hpp b/src/util/debug.hpp index f258b37123755c7ec7a7324641249ed35290e8bb..be2b3d51e4d7bf9b5d4c5fb9b7c78855c5e4b4c5 100644 --- a/src/util/debug.hpp +++ b/src/util/debug.hpp @@ -10,7 +10,17 @@ namespace nest { namespace mc { namespace util { -bool failed_assertion(const char* assertion, const char* file, int line, const char* func); +using failed_assertion_handler_t = + bool (*)(const char* assertion, const char* file, int line, const char* func); + +bool abort_on_failed_assertion(const char* assertion, const char* file, int line, const char* func); +inline bool ignore_failed_assertion(const char*, const char*, int, const char*) { + return false; +} + +// defaults to abort_on_failed_assertion; +extern failed_assertion_handler_t global_failed_assertion_handler; + std::ostream& debug_emit_trace_leader(std::ostream& out, const char* file, int line, const char* varlist); inline void debug_emit(std::ostream& out) { @@ -66,7 +76,7 @@ void debug_emit_trace(const char* file, int line, const char* varlist, const Arg #define EXPECTS(condition) \ (void)((condition) || \ - nest::mc::util::failed_assertion(#condition, __FILE__, __LINE__, DEBUG_FUNCTION_NAME)) + nest::mc::util::global_failed_assertion_handler(#condition, __FILE__, __LINE__, DEBUG_FUNCTION_NAME)) #else #define EXPECTS(condition) #endif // def WITH_ASSERTIONS diff --git a/src/util/iterutil.hpp b/src/util/iterutil.hpp index 7d7d12b85c7ebe8588c98aba8842532ff074cd34..6a9fd159ffe9f75eddf57f2adb5c4d42cb9e7861 100644 --- a/src/util/iterutil.hpp +++ b/src/util/iterutil.hpp @@ -40,7 +40,7 @@ upto(I iter, E end) { template <typename I, typename E> enable_if_t<is_bidirectional_iterator<E>::value && std::is_constructible<I, E>::value, I> upto(I iter, E end) { - return I{std::prev(end)}; + return iter==I{end}? iter: I{std::prev(end)}; } /* diff --git a/src/util/meta.hpp b/src/util/meta.hpp index e949e251dc2f92101555cd7845931728c5b19f0b..9961969541a05b9faa93a88d252457efc301d91d 100644 --- a/src/util/meta.hpp +++ b/src/util/meta.hpp @@ -2,6 +2,7 @@ /* Type utilities and convenience expressions. */ +#include <cstddef> #include <iterator> #include <cstddef> #include <type_traits> @@ -55,6 +56,12 @@ struct sequence_traits { using const_sentinel = decltype(cend(std::declval<Seq&>())); }; +template <typename X> +std::size_t size(const X& x) { return x.size(); } + +template <typename X, std::size_t N> +constexpr std::size_t size(X (&)[N]) { return N; } + // Convenience short cuts template <typename T> diff --git a/src/util/partition.hpp b/src/util/partition.hpp index a78bb8f5d78056b7afce318969a8e178791efef8..b35f81de8fa1f40217e331dd96e09a11eba1c174 100644 --- a/src/util/partition.hpp +++ b/src/util/partition.hpp @@ -75,7 +75,7 @@ public: private: either<bool, std::string> is_valid() const { if (!std::is_sorted(left.get(), right.get())) { - return "offsets are not monotonically increasing"; + return std::string("offsets are not monotonically increasing"); } else { return true; diff --git a/src/util/range.hpp b/src/util/range.hpp index 72b977bdb019d14be71d8cb04963f7235562fe7b..7777444d0f65d7feebc03f8f3e988a234462cdf6 100644 --- a/src/util/range.hpp +++ b/src/util/range.hpp @@ -48,18 +48,20 @@ struct range { using difference_type = typename std::iterator_traits<iterator>::difference_type; using size_type = typename std::make_unsigned<difference_type>::type; using value_type = typename std::iterator_traits<iterator>::value_type; + using reference = typename std::iterator_traits<iterator>::reference; using const_reference = const value_type&; - using reference = const_reference; - U left; - S right; + iterator left; + sentinel right; range() = default; range(const range&) = default; range(range&&) = default; template <typename U1, typename U2> - range(U1&& l, U2&& r): left(std::forward<U1>(l)), right(std::forward<U2>(r)) {} + range(U1&& l, U2&& r): + left(std::forward<U1>(l)), right(std::forward<U2>(r)) + {} range& operator=(const range&) = default; range& operator=(range&&) = default; @@ -78,11 +80,11 @@ struct range { return std::distance(begin(), end()); } - size_type max_size() const { return std::numeric_limits<size_type>::max(); } + constexpr size_type max_size() const { return std::numeric_limits<size_type>::max(); } - void swap(range<U>& b) { - std::swap(left, b.left); - std::swap(right, b.right); + void swap(range& other) { + std::swap(left, other.left); + std::swap(right, other.right); } decltype(*left) front() const { return *left; } @@ -90,13 +92,13 @@ struct range { decltype(*left) back() const { return *upto(left, right); } template <typename V = iterator> - enable_if_t<is_random_access_iterator<V>::value, value_type> + enable_if_t<is_random_access_iterator<V>::value, decltype(*left)> operator[](difference_type n) const { return *std::next(begin(), n); } template <typename V = iterator> - enable_if_t<is_random_access_iterator<V>::value, value_type> + enable_if_t<is_random_access_iterator<V>::value, decltype(*left)> at(difference_type n) const { if (size_type(n) >= size()) { throw std::out_of_range("out of range in range"); @@ -105,16 +107,24 @@ struct range { } #ifdef WITH_TBB - template <typename V = iterator, - typename = enable_if_t<is_forward_iterator<V>::value>> - range(range& r, tbb::split): left(r.left), right(r.right) { + template < + typename V = iterator, + typename = enable_if_t<is_forward_iterator<V>::value> + > + range(range& r, tbb::split): + left(r.left), right(r.right) + { std::advance(left, r.size()/2u); r.right = left; } - template <typename V = iterator, - typename = enable_if_t<is_forward_iterator<V>::value>> - range(range& r, tbb::proportional_split p): left(r.left), right(r.right) { + template < + typename V = iterator, + typename = enable_if_t<is_forward_iterator<V>::value> + > + range(range& r, tbb::proportional_split p): + left(r.left), right(r.right) + { size_type i = (r.size()*p.left())/(p.left()+p.right()); if (i<1) { i = 1; @@ -145,28 +155,28 @@ range<U, V> make_range(const U& left, const V& right) { */ template <typename I, typename S> class sentinel_iterator { - nest::mc::util::either<I, S> e; + nest::mc::util::either<I, S> e_; - bool is_sentinel() const { return e.index()!=0; } + bool is_sentinel() const { return e_.index()!=0; } I& iter() { EXPECTS(!is_sentinel()); - return e.template unsafe_get<0>(); + return e_.template unsafe_get<0>(); } const I& iter() const { EXPECTS(!is_sentinel()); - return e.template unsafe_get<0>(); + return e_.template unsafe_get<0>(); } S& sentinel() { EXPECTS(is_sentinel()); - return e.template unsafe_get<1>(); + return e_.template unsafe_get<1>(); } const S& sentinel() const { EXPECTS(is_sentinel()); - return e.template unsafe_get<1>(); + return e_.template unsafe_get<1>(); } public: @@ -176,10 +186,10 @@ public: using reference = typename std::iterator_traits<I>::reference; using iterator_category = typename std::iterator_traits<I>::iterator_category; - sentinel_iterator(I i): e(i) {} + sentinel_iterator(I i): e_(i) {} template <typename V = S, typename = enable_if_t<!std::is_same<I, V>::value>> - sentinel_iterator(S i): e(i) {} + sentinel_iterator(S i): e_(i) {} sentinel_iterator() = default; sentinel_iterator(const sentinel_iterator&) = default; @@ -192,7 +202,7 @@ public: auto operator*() const -> decltype(*iter()) { return *iter(); } - I operator->() const { return e.template ptr<0>(); } + I operator->() const { return e_.template ptr<0>(); } sentinel_iterator& operator++() { ++iter(); @@ -298,9 +308,9 @@ sentinel_iterator_t<I, S> make_sentinel_end(const I& i, const S& s) { template <typename Seq> auto canonical_view(const Seq& s) -> - range<sentinel_iterator_t<decltype(s.begin()), decltype(s.end())>> + range<sentinel_iterator_t<decltype(std::begin(s)), decltype(std::end(s))>> { - return {make_sentinel_iterator(s.begin(), s.end()), make_sentinel_end(s.begin(), s.end())}; + return {make_sentinel_iterator(std::begin(s), std::end(s)), make_sentinel_end(std::begin(s), std::end(s))}; } /* diff --git a/src/util/span.hpp b/src/util/span.hpp index c296a59aedaef78e9a1ae2e0395d8f936002d82d..06cdfa36924324c589af8a16412d8c167fb0429f 100644 --- a/src/util/span.hpp +++ b/src/util/span.hpp @@ -24,7 +24,7 @@ span<typename std::common_type<I, J>::type> make_span(I left, J right) { template <typename I, typename J> span<typename std::common_type<I, J>::type> make_span(std::pair<I, J> interval) { - return span<typename std::common_type<I, J>::type>(interval.left, interval.right); + return span<typename std::common_type<I, J>::type>(interval.first, interval.second); } diff --git a/src/util/transform.hpp b/src/util/transform.hpp index dd66bd25070e7bb971b47b77d262f939b96f8e37..f7d9fed04c721f09611757c84c54e5f13cd15942 100644 --- a/src/util/transform.hpp +++ b/src/util/transform.hpp @@ -29,7 +29,7 @@ class transform_iterator: public iterator_adaptor<transform_iterator<I, F>, I> { const I& inner() const { return inner_; } I& inner() { return inner_; } - using inner_value_type = decay_t<decltype(*inner_)>; + using inner_value_type = util::decay_t<decltype(*inner_)>; public: using typename base::difference_type; @@ -70,12 +70,12 @@ public: bool operator==(const Sentinel& s) const { return inner_==s; } template <typename Sentinel> - bool operator!=(const Sentinel& s) const { return inner_!=s; } + bool operator!=(const Sentinel& s) const { return !(inner_==s); } }; template <typename I, typename F> -transform_iterator<I, decay_t<F>> make_transform_iterator(const I& i, const F& f) { - return transform_iterator<I, decay_t<F>>(i, f); +transform_iterator<I, util::decay_t<F>> make_transform_iterator(const I& i, const F& f) { + return transform_iterator<I, util::decay_t<F>>(i, f); } template < @@ -85,7 +85,7 @@ template < typename seq_csent = typename sequence_traits<Seq>::const_sentinel, typename = enable_if_t<std::is_same<seq_citer, seq_csent>::value> > -range<transform_iterator<seq_citer, decay_t<F>>> +range<transform_iterator<seq_citer, util::decay_t<F>>> transform_view(const Seq& s, const F& f) { return {make_transform_iterator(cbegin(s), f), make_transform_iterator(cend(s), f)}; } @@ -98,7 +98,7 @@ template < typename seq_csent = typename sequence_traits<Seq>::const_sentinel, typename = enable_if_t<!std::is_same<seq_citer, seq_csent>::value> > -range<transform_iterator<seq_citer, decay_t<F>>, seq_csent> +range<transform_iterator<seq_citer, util::decay_t<F>>, seq_csent> transform_view(const Seq& s, const F& f) { return {make_transform_iterator(cbegin(s), f), cend(s)}; } diff --git a/tests/unit/test_fvm.cpp b/tests/unit/test_fvm.cpp index ae7f0b5a3c15c01fc2c952c3ca84ff860bd10294..39fdb6c837f71820c3fd6981f9be2bc38cee91c8 100644 --- a/tests/unit/test_fvm.cpp +++ b/tests/unit/test_fvm.cpp @@ -5,7 +5,7 @@ #include <common_types.hpp> #include <cell.hpp> #include <fvm_cell.hpp> -#include <util/range.hpp> +#include <util/singleton.hpp> #include "../test_util.hpp" diff --git a/tests/unit/test_partition.cpp b/tests/unit/test_partition.cpp index e5e8cfe4cbcbf2b8255e67031f3dd11e918f8a78..edfb95d1021ea452c1701d9a84ef2341ed17e48f 100644 --- a/tests/unit/test_partition.cpp +++ b/tests/unit/test_partition.cpp @@ -1,8 +1,12 @@ #include "gtest.h" +#include <array> #include <forward_list> +#include <string> #include <vector> +#include <util/debug.hpp> +#include <util/nop.hpp> #include <util/partition.hpp> using namespace nest::mc; @@ -30,6 +34,28 @@ TEST(partition, partition_view) { EXPECT_EQ(ends_expected, ends); } +TEST(partition, short_partition_view) { + int two_divs[] = {10, 15}; + EXPECT_EQ(1u, util::partition_view(two_divs).size()); + + int one_div[] = {10}; + EXPECT_EQ(0u, util::partition_view(one_div).size()); + + std::array<int, 0> zero_divs; + EXPECT_EQ(0u, util::partition_view(zero_divs).size()); +} + +TEST(partition, check_monotonicity) { + // override any EXPECTS checks in partition + util::global_failed_assertion_handler = util::ignore_failed_assertion; + + int divs_ok[] = {1, 2, 2, 3, 3}; + EXPECT_NO_THROW(util::partition_view(divs_ok).validate()); + + int divs_bad[] = {3, 2, 1}; + EXPECT_THROW(util::partition_view(divs_bad).validate(), util::invalid_partition); +} + TEST(partition, partition_view_find) { std::vector<double> divs = { 1, 2.5, 3, 5.5 }; double eps = 0.1; @@ -48,6 +74,13 @@ TEST(partition, partition_view_find) { EXPECT_EQ(divs[2], p.find(divs[1]+eps)->second); } +TEST(partition, partition_view_non_numeric) { + std::string divs[] = { "a", "dictionary", "of", "sorted", "words" }; + auto p = util::partition_view(divs); + + EXPECT_EQ("dictionary", p.find("elephant")->first); +} + TEST(partition, make_partition_in_place) { unsigned sizes[] = { 7, 3, 0, 2 }; unsigned part_store[util::size(sizes)+1]; @@ -67,9 +100,39 @@ TEST(partition, make_partition_in_place) { EXPECT_EQ(std::make_pair(1u, 3u), p[1]); EXPECT_EQ(std::make_pair(3u, 3u), p[2]); EXPECT_EQ(std::make_pair(3u, 3u), p[3]); + + // with longer sizes sequence + unsigned long_sizes[] = {1, 2, 3, 4, 5, 6}; + p = util::make_partition(util::partition_in_place, part_store, long_sizes, 0u); + ASSERT_EQ(4u, p.size()); + EXPECT_EQ(std::make_pair(0u, 1u), p[0]); + EXPECT_EQ(std::make_pair(1u, 3u), p[1]); + EXPECT_EQ(std::make_pair(3u, 6u), p[2]); + EXPECT_EQ(std::make_pair(6u, 10u), p[3]); + + // with empty sizes sequence + std::array<unsigned, 0> no_sizes; + p = util::make_partition(util::partition_in_place, part_store, no_sizes, 17u); + ASSERT_EQ(4u, p.size()); + EXPECT_EQ(std::make_pair(17u, 17u), p[0]); + EXPECT_EQ(std::make_pair(17u, 17u), p[1]); + EXPECT_EQ(std::make_pair(17u, 17u), p[2]); + EXPECT_EQ(std::make_pair(17u, 17u), p[3]); + + // with short partition containers + unsigned part_store_one[1]; + p = util::make_partition(util::partition_in_place, part_store_one, sizes, 10u); + ASSERT_EQ(0u, p.size()); + ASSERT_TRUE(p.empty()); + + std::array<unsigned,0> part_store_zero; + p = util::make_partition(util::partition_in_place, part_store_zero, sizes, 10u); + ASSERT_EQ(0u, p.size()); + ASSERT_TRUE(p.empty()); } TEST(partition, make_partition) { + // (also tests differing types for sizes and divisiosn) unsigned sizes[] = { 7, 3, 0, 2 }; std::forward_list<double> part_store = { 100.3 }; diff --git a/tests/unit/test_probe.cpp b/tests/unit/test_probe.cpp index cf7562c94fbf9ddcf33e16308d418dbdddd0ebf7..13a12ad6bd067ed91b37f2867f22b0f6405ba89a 100644 --- a/tests/unit/test_probe.cpp +++ b/tests/unit/test_probe.cpp @@ -3,7 +3,7 @@ #include <common_types.hpp> #include <cell.hpp> #include <fvm_cell.hpp> -#include <util/range.hpp> +#include <util/singleton.hpp> TEST(probe, instantiation) { diff --git a/tests/unit/test_range.cpp b/tests/unit/test_range.cpp index 99f2fa0b6cede34e88274956fcebb769addb969f..7847212e3f0b0546c1f974dbdebab3303a15ea1d 100644 --- a/tests/unit/test_range.cpp +++ b/tests/unit/test_range.cpp @@ -37,6 +37,9 @@ TEST(range, list_iterator) { } EXPECT_EQ(check, sum); + + auto sum2 = std::accumulate(s.begin(), s.end(), 0); + EXPECT_EQ(check, sum2); } TEST(range, pointer) { @@ -45,6 +48,11 @@ TEST(range, pointer) { int r = 5; util::range<int *> s(&xs[l], &xs[r]); + auto s_deduced = util::make_range(xs+l, xs+r); + + EXPECT_TRUE((std::is_same<decltype(s), decltype(s_deduced)>::value)); + EXPECT_EQ(s.left, s_deduced.left); + EXPECT_EQ(s.right, s_deduced.right); EXPECT_EQ(3u, s.size()); @@ -70,6 +78,16 @@ TEST(range, input_iterator) { EXPECT_TRUE(std::equal(s.begin(), s.end(), &nums[0])); } +TEST(range, const_iterator) { + std::vector<int> xs = { 1, 2, 3, 4, 5 }; + auto r = util::make_range(xs.begin(), xs.end()); + EXPECT_TRUE((std::is_same<int&, decltype(r.front())>::value)); + + const auto& xs_const = xs; + auto r_const = util::make_range(xs_const.begin(), xs_const.end()); + EXPECT_TRUE((std::is_same<const int&, decltype(r_const.front())>::value)); +} + struct null_terminated_t { bool operator==(const char *p) const { return !*p; } bool operator!=(const char *p) const { return !!*p; } diff --git a/tests/unit/test_span.cpp b/tests/unit/test_span.cpp index 35729cafd495811b6351299c5fa7edaeb5150377..edb55f7d65e7f0321add82de6e0879143050fede 100644 --- a/tests/unit/test_span.cpp +++ b/tests/unit/test_span.cpp @@ -5,6 +5,7 @@ #include <list> #include <numeric> #include <type_traits> +#include <utility> #include <util/span.hpp> @@ -59,3 +60,34 @@ TEST(span, int_iterators) { EXPECT_EQ(sum, (a+b-1)*(b-a)/2); } +TEST(span, make_span) { + auto s_empty = util::make_span(3, 3); + EXPECT_TRUE(s_empty.empty()); + + { + auto s = util::make_span((short)3, (unsigned long long)10); + auto first = s.front(); + auto last = s.back(); + + EXPECT_EQ(3u, first); + EXPECT_EQ(9u, last); + + EXPECT_TRUE((std::is_same<decltype(first), decltype(last)>::value)); + EXPECT_TRUE((std::is_same<unsigned long long, decltype(first)>::value)); + } + + { + // type abuse! should promote bool to long in span. + std::pair<long, bool> bounds(-3, false); + auto s = util::make_span(bounds); + auto first = s.front(); + auto last = s.back(); + + EXPECT_EQ(-3, first); + EXPECT_EQ(-1, last); + + EXPECT_TRUE((std::is_same<decltype(first), decltype(last)>::value)); + EXPECT_TRUE((std::is_same<long, decltype(first)>::value)); + } +} +