From c69ba65c8e9f7aa881b84019cff73291437adec7 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Wed, 20 Sep 2023 11:09:25 +0200 Subject: [PATCH 01/12] Add ASCII `show` for segment tree. --- arbor/include/arbor/morph/segment_tree.hpp | 3 +- arbor/morph/segment_tree.cpp | 61 ++++++++++++++++++++++ test/unit/test_segment_tree.cpp | 1 + 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/arbor/include/arbor/morph/segment_tree.hpp b/arbor/include/arbor/morph/segment_tree.hpp index 0d9c6b564a..e9f66975cf 100644 --- a/arbor/include/arbor/morph/segment_tree.hpp +++ b/arbor/include/arbor/morph/segment_tree.hpp @@ -88,5 +88,6 @@ apply(const segment_tree&, const isometry&); // Roots of regions of specific tag in segment tree ARB_ARBOR_API std::vector tag_roots(const segment_tree& in, int tag); +std::string show(const segment_tree& tree); -} // namespace arb \ No newline at end of file +} // namespace arb diff --git a/arbor/morph/segment_tree.cpp b/arbor/morph/segment_tree.cpp index 92eb13d6fb..698ccaeac0 100644 --- a/arbor/morph/segment_tree.cpp +++ b/arbor/morph/segment_tree.cpp @@ -1,5 +1,7 @@ +#include #include #include +#include #include #include @@ -8,6 +10,7 @@ #include "io/sepval.hpp" #include "util/span.hpp" #include "util/transform.hpp" +#include "util/strprintf.hpp" using arb::util::make_span; @@ -246,6 +249,64 @@ ARB_ARBOR_API std::vector tag_roots(const segment_tree& t, int tag) { return tag_roots; } +std::vector render(const segment_tree& tree, + msize_t root, + const std::multimap& children) { + auto n_child = children.count(root); + auto seg = util::pprintf("[- {} -]", root); + if (0 == n_child) return {seg}; + auto sep = std::string(seg.size(), ' '); + if (1 == n_child) { + const auto& [lo, hi] = children.equal_range(root); + auto child = render(tree, lo->second, children); + child.front() = seg + "---" + child.front(); + for (auto rdx = 1; rdx < child.size(); ++rdx) child[rdx] = sep + " " + child[rdx]; + return child; + } + std::vector res = {seg}; + auto cdx = 0; + for (auto [parent, child]: util::make_range(children.equal_range(root))) { + auto rows = render(tree, child, children); + auto rdx = 0; + for (const auto& row: rows) { + // Append the first row directly onto our segments, this [- -] -- [- -] + if (rdx == 0) { + // The first child of a node may span a sub-tree + if (cdx == 0) { + res.back() += std::string{"-+-"} + row; + } else { + // Other children get connected to the vertical line + res.push_back(sep + " +-" + row); + } + cdx++; + } else { + // If there are more children, extend the subtree by showing a + // vertical line + res.push_back(sep + (cdx < n_child ? " | " : " ") + row); + } + ++rdx; + } + } + res.push_back(sep); + return res; +} + +std::string show(const segment_tree& tree) { + if (tree.empty()) return ""; + // the tree as parent -> child + std::multimap children; + const auto& parents = tree.parents(); + for (auto idx = 0; idx < tree.size(); ++idx) { + auto parent = parents[idx]; + children.emplace(parent, idx); + } + + auto res = render(tree, 0, children); + return std::accumulate(res.begin(), res.end(), + std::string{}, + [](auto lhs, auto rhs) { return lhs + rhs + "\n"; }); +} + } // namespace arb diff --git a/test/unit/test_segment_tree.cpp b/test/unit/test_segment_tree.cpp index fb05e9abe5..ff0ad57a71 100644 --- a/test/unit/test_segment_tree.cpp +++ b/test/unit/test_segment_tree.cpp @@ -100,6 +100,7 @@ TEST(segment_tree, fuzz) { // Validate that the correct number of segments created. EXPECT_EQ(tree.size(), std::size_t(i+1)); } + std::cerr << "TREE" << show(tree); EXPECT_EQ(parents, tree.parents()); for (int i=0; i Date: Wed, 20 Sep 2023 11:52:36 +0200 Subject: [PATCH 02/12] Add Python ad docs. --- arbor/include/arbor/morph/segment_tree.hpp | 5 +++-- arbor/morph/segment_tree.cpp | 12 ++++++------ doc/cpp/morphology.rst | 4 ++++ doc/python/morphology.rst | 6 ++++++ python/morphology.cpp | 3 +++ 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/arbor/include/arbor/morph/segment_tree.hpp b/arbor/include/arbor/morph/segment_tree.hpp index e9f66975cf..5f875c7ec2 100644 --- a/arbor/include/arbor/morph/segment_tree.hpp +++ b/arbor/include/arbor/morph/segment_tree.hpp @@ -52,6 +52,9 @@ class ARB_ARBOR_API segment_tree { bool is_terminal(msize_t i) const; bool is_root(msize_t i) const; + // Make an ASCII tree + std::string show(std::function) const; + friend std::ostream& operator<<(std::ostream&, const segment_tree&); // compare two trees for _identity_, not _equivalence_ @@ -88,6 +91,4 @@ apply(const segment_tree&, const isometry&); // Roots of regions of specific tag in segment tree ARB_ARBOR_API std::vector tag_roots(const segment_tree& in, int tag); -std::string show(const segment_tree& tree); - } // namespace arb diff --git a/arbor/morph/segment_tree.cpp b/arbor/morph/segment_tree.cpp index 698ccaeac0..1c66708235 100644 --- a/arbor/morph/segment_tree.cpp +++ b/arbor/morph/segment_tree.cpp @@ -291,17 +291,17 @@ std::vector render(const segment_tree& tree, return res; } -std::string show(const segment_tree& tree) { - if (tree.empty()) return ""; +std::string segment_tree::show() const { + if (empty()) return ""; // the tree as parent -> child std::multimap children; - const auto& parents = tree.parents(); - for (auto idx = 0; idx < tree.size(); ++idx) { - auto parent = parents[idx]; + const auto& ps = parents(); + for (auto idx = 0; idx < size(); ++idx) { + auto parent = ps[idx]; children.emplace(parent, idx); } - auto res = render(tree, 0, children); + auto res = render(*this, 0, children); return std::accumulate(res.begin(), res.end(), std::string{}, [](auto lhs, auto rhs) { return lhs + rhs + "\n"; }); diff --git a/doc/cpp/morphology.rst b/doc/cpp/morphology.rst index 178878e930..a26d67b4ae 100644 --- a/doc/cpp/morphology.rst +++ b/doc/cpp/morphology.rst @@ -66,6 +66,10 @@ consistent parent-child indexing, and with ``n`` segments numbered from ``0`` to A list of the segments. + .. cpp:function:: std::string show() + + Return a string containing an ASCII rendering of the tree. + .. cpp:function:: std::pair split_at(const segment_tree& t, msize_t id) Split a segment_tree into a pair of subtrees at the given id, diff --git a/doc/python/morphology.rst b/doc/python/morphology.rst index ae00ea1ce7..1827b0bdc5 100644 --- a/doc/python/morphology.rst +++ b/doc/python/morphology.rst @@ -299,6 +299,12 @@ Cable cell morphology A list of the segments. + .. method:: show + + Return a string containing an ASCII rendering of the tree. + + :return: string + .. py:class:: morphology A *morphology* describes the geometry of a cell as unbranched cables diff --git a/python/morphology.cpp b/python/morphology.cpp index 3c0e3579a9..61baba8e32 100644 --- a/python/morphology.cpp +++ b/python/morphology.cpp @@ -287,6 +287,9 @@ void register_morphology(py::module& m) { .def("tag_roots", [](const arb::segment_tree& t, int tag) { return arb::tag_roots(t, tag); }, "Get roots of tag region of this segment tree.") + .def("show", + &arb::segment_tree::show, + "Return ASCII representation of tree.") .def("__str__", [](const arb::segment_tree& s) { return util::pprintf("", s);}); From 2cc501f70ece73e4a65f27612bdea17e34cb07c4 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Wed, 20 Sep 2023 12:26:38 +0200 Subject: [PATCH 03/12] Remove formatter. --- arbor/include/arbor/morph/segment_tree.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbor/include/arbor/morph/segment_tree.hpp b/arbor/include/arbor/morph/segment_tree.hpp index 5f875c7ec2..031cf31b9b 100644 --- a/arbor/include/arbor/morph/segment_tree.hpp +++ b/arbor/include/arbor/morph/segment_tree.hpp @@ -53,7 +53,7 @@ class ARB_ARBOR_API segment_tree { bool is_root(msize_t i) const; // Make an ASCII tree - std::string show(std::function) const; + std::string show() const; friend std::ostream& operator<<(std::ostream&, const segment_tree&); From eb0200b2ca69ace3058a6aa82934fcb00cfb938c Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Wed, 20 Sep 2023 12:36:58 +0200 Subject: [PATCH 04/12] Remove debug print. --- test/unit/test_segment_tree.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit/test_segment_tree.cpp b/test/unit/test_segment_tree.cpp index ff0ad57a71..fb05e9abe5 100644 --- a/test/unit/test_segment_tree.cpp +++ b/test/unit/test_segment_tree.cpp @@ -100,7 +100,6 @@ TEST(segment_tree, fuzz) { // Validate that the correct number of segments created. EXPECT_EQ(tree.size(), std::size_t(i+1)); } - std::cerr << "TREE" << show(tree); EXPECT_EQ(parents, tree.parents()); for (int i=0; i Date: Thu, 21 Sep 2023 08:42:11 +0200 Subject: [PATCH 05/12] Shuffle meothds to dedicated header. Add morphology support. --- arbor/include/arbor/morph/segment_tree.hpp | 3 -- arbor/morph/segment_tree.cpp | 59 ---------------------- arborio/CMakeLists.txt | 3 +- doc/cpp/morphology.rst | 49 +++++++++++++++--- doc/python/morphology.rst | 6 +++ python/morphology.cpp | 8 ++- 6 files changed, 56 insertions(+), 72 deletions(-) diff --git a/arbor/include/arbor/morph/segment_tree.hpp b/arbor/include/arbor/morph/segment_tree.hpp index 031cf31b9b..814d7d2cb0 100644 --- a/arbor/include/arbor/morph/segment_tree.hpp +++ b/arbor/include/arbor/morph/segment_tree.hpp @@ -52,9 +52,6 @@ class ARB_ARBOR_API segment_tree { bool is_terminal(msize_t i) const; bool is_root(msize_t i) const; - // Make an ASCII tree - std::string show() const; - friend std::ostream& operator<<(std::ostream&, const segment_tree&); // compare two trees for _identity_, not _equivalence_ diff --git a/arbor/morph/segment_tree.cpp b/arbor/morph/segment_tree.cpp index 1c66708235..7313e11574 100644 --- a/arbor/morph/segment_tree.cpp +++ b/arbor/morph/segment_tree.cpp @@ -249,64 +249,5 @@ ARB_ARBOR_API std::vector tag_roots(const segment_tree& t, int tag) { return tag_roots; } -std::vector render(const segment_tree& tree, - msize_t root, - const std::multimap& children) { - auto n_child = children.count(root); - auto seg = util::pprintf("[- {} -]", root); - if (0 == n_child) return {seg}; - auto sep = std::string(seg.size(), ' '); - if (1 == n_child) { - const auto& [lo, hi] = children.equal_range(root); - auto child = render(tree, lo->second, children); - child.front() = seg + "---" + child.front(); - for (auto rdx = 1; rdx < child.size(); ++rdx) child[rdx] = sep + " " + child[rdx]; - return child; - } - std::vector res = {seg}; - auto cdx = 0; - for (auto [parent, child]: util::make_range(children.equal_range(root))) { - auto rows = render(tree, child, children); - auto rdx = 0; - for (const auto& row: rows) { - // Append the first row directly onto our segments, this [- -] -- [- -] - if (rdx == 0) { - // The first child of a node may span a sub-tree - if (cdx == 0) { - res.back() += std::string{"-+-"} + row; - } else { - // Other children get connected to the vertical line - res.push_back(sep + " +-" + row); - } - cdx++; - } else { - // If there are more children, extend the subtree by showing a - // vertical line - res.push_back(sep + (cdx < n_child ? " | " : " ") + row); - } - ++rdx; - } - } - res.push_back(sep); - return res; -} - -std::string segment_tree::show() const { - if (empty()) return ""; - // the tree as parent -> child - std::multimap children; - const auto& ps = parents(); - for (auto idx = 0; idx < size(); ++idx) { - auto parent = ps[idx]; - children.emplace(parent, idx); - } - - auto res = render(*this, 0, children); - return std::accumulate(res.begin(), res.end(), - std::string{}, - [](auto lhs, auto rhs) { return lhs + rhs + "\n"; }); -} - - } // namespace arb diff --git a/arborio/CMakeLists.txt b/arborio/CMakeLists.txt index bd4bbce4a4..4b73f0a5fe 100644 --- a/arborio/CMakeLists.txt +++ b/arborio/CMakeLists.txt @@ -6,7 +6,8 @@ set(arborio-sources cv_policy_parse.cpp label_parse.cpp neuroml.cpp - nml_parse_morphology.cpp) + nml_parse_morphology.cpp + debug.cpp) add_library(arborio ${arborio-sources}) diff --git a/doc/cpp/morphology.rst b/doc/cpp/morphology.rst index a26d67b4ae..43b9a4942f 100644 --- a/doc/cpp/morphology.rst +++ b/doc/cpp/morphology.rst @@ -31,7 +31,6 @@ consistent parent-child indexing, and with ``n`` segments numbered from ``0`` to .. cpp:class:: segment_tree - .. cpp:function:: segment_tree() Construct an empty segment tree. @@ -66,10 +65,6 @@ consistent parent-child indexing, and with ``n`` segments numbered from ``0`` to A list of the segments. - .. cpp:function:: std::string show() - - Return a string containing an ASCII rendering of the tree. - .. cpp:function:: std::pair split_at(const segment_tree& t, msize_t id) Split a segment_tree into a pair of subtrees at the given id, @@ -104,9 +99,43 @@ consistent parent-child indexing, and with ``n`` segments numbered from ``0`` to Morphology API -------------- -.. todo:: +.. cpp:class:: morphology + + .. cpp:function:: morphology() + + Construct an empty morphology. + + .. cpp:function:: morphology(const segment_tree&) + + Construct a morphology from a segment tree. + + .. cpp:function:: segment_tree to_segment_tree() const + + Reconcstruct the underlying segment tree. + + .. cpp:function:: bool empty() const - Describe morphology methods. + Is this the trivial morphology? + + .. cpp:function:: msize_t num_branches() const + + The number of branches in the morphology. + + .. cpp:function:: msize_t branch_parent(msize_t b) const + + The parent branch of branch ``b``. Return ``mnpos`` if branch has no parent. + + .. cpp:function:: const std::vector& branch_children(msize_t b) const + + The child branches of branch ``b``. If b is ``mnpos``, return root branches. + + .. cpp:function:: const std::vector& terminal_branches() const + + Branches with no children. + + .. cpp:function:: const std::vector& branch_segments(msize_t b) const + + Range of segments in a branch. .. _cppcablecell-morphology-construction: @@ -204,6 +233,12 @@ by two stitches: cable_cell cell(stitched.morphology(), dec, stitched.labels()); +Debug Ouput +----------- + +Tree representations of :cpp:type:`segment_tree` and :cpp:type:`morphology` can be obtained +by including ``arborio/debug.hpp`` which contains a series of :cpp:func:`show` functions +that return ASCII renderings of the given object. .. _locsets-and-regions: diff --git a/doc/python/morphology.rst b/doc/python/morphology.rst index 1827b0bdc5..6fbfc20514 100644 --- a/doc/python/morphology.rst +++ b/doc/python/morphology.rst @@ -358,6 +358,12 @@ Cable cell morphology :param int i: branch index :rtype: list + .. method:: show + + Return a string containing an ASCII rendering of the morphology. + + :return: string + .. py:class:: place_pwlin A :class:`place_pwlin` object allows the querying of the 3-d location of locations and cables diff --git a/python/morphology.cpp b/python/morphology.cpp index 61baba8e32..4a8789d4e7 100644 --- a/python/morphology.cpp +++ b/python/morphology.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "util.hpp" #include "error.hpp" @@ -288,8 +289,8 @@ void register_morphology(py::module& m) { [](const arb::segment_tree& t, int tag) { return arb::tag_roots(t, tag); }, "Get roots of tag region of this segment tree.") .def("show", - &arb::segment_tree::show, - "Return ASCII representation of tree.") + [] (const arb::segment_tree& t) { return arborio::show(t); }, + "Return an ASCII representation of this segment tree.") .def("__str__", [](const arb::segment_tree& s) { return util::pprintf("", s);}); @@ -371,6 +372,9 @@ void register_morphology(py::module& m) { "i"_a, "A list of the segments in branch i, ordered from proximal to distal ends of the branch.") .def("to_segment_tree", &arb::morphology::to_segment_tree, "Convert this morphology to a segment_tree.") + .def("show", + [] (const arb::morphology& t) { return arborio::show(t); }, + "Return an ASCII representation.") .def("__str__", [](const arb::morphology& m) { return util::pprintf("", m); From 0585a8e21e4c45cc061c4cd8cb6cf5e6389b0dfc Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Thu, 21 Sep 2023 08:42:54 +0200 Subject: [PATCH 06/12] Add the headers. --- arborio/debug.cpp | 95 +++++++++++++++++++++++++++++++ arborio/include/arborio/debug.hpp | 16 ++++++ 2 files changed, 111 insertions(+) create mode 100644 arborio/debug.cpp create mode 100644 arborio/include/arborio/debug.hpp diff --git a/arborio/debug.cpp b/arborio/debug.cpp new file mode 100644 index 0000000000..813e67bc2f --- /dev/null +++ b/arborio/debug.cpp @@ -0,0 +1,95 @@ +#include + +#include + +#include +#include +#include + +namespace arborio { + +template +std::vector render(const T& tree, + arb::msize_t root, + const std::multimap& children, + P print) { + auto n_child = children.count(root); + auto seg = print(root, tree); + if (0 == n_child) return {seg}; + auto sep = std::string(seg.size(), ' '); + if (1 == n_child) { + const auto& [lo, hi] = children.equal_range(root); + auto child = render(tree, lo->second, children, print); + child.front() = seg + "---" + child.front(); + for (auto rdx = 1; rdx < child.size(); ++rdx) child[rdx] = sep + " " + child[rdx]; + return child; + } + std::vector res = {seg}; + auto cdx = 0; + auto [beg, end] = children.equal_range(root); + for (auto it = beg; it != end; ++it) { + const auto& [parent, child] = *it; + auto rows = render(tree, child, children, print); + auto rdx = 0; + for (const auto& row: rows) { + // Append the first row directly onto our segments, this [- -] -- [- -] + if (rdx == 0) { + // The first child of a node may span a sub-tree + if (cdx == 0) { + res.back() += std::string{"-+-"} + row; + } else { + // Other children get connected to the vertical line + res.push_back(sep + " +-" + row); + } + cdx++; + } else { + // If there are more children, extend the subtree by showing a + // vertical line + res.push_back(sep + (cdx < n_child ? " | " : " ") + row); + } + ++rdx; + } + } + res.push_back(sep); + return res; +} + +ARB_ARBORIO_API std::string default_segment_printer(const arb::msize_t id, const arb::segment_tree&) { + return "[-- id=" + std::to_string(id) + " --]" ; +} + +std::string ARB_ARBORIO_API default_branch_printer(const arb::msize_t id, const arb::morphology& mrf) { + return "<-- id=" + std::to_string(id) + " len=" + std::to_string(mrf.branch_segments(id).size()) + " -->" ; +} + +ARB_ARBORIO_API std::string show(const arb::segment_tree& tree) { + if (tree.empty()) return ""; + + std::multimap children; + const auto& ps = tree.parents(); + for (auto idx = 0; idx < tree.size(); ++idx) { + auto parent = ps[idx]; + children.emplace(parent, idx); + } + + auto res = render(tree, -1, children, default_segment_printer); + return std::accumulate(res.begin(), res.end(), + std::string{}, + [](auto lhs, auto rhs) { return lhs + rhs + "\n"; }); +} + +ARB_ARBORIO_API std::string show(const arb::morphology& mrf) { + if (mrf.empty()) return ""; + + std::multimap children; + for (auto idx = 0; idx < mrf.num_branches(); ++idx) { + auto parent = mrf.branch_parent(idx); + children.emplace(parent, idx); + } + + auto res = render(mrf, 0, children, default_branch_printer); + return std::accumulate(res.begin(), res.end(), + std::string{}, + [](auto lhs, auto rhs) { return lhs + rhs + "\n"; }); +} +} diff --git a/arborio/include/arborio/debug.hpp b/arborio/include/arborio/debug.hpp new file mode 100644 index 0000000000..84faf5299d --- /dev/null +++ b/arborio/include/arborio/debug.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include +#include + +namespace arborio { +ARB_ARBORIO_API std::string show(const arb::segment_tree&); +ARB_ARBORIO_API std::string show(const arb::morphology&); +} From 1894f8965d1b389d5eb307f70c1113d1e4c989c7 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Tue, 13 Aug 2024 12:29:15 +0200 Subject: [PATCH 07/12] Clean-up headers. --- arbor/morph/segment_tree.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arbor/morph/segment_tree.cpp b/arbor/morph/segment_tree.cpp index 7313e11574..551e213ed1 100644 --- a/arbor/morph/segment_tree.cpp +++ b/arbor/morph/segment_tree.cpp @@ -1,7 +1,4 @@ -#include -#include #include -#include #include #include @@ -10,7 +7,6 @@ #include "io/sepval.hpp" #include "util/span.hpp" #include "util/transform.hpp" -#include "util/strprintf.hpp" using arb::util::make_span; From dd150268865e8950f5f8b5be550824d6dee2b356 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Tue, 13 Aug 2024 12:29:31 +0200 Subject: [PATCH 08/12] Start cleaning up code. Snapshot to test. --- arborio/debug.cpp | 31 ++++++++++++++++++++++--------- doc/cpp/morphology.rst | 32 +++++++++++++++++++++++++++++--- ext/fmt | 2 +- ext/json | 2 +- ext/pugixml | 2 +- ext/pybind11 | 2 +- 6 files changed, 55 insertions(+), 16 deletions(-) diff --git a/arborio/debug.cpp b/arborio/debug.cpp index 813e67bc2f..e6b1890afe 100644 --- a/arborio/debug.cpp +++ b/arborio/debug.cpp @@ -3,7 +3,6 @@ #include #include -#include #include namespace arborio { @@ -13,20 +12,34 @@ std::vector render(const T& tree, arb::msize_t root, const std::multimap& children, P print) { + // ASCII art elements + // TODO these could be customizable, but need conformant lengths + const std::string vline = " | "; + const std::string hline = "---"; + const std::string blank = " "; + const std::string split = "-+-"; + const std::string start = " +-"; + + auto n_child = children.count(root); auto seg = print(root, tree); if (0 == n_child) return {seg}; + auto sep = std::string(seg.size(), ' '); + const auto& [beg, end] = children.equal_range(root); + if (1 == n_child) { - const auto& [lo, hi] = children.equal_range(root); - auto child = render(tree, lo->second, children, print); - child.front() = seg + "---" + child.front(); - for (auto rdx = 1; rdx < child.size(); ++rdx) child[rdx] = sep + " " + child[rdx]; + auto child = render(tree, beg->second, children, print); + auto pad = seg; + for (auto rdx = 0; rdx < child.size(); ++rdx) { + child[rdx] = pad + blank + child[rdx]; + pad = sep; + } return child; } + std::vector res = {seg}; auto cdx = 0; - auto [beg, end] = children.equal_range(root); for (auto it = beg; it != end; ++it) { const auto& [parent, child] = *it; auto rows = render(tree, child, children, print); @@ -36,16 +49,16 @@ std::vector render(const T& tree, if (rdx == 0) { // The first child of a node may span a sub-tree if (cdx == 0) { - res.back() += std::string{"-+-"} + row; + res.back() += split + row; } else { // Other children get connected to the vertical line - res.push_back(sep + " +-" + row); + res.push_back(sep + start + row); } cdx++; } else { // If there are more children, extend the subtree by showing a // vertical line - res.push_back(sep + (cdx < n_child ? " | " : " ") + row); + res.push_back(sep + (cdx < n_child ? vline : blank) + row); } ++rdx; } diff --git a/doc/cpp/morphology.rst b/doc/cpp/morphology.rst index 43b9a4942f..5b92bbc1f1 100644 --- a/doc/cpp/morphology.rst +++ b/doc/cpp/morphology.rst @@ -65,6 +65,10 @@ consistent parent-child indexing, and with ``n`` segments numbered from ``0`` to A list of the segments. +.. cpp:function:: std::string show(const arb::segment_tree&) + + Return a string representation of the tree. + .. cpp:function:: std::pair split_at(const segment_tree& t, msize_t id) Split a segment_tree into a pair of subtrees at the given id, @@ -137,6 +141,10 @@ Morphology API Range of segments in a branch. +.. cpp:function:: std::string show(const arb::morphology&) + + Return a string representation of the tree underlying the morphology. + .. _cppcablecell-morphology-construction: The stitch-builder interface @@ -236,9 +244,27 @@ by two stitches: Debug Ouput ----------- -Tree representations of :cpp:type:`segment_tree` and :cpp:type:`morphology` can be obtained -by including ``arborio/debug.hpp`` which contains a series of :cpp:func:`show` functions -that return ASCII renderings of the given object. +Tree representations of :cpp:type:`segment_tree` and :cpp:type:`morphology` can +be obtained by including ``arborio/debug.hpp`` which contains a series of +:cpp:func:`show` functions that return ASCII renderings of the given object. +Example for an arbitrary morphology (truncated) + +.. code:: + + +-<-- id=217 len=1 -->-+-<-- id=301 len=1 -->-+-<-- id=310 len=2 -->-+-<-- id=2246 len=2 --> + | | | +-<-- id=2830 len=1 --> + | | | + | | +-<-- id=323 len=1 -->-+-<-- id=361 len=3 --> + | | | +-<-- id=1696 len=1 -->-+-<-- id=2166 len=1 --> + | | | | +-<-- id=2260 len=2 --> + | | | | + | | | +-<-- id=1735 len=1 -->-+-<-- id=1818 len=1 --> + | | | | +-<-- id=2118 len=1 --> + | | | | + | | | +-<-- id=1909 len=3 --> + | | | +-<-- id=2772 len=1 --> + | | | +-<-- id=3004 len=1 --> + | | | .. _locsets-and-regions: diff --git a/ext/fmt b/ext/fmt index a0b8a92e3d..e69e5f977d 160000 --- a/ext/fmt +++ b/ext/fmt @@ -1 +1 @@ -Subproject commit a0b8a92e3d1532361c2f7feb63babc5c18d00ef2 +Subproject commit e69e5f977d458f2650bb346dadf2ad30c5320281 diff --git a/ext/json b/ext/json index bc889afb4c..9cca280a4d 160000 --- a/ext/json +++ b/ext/json @@ -1 +1 @@ -Subproject commit bc889afb4c5bf1c0d8ee29ef35eaaf4c8bef8a5d +Subproject commit 9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03 diff --git a/ext/pugixml b/ext/pugixml index a0e0643363..db78afc2b7 160000 --- a/ext/pugixml +++ b/ext/pugixml @@ -1 +1 @@ -Subproject commit a0e064336317c9347a91224112af9933598714e9 +Subproject commit db78afc2b7d8f043b4bc6b185635d949ea2ed2a8 diff --git a/ext/pybind11 b/ext/pybind11 index 80dc998efc..8a099e44b3 160000 --- a/ext/pybind11 +++ b/ext/pybind11 @@ -1 +1 @@ -Subproject commit 80dc998efced8ceb2be59756668a7e90e8bef917 +Subproject commit 8a099e44b3d5f85b20f05828d919d2332a8de841 From 6ebd9ea135d6396d5fdceadaa715049b72deaea6 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:57:36 +0200 Subject: [PATCH 09/12] Simplify code, add tests, polish docs. --- arborio/debug.cpp | 27 +++++++++------------------ doc/cpp/morphology.rst | 28 +++++++++++++--------------- test/unit/CMakeLists.txt | 1 + test/unit/test_asc.cpp | 3 --- 4 files changed, 23 insertions(+), 36 deletions(-) diff --git a/arborio/debug.cpp b/arborio/debug.cpp index e6b1890afe..17becc18ca 100644 --- a/arborio/debug.cpp +++ b/arborio/debug.cpp @@ -20,7 +20,6 @@ std::vector render(const T& tree, const std::string split = "-+-"; const std::string start = " +-"; - auto n_child = children.count(root); auto seg = print(root, tree); if (0 == n_child) return {seg}; @@ -28,18 +27,8 @@ std::vector render(const T& tree, auto sep = std::string(seg.size(), ' '); const auto& [beg, end] = children.equal_range(root); - if (1 == n_child) { - auto child = render(tree, beg->second, children, print); - auto pad = seg; - for (auto rdx = 0; rdx < child.size(); ++rdx) { - child[rdx] = pad + blank + child[rdx]; - pad = sep; - } - return child; - } - - std::vector res = {seg}; - auto cdx = 0; + std::vector res = {seg}; + arb::msize_t cdx = 0; for (auto it = beg; it != end; ++it) { const auto& [parent, child] = *it; auto rows = render(tree, child, children, print); @@ -63,15 +52,17 @@ std::vector render(const T& tree, ++rdx; } } - res.push_back(sep); + // res.push_back(sep); return res; } ARB_ARBORIO_API std::string default_segment_printer(const arb::msize_t id, const arb::segment_tree&) { - return "[-- id=" + std::to_string(id) + " --]" ; + auto lbl = (id == arb::mnpos) ? "(root)" : std::to_string(id); + return "[-- id=" + lbl + " --]" ; } std::string ARB_ARBORIO_API default_branch_printer(const arb::msize_t id, const arb::morphology& mrf) { + auto lbl = (id == arb::mnpos) ? std::string("(root)") : std::to_string(id); return "<-- id=" + std::to_string(id) + " len=" + std::to_string(mrf.branch_segments(id).size()) + " -->" ; } @@ -80,12 +71,12 @@ ARB_ARBORIO_API std::string show(const arb::segment_tree& tree) { std::multimap children; const auto& ps = tree.parents(); - for (auto idx = 0; idx < tree.size(); ++idx) { + for (arb::msize_t idx = 0; idx < tree.size(); ++idx) { auto parent = ps[idx]; children.emplace(parent, idx); } - auto res = render(tree, -1, children, default_segment_printer); + auto res = render(tree, 0, children, default_segment_printer); return std::accumulate(res.begin(), res.end(), std::string{}, [](auto lhs, auto rhs) { return lhs + rhs + "\n"; }); @@ -95,7 +86,7 @@ ARB_ARBORIO_API std::string show(const arb::morphology& mrf) { if (mrf.empty()) return ""; std::multimap children; - for (auto idx = 0; idx < mrf.num_branches(); ++idx) { + for (arb::msize_t idx = 0; idx < mrf.num_branches(); ++idx) { auto parent = mrf.branch_parent(idx); children.emplace(parent, idx); } diff --git a/doc/cpp/morphology.rst b/doc/cpp/morphology.rst index 7d2c9c1cb9..0de4df537a 100644 --- a/doc/cpp/morphology.rst +++ b/doc/cpp/morphology.rst @@ -247,24 +247,22 @@ Debug Ouput Tree representations of :cpp:type:`segment_tree` and :cpp:type:`morphology` can be obtained by including ``arborio/debug.hpp`` which contains a series of :cpp:func:`show` functions that return ASCII renderings of the given object. -Example for an arbitrary morphology (truncated) + +Example for an arbitrary segment tree + +.. code:: + + [-- id=0 --]-+-[-- id=1 --] + +-[-- id=2 --]-+-[-- id=3 --] + +-[-- id=4 --] + +and for the equivalent morphology .. code:: - +-<-- id=217 len=1 -->-+-<-- id=301 len=1 -->-+-<-- id=310 len=2 -->-+-<-- id=2246 len=2 --> - | | | +-<-- id=2830 len=1 --> - | | | - | | +-<-- id=323 len=1 -->-+-<-- id=361 len=3 --> - | | | +-<-- id=1696 len=1 -->-+-<-- id=2166 len=1 --> - | | | | +-<-- id=2260 len=2 --> - | | | | - | | | +-<-- id=1735 len=1 -->-+-<-- id=1818 len=1 --> - | | | | +-<-- id=2118 len=1 --> - | | | | - | | | +-<-- id=1909 len=3 --> - | | | +-<-- id=2772 len=1 --> - | | | +-<-- id=3004 len=1 --> - | | | + <-- id=0 len=1 -->-+-<-- id=1 len=1 --> + +-<-- id=2 len=1 -->-+-<-- id=3 len=1 --> + +-<-- id=4 len=1 --> .. _locsets-and-regions: diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index b75a4555bc..96f7e6d328 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -149,6 +149,7 @@ set(unit_sources test_vector.cpp test_version.cpp test_v_clamp.cpp + test_debug.cpp # unit test driver test.cpp diff --git a/test/unit/test_asc.cpp b/test/unit/test_asc.cpp index 0c06fbdc40..5e0443b742 100644 --- a/test/unit/test_asc.cpp +++ b/test/unit/test_asc.cpp @@ -1,6 +1,3 @@ -#include -#include - #include #include #include From 9510a256bb9d03162e419064c2cb85727270f92f Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:58:35 +0200 Subject: [PATCH 10/12] Redundant. --- arborio/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/arborio/CMakeLists.txt b/arborio/CMakeLists.txt index dc53fc9315..6cfd9fb6c0 100644 --- a/arborio/CMakeLists.txt +++ b/arborio/CMakeLists.txt @@ -6,7 +6,6 @@ set(arborio-sources cv_policy_parse.cpp label_parse.cpp neuroml.cpp - nml_parse_morphology.cpp networkio.cpp nml_parse_morphology.cpp debug.cpp) From bda3d0676999ead9141ee0979bdb40b7c21f0f13 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:59:41 +0200 Subject: [PATCH 11/12] Extra \n. --- arborio/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/arborio/CMakeLists.txt b/arborio/CMakeLists.txt index 6cfd9fb6c0..6016a3417d 100644 --- a/arborio/CMakeLists.txt +++ b/arborio/CMakeLists.txt @@ -10,7 +10,6 @@ set(arborio-sources nml_parse_morphology.cpp debug.cpp) - add_library(arborio ${arborio-sources}) if (ARB_USE_BUNDLED_PUGIXML) From 208df76bef94acc41aa4dd6c82c573390350ecf9 Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Tue, 13 Aug 2024 14:00:20 +0200 Subject: [PATCH 12/12] Add missing test. --- test/unit/test_debug.cpp | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 test/unit/test_debug.cpp diff --git a/test/unit/test_debug.cpp b/test/unit/test_debug.cpp new file mode 100644 index 0000000000..eb944e99f5 --- /dev/null +++ b/test/unit/test_debug.cpp @@ -0,0 +1,49 @@ +#include +#include + +#include + +#include + +TEST(debug_io, single) { + arb::segment_tree tree; + arb::msize_t par = arb::mnpos; + tree.append(par, {0, 0, 0, 5}, {0, 0, 10, 5}, 42); + + EXPECT_EQ("[-- id=0 --]\n", arborio::show(tree)); + EXPECT_EQ("<-- id=0 len=1 -->\n", arborio::show(arb::morphology{tree})); +} + +TEST(debug_io, fork) { + arb::segment_tree tree; + arb::msize_t par = arb::mnpos; + par = tree.append(par, {0, 0, 0, 5}, {0, 0, 10, 5}, 42); + tree.append(par, {0, 0, 10, 5}, {0, 1, 10, 5}, 23); + tree.append(par, {0, 0, 10, 5}, {0, -1, 10, 5}, 23); + + EXPECT_EQ("[-- id=0 --]-+-[-- id=1 --]\n" + " +-[-- id=2 --]\n", + arborio::show(tree)); + EXPECT_EQ("<-- id=0 len=1 -->-+-<-- id=1 len=1 -->\n" + " +-<-- id=2 len=1 -->\n", + arborio::show(arb::morphology{tree})); +} + +TEST(debug_io, complex) { + arb::segment_tree tree; + arb::msize_t lvl0 = arb::mnpos; + lvl0 = tree.append(lvl0, {0, 0, 0, 5}, {0, 0, 10, 5}, 42); + tree.append(lvl0, {0, 0, 10, 5}, {0, 1, 10, 5}, 23); + auto lvl1 = tree.append(lvl0, {0, 0, 10, 5}, {0, -1, 10, 5}, 23); + tree.append(lvl1, {0, -1, 10, 5}, { 1, -1, 10, 5}, 23); + tree.append(lvl1, {0, -1, 10, 5}, {-1, -1, 10, 5}, 23); + + EXPECT_EQ("[-- id=0 --]-+-[-- id=1 --]\n" + " +-[-- id=2 --]-+-[-- id=3 --]\n" + " +-[-- id=4 --]\n", + arborio::show(tree)); + EXPECT_EQ("<-- id=0 len=1 -->-+-<-- id=1 len=1 -->\n" + " +-<-- id=2 len=1 -->-+-<-- id=3 len=1 -->\n" + " +-<-- id=4 len=1 -->\n", + arborio::show(arb::morphology{tree})); +}