From 3ddff699932471ce8843b7ab7c87a1af4ff2c7ed Mon Sep 17 00:00:00 2001 From: Sam Yates <halfflat@gmail.com> Date: Wed, 28 Sep 2016 21:23:16 +0200 Subject: [PATCH] Order synapse compartment indices. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Present an ordered sequence of compartment indices to point processes (synapses) on creation in fvm_multicell. Required for GPU implementations. Also: * Change target handle for fvm_multicell to refer to actual (rather than offset) mechanism index. * Split out the range/sequence utilities that were accumulating in `util/range.hpp` into their own header `util/rangeutil.hpp`. Possibly `sequtil.hpp` is a better name… * Add range/sequence-friendly `util::sort` and `util::sort_by`. The latter sorts based on comparing a derived key. * Add `util::assign_by`. Assigns after applying a transformation. * Fix `canonical_view` for non-const ranges/sequences. * Unit tests for `assign`, `assign_by`, `sort`, `sort_by`. --- src/fvm_multicell.hpp | 47 ++++++-- src/util/range.hpp | 53 +--------- src/util/rangeutil.hpp | 100 ++++++++++++++++++ src/util/sentinel.hpp | 6 +- .../test_mpi_gather_all.cpp | 2 +- tests/unit/test_cell_group.cpp | 2 +- tests/unit/test_fvm.cpp | 2 +- tests/unit/test_fvm_multi.cpp | 2 +- tests/unit/test_probe.cpp | 2 +- tests/unit/test_range.cpp | 33 +++++- tests/validation/validate_soma.cpp | 2 +- tests/validation/validate_synapses.cpp | 2 +- 12 files changed, 185 insertions(+), 68 deletions(-) create mode 100644 src/util/rangeutil.hpp diff --git a/src/fvm_multicell.hpp b/src/fvm_multicell.hpp index 72d22b25..9e5fd060 100644 --- a/src/fvm_multicell.hpp +++ b/src/fvm_multicell.hpp @@ -22,6 +22,7 @@ #include <util/debug.hpp> #include <util/meta.hpp> #include <util/partition.hpp> +#include <util/rangeutil.hpp> #include <util/span.hpp> #include <vector/include/Vector.hpp> @@ -67,7 +68,7 @@ public: void reset(); void deliver_event(target_handle h, value_type weight) { - mechanisms_[synapse_base_+h.first]->net_receive(h.second, weight); + mechanisms_[h.first]->net_receive(h.second, weight); } value_type detector_voltage(detector_handle h) const { @@ -300,6 +301,8 @@ void fvm_multicell<T, I>::initialize( Targets& target_handles, Probes& probe_handles) { + using util::assign_by; + using util::sort_by; using util::transform_view; using util::make_partition; using util::make_span; @@ -388,11 +391,13 @@ void fvm_multicell<T, I>::initialize( auto& map_entry = syn_mech_map[syn_mech_index]; size_type syn_comp = comp_ival.first+find_compartment_index(syn.location, graph); - size_type syn_index = map_entry.size(); +// size_type syn_index = map_entry.size(); map_entry.push_back(syn_comp); +/* *target_hi++ = target_handle{syn_mech_index, syn_index}; ++targets_count; +*/ } // normalize capacitance across cell @@ -434,11 +439,6 @@ void fvm_multicell<T, I>::initialize( } } - // confirm write-parameters were appropriately sized - EXPECTS(detectors_size==detectors_count); - EXPECTS(targets_size==targets_count); - EXPECTS(probes_size==probes_count); - // initalize matrix matrix_ = matrix_type(group_parent_index); @@ -461,12 +461,43 @@ void fvm_multicell<T, I>::initialize( synapse_base_ = mechanisms_.size(); for (const auto& syni: syn_mech_indices) { const auto& mech_name = syni.first; + size_type mech_index = mechanisms_.size(); + + auto comp_indices = syn_mech_map[syni.second]; + size_type n_indices = size(comp_indices); + + // sort indices but keep track of their original order for assigning + // target handles + + using index_pair = std::pair<cell_lid_type, size_type>; + auto compartment_index = [](index_pair x) { return x.first; }; + auto target_index = [](index_pair x) { return x.second; }; + + std::vector<index_pair> permute; + assign_by(permute, make_span(0u, n_indices), + [&](size_type i) { return index_pair(comp_indices[i], i); }); - auto mech = mechanism_catalogue::make(mech_name, voltage_, current_, syn_mech_map[syni.second]); + sort_by(permute, compartment_index); + assign_by(comp_indices, permute, compartment_index); + + // make target handles + std::vector<target_handle> handles(n_indices); + for (auto i: make_span(0u, n_indices)) { + handles[target_index(permute[i])] = {mech_index, i}; + } + target_hi = std::copy_n(std::begin(handles), n_indices, target_hi); + targets_count += n_indices; + + auto mech = mechanism_catalogue::make(mech_name, voltage_, current_, comp_indices); mech->set_areas(cv_areas_); mechanisms_.push_back(std::move(mech)); } + // confirm write-parameters were appropriately sized + EXPECTS(detectors_size==detectors_count); + EXPECTS(targets_size==targets_count); + EXPECTS(probes_size==probes_count); + // build the ion species for(auto ion : mechanisms::ion_kinds()) { // find the compartment indexes of all compartments that have a diff --git a/src/util/range.hpp b/src/util/range.hpp index df5625d0..4fa8f25f 100644 --- a/src/util/range.hpp +++ b/src/util/range.hpp @@ -152,60 +152,17 @@ range<U, V> make_range(const U& left, const V& right) { } template <typename Seq> -auto canonical_view(const Seq& s) -> +auto canonical_view(Seq& s) -> range<sentinel_iterator_t<decltype(std::begin(s)), decltype(std::end(s))>> { return {make_sentinel_iterator(std::begin(s), std::end(s)), make_sentinel_end(std::begin(s), std::end(s))}; } -/* - * Present a single item as a range - */ - -template <typename T> -range<T*> singleton_view(T& item) { - return {&item, &item+1}; -} - -template <typename T> -range<const T*> singleton_view(const T& item) { - return {&item, &item+1}; -} - -/* - * Range/container utility functions - */ - -template <typename Container, typename Seq> -Container& append(Container &c, const Seq& seq) { - auto canon = canonical_view(seq); - c.insert(c.end(), std::begin(canon), std::end(canon)); - return c; -} - -template <typename AssignableContainer, typename Seq> -AssignableContainer& assign(AssignableContainer& c, const Seq& seq) { - auto canon = canonical_view(seq); - c.assign(std::begin(canon), std::end(canon)); - return c; -} - template <typename Seq> -range<typename sequence_traits<Seq>::iterator_type, typename sequence_traits<Seq>::sentinel_type> -range_view(Seq& seq) { - return make_range(std::begin(seq), std::end(seq)); -} - -template < - typename Seq, - typename Iter = typename sequence_traits<Seq>::iterator_type, - typename Size = typename sequence_traits<Seq>::size_type -> -enable_if_t<is_forward_iterator<Iter>::value, range<Iter>> -subrange_view(Seq& seq, Size bi, Size ei) { - Iter b = std::next(std::begin(seq), bi); - Iter e = std::next(b, ei-bi); - return make_range(b, e); +auto canonical_view(const Seq& s) -> + range<sentinel_iterator_t<decltype(std::begin(s)), decltype(std::end(s))>> +{ + return {make_sentinel_iterator(std::begin(s), std::end(s)), make_sentinel_end(std::begin(s), std::end(s))}; } } // namespace util diff --git a/src/util/rangeutil.hpp b/src/util/rangeutil.hpp new file mode 100644 index 00000000..95200b9c --- /dev/null +++ b/src/util/rangeutil.hpp @@ -0,0 +1,100 @@ +#pragma once + +/* + * Sequence and container utilities compatible + * with ranges. + */ + +#include <iterator> + +#include <util/meta.hpp> +#include <util/range.hpp> +#include <util/transform.hpp> + +namespace nest { +namespace mc { +namespace util { + +// Present a single item as a range + +template <typename T> +range<T*> singleton_view(T& item) { + return {&item, &item+1}; +} + +template <typename T> +range<const T*> singleton_view(const T& item) { + return {&item, &item+1}; +} + +// Non-owning views and subviews + +template <typename Seq> +range<typename sequence_traits<Seq>::iterator_type, typename sequence_traits<Seq>::sentinel_type> +range_view(Seq& seq) { + return make_range(std::begin(seq), std::end(seq)); +} + +template < + typename Seq, + typename Iter = typename sequence_traits<Seq>::iterator_type, + typename Size = typename sequence_traits<Seq>::size_type +> +enable_if_t<is_forward_iterator<Iter>::value, range<Iter>> +subrange_view(Seq& seq, Size bi, Size ei) { + Iter b = std::next(std::begin(seq), bi); + Iter e = std::next(b, ei-bi); + return make_range(b, e); +} + +// Append sequence to a container + +template <typename Container, typename Seq> +Container& append(Container &c, const Seq& seq) { + auto canon = canonical_view(seq); + c.insert(c.end(), std::begin(canon), std::end(canon)); + return c; +} + +// Assign sequence to a container + +template <typename AssignableContainer, typename Seq> +AssignableContainer& assign(AssignableContainer& c, const Seq& seq) { + auto canon = canonical_view(seq); + c.assign(std::begin(canon), std::end(canon)); + return c; +} + +// Assign sequence to a container with transform `proj` + +template <typename AssignableContainer, typename Seq, typename Proj> +AssignableContainer& assign_by(AssignableContainer& c, const Seq& seq, const Proj& proj) { + auto canon = canonical_view(transform_view(seq, proj)); + c.assign(std::begin(canon), std::end(canon)); + return c; +} + +// Sort in-place + +template <typename Seq> +void sort(Seq& seq) { + auto canon = canonical_view(seq); + std::sort(std::begin(canon), std::end(canon)); +} + +// Sort in-place by projection `proj` + +template <typename Seq, typename Proj> +void sort_by(Seq& seq, const Proj& proj) { + using value_type = typename sequence_traits<Seq>::value_type; + auto canon = canonical_view(seq); + + std::sort(std::begin(canon), std::end(canon), + [&proj](const value_type& a, const value_type& b) { + return proj(a) < proj(b); + }); +} + +} // namespace util +} // namespace mc +} // namespace nest diff --git a/src/util/sentinel.hpp b/src/util/sentinel.hpp index 8d08ecd4..eebbfd23 100644 --- a/src/util/sentinel.hpp +++ b/src/util/sentinel.hpp @@ -179,6 +179,6 @@ sentinel_iterator_t<I, S> make_sentinel_end(const I& i, const S& s) { return sentinel_iterator_t<I, S>(s); } -} -} -} +} // namespace util +} // namespace mc +} // namespace nest diff --git a/tests/global_communication/test_mpi_gather_all.cpp b/tests/global_communication/test_mpi_gather_all.cpp index 50fbf405..b5ecf73f 100644 --- a/tests/global_communication/test_mpi_gather_all.cpp +++ b/tests/global_communication/test_mpi_gather_all.cpp @@ -7,7 +7,7 @@ #include <communication/mpi_global_policy.hpp> #include <communication/mpi.hpp> -#include <util/range.hpp> +#include <util/rangeutil.hpp> using namespace nest::mc; using namespace nest::mc::communication; diff --git a/tests/unit/test_cell_group.cpp b/tests/unit/test_cell_group.cpp index 9b3f6e6a..a18b9b88 100644 --- a/tests/unit/test_cell_group.cpp +++ b/tests/unit/test_cell_group.cpp @@ -3,7 +3,7 @@ #include <cell_group.hpp> #include <common_types.hpp> #include <fvm_cell.hpp> -#include <util/range.hpp> +#include <util/rangeutil.hpp> #include "../test_common_cells.hpp" diff --git a/tests/unit/test_fvm.cpp b/tests/unit/test_fvm.cpp index 6d067182..45c038bf 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/rangeutil.hpp> #include "../test_common_cells.hpp" #include "../test_util.hpp" diff --git a/tests/unit/test_fvm_multi.cpp b/tests/unit/test_fvm_multi.cpp index fbd4de99..df835641 100644 --- a/tests/unit/test_fvm_multi.cpp +++ b/tests/unit/test_fvm_multi.cpp @@ -5,7 +5,7 @@ #include <common_types.hpp> #include <cell.hpp> #include <fvm_multicell.hpp> -#include <util/range.hpp> +#include <util/rangeutil.hpp> #include "../test_util.hpp" #include "../test_common_cells.hpp" diff --git a/tests/unit/test_probe.cpp b/tests/unit/test_probe.cpp index cf7562c9..1a2d7f15 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/rangeutil.hpp> TEST(probe, instantiation) { diff --git a/tests/unit/test_range.cpp b/tests/unit/test_range.cpp index 118a4dc1..3cf2893e 100644 --- a/tests/unit/test_range.cpp +++ b/tests/unit/test_range.cpp @@ -14,6 +14,7 @@ #include <util/counter.hpp> #include <util/meta.hpp> #include <util/range.hpp> +#include <util/rangeutil.hpp> #include <util/sentinel.hpp> using namespace nest::mc; @@ -212,7 +213,6 @@ TYPED_TEST_P(counter_range, extreme_size) { auto range = util::make_range(l, r); EXPECT_FALSE(range.empty()); EXPECT_EQ(std::numeric_limits<unsigned_int_type>::max(), range.size()); - } @@ -226,7 +226,6 @@ TYPED_TEST_P(counter_range, size) { auto range = util::make_range(l, r); EXPECT_FALSE(range.empty()); EXPECT_EQ(6u, range.size()); - } TYPED_TEST_P(counter_range, at) { @@ -270,6 +269,36 @@ using signed_int_types = ::testing::Types<signed char, short, INSTANTIATE_TYPED_TEST_CASE_P(int_types, counter_range, int_types); +TEST(range, assign) { + const char *cstr = "howdy"; + std::string text = "pardner"; + + util::assign(text, util::make_range(cstr, null_terminated)); + EXPECT_EQ("howdy", text); + + const std::vector<char> vstr = {'x', 'y', 'z', 'z', 'y'}; + util::assign(text, vstr); + EXPECT_EQ("xyzzy", text); + + util::assign_by(text, vstr, + [](char c) { return c=='z'? '1': '0'; }); + EXPECT_EQ("00110", text); +} + +TEST(range, sort) { + char cstr[] = "howdy"; + + auto cstr_range = util::make_range(std::begin(cstr), null_terminated); + + // simple sort + util::sort(cstr_range); + EXPECT_EQ(std::string("dhowy"), cstr); + + // reverse sort by transform c to -c + util::sort_by(cstr_range, [](char c) { return -c; }); + EXPECT_EQ(std::string("ywohd"), cstr); +} + #ifdef WITH_TBB TEST(range, tbb_split) { diff --git a/tests/validation/validate_soma.cpp b/tests/validation/validate_soma.cpp index 8952ba30..05ff783e 100644 --- a/tests/validation/validate_soma.cpp +++ b/tests/validation/validate_soma.cpp @@ -4,7 +4,7 @@ #include <common_types.hpp> #include <cell.hpp> #include <fvm_cell.hpp> -#include <util/range.hpp> +#include <util/rangeutil.hpp> #include "gtest.h" diff --git a/tests/validation/validate_synapses.cpp b/tests/validation/validate_synapses.cpp index d1de3df0..62b91841 100644 --- a/tests/validation/validate_synapses.cpp +++ b/tests/validation/validate_synapses.cpp @@ -8,7 +8,7 @@ #include <cell_group.hpp> #include <fvm_cell.hpp> #include <mechanism_interface.hpp> -#include <util/range.hpp> +#include <util/rangeutil.hpp> #include "gtest.h" #include "../test_util.hpp" -- GitLab