diff --git a/arbor/morph/em_morphology.cpp b/arbor/morph/em_morphology.cpp index 6e482c7e062e68eefc4545e0129b7441e1aa7042..ba6d275131217ae32a7b175c25e893ee0fb23e31 100644 --- a/arbor/morph/em_morphology.cpp +++ b/arbor/morph/em_morphology.cpp @@ -1,4 +1,5 @@ #include <mutex> +#include <stack> #include <vector> #include <arbor/morph/error.hpp> @@ -141,4 +142,40 @@ mlocation em_morphology::canonicalize(mlocation loc) const { return loc; } +mlocation_list em_morphology::minset(const mlocation_list& in) const { + mlocation_list L; + + std::stack<msize_t> stack; + + // All root branches must be searched. + for (auto c: morph_.branch_children(mnpos)) { + stack.push(c); + } + + // Depth-first traversal of the branch tree. + while (!stack.empty()) { + auto branch = stack.top(); + stack.pop(); + + // Search for a location on branch. + auto it = std::lower_bound(in.begin(), in.end(), mlocation{branch, 0}); + + // If found, insert to the minset and skip the rest of this sub-tree. + if (it!=in.end() && it->branch==branch) { + L.push_back(*it); + continue; + } + + // No location on this branch, so continue searching in this sub-tree. + for (auto c: morph_.branch_children(branch)) { + stack.push(c); + } + } + + util::sort(L); + + return L; +} + } // namespace arb + diff --git a/arbor/morph/em_morphology.hpp b/arbor/morph/em_morphology.hpp index 5b2f3dc4a5fab0ed403bb998f7d07352372907a7..94aa5b0fe783551928d3506d5cdc5910d2ea0d5b 100644 --- a/arbor/morph/em_morphology.hpp +++ b/arbor/morph/em_morphology.hpp @@ -34,6 +34,8 @@ public: // representation of loc. // If include_loc is false, the input location is excluded from the result. mlocation_list cover(mlocation, bool include_loc=true) const; + + mlocation_list minset(const mlocation_list&) const; }; } // namespace arb diff --git a/test/unit/test_em_morphology.cpp b/test/unit/test_em_morphology.cpp index c724f5d1f9b772df2893d1c1a7e71a13ceccf094..39528837f92c891ecc1f141e6ff71de20dcac907 100644 --- a/test/unit/test_em_morphology.cpp +++ b/test/unit/test_em_morphology.cpp @@ -187,6 +187,57 @@ TEST(em_morphology, cover) { } } +TEST(em_morphology, minset) { + using pvec = std::vector<arb::msize_t>; + using svec = std::vector<arb::msample>; + using ll = arb::mlocation_list; + constexpr auto npos = arb::mnpos; + + // Eight samples + // no-sphere sphere + // sample branch branch + // 0 0 0 + // 1 3 0 1 1 2 + // 2 4 0 1 1 2 + // 5 6 2 3 3 4 + // 7 3 4 + pvec parents = {npos, 0, 1, 0, 3, 4, 4, 6}; + svec samples = { + {{ 0, 0, 0, 2}, 3}, + {{ 10, 0, 0, 2}, 3}, + {{100, 0, 0, 2}, 3}, + {{ 0, 10, 0, 2}, 3}, + {{ 0,100, 0, 2}, 3}, + {{100,100, 0, 2}, 3}, + {{ 0,200, 0, 2}, 3}, + {{ 0,300, 0, 2}, 3}, + }; + arb::sample_tree sm(samples, parents); + { + arb::em_morphology em(arb::morphology(sm, false)); + + EXPECT_EQ((ll{}), em.minset(ll{})); + EXPECT_EQ((ll{{2,0.1}}), em.minset(ll{{2,0.1}})); + EXPECT_EQ((ll{{0,0.5}, {1,0.5}}), em.minset(ll{{0,0.5}, {1,0.5}})); + EXPECT_EQ((ll{{0,0.5}}), em.minset(ll{{0,0.5}})); + EXPECT_EQ((ll{{0,0}, {1,0}}), em.minset(ll{{0,0}, {0,0.5}, {1,0}, {1,0.5}})); + EXPECT_EQ((ll{{0,0}, {1,0.5}}), em.minset(ll{{0,0}, {0,0.5}, {1,0.5}, {2,0.5}})); + EXPECT_EQ((ll{{0,0}, {2,0.5}}), em.minset(ll{{0,0}, {0,0.5}, {2,0.5}})); + EXPECT_EQ((ll{{0,0}, {2,0.5}, {3,0}}), em.minset(ll{{0,0}, {0,0.5}, {2,0.5}, {3,0}, {3,1}})); + EXPECT_EQ((ll{{0,0}, {1,0}}), em.minset(ll{{0,0}, {0,0.5}, {1,0}, {2,0.5}, {3,0}, {3,1}})); + } + { + arb::em_morphology em(arb::morphology(sm, true)); + + EXPECT_EQ((ll{}), em.minset(ll{})); + EXPECT_EQ((ll{{2,0.1}}), em.minset(ll{{2,0.1}})); + EXPECT_EQ((ll{{0,0.5}}), em.minset(ll{{0,0.5}, {1,0.5}})); + EXPECT_EQ((ll{{0,0.5}}), em.minset(ll{{0,0.5}})); + EXPECT_EQ((ll{{0,0}}), em.minset(ll{{0,0}, {0,0.5}, {1,0}, {1,0.5}})); + EXPECT_EQ((ll{{1,0.5}, {3,0.1}, {4,0.5}}), em.minset(ll{{1,0.5}, {1,1}, {3,0.1}, {4,0.5}, {4,0.7}})); + } +} + // Test expressions that describe location sets (locset). TEST(locset, expressions) { auto root = arb::ls::root(); @@ -625,3 +676,4 @@ TEST(region, thingify) { EXPECT_EQ(thingify(join(lhs, rhs), em), ror); } } +