Skip to content

Commit

Permalink
ASCII art for segment tree (#2224)
Browse files Browse the repository at this point in the history
Allow ASCII rendering of segment trees and morphologies for debugging
Example `segment_tree`
```
                      |              |                +-[-- id=1161 --]-+-[-- id=1709 --]---[-- id=2234 --]-+-[-- id=2625 --]---[-- id=3172 --]
                      |              |                |                 |                                   +-[-- id=2928 --]---[-- id=3765 --]
                      |              |                |                 |
                      |              |                |                 +-[-- id=1721 --]---[-- id=3504 --]---[-- id=3846 --]
                      |              |                |
                      |              |                +-[-- id=1867 --]
                      |              |
                      |              +-[-- id=1451 --]-+-[-- id=2356 --]
                      |                                +-[-- id=2471 --]---[-- id=2670 --]---[-- id=4031 --]
                      |
                      |
                      +-[-- id=35 --]---[-- id=263 --]-+-[-- id=409 --]-+-[-- id=475 --]---[-- id=1347 --]
                      |                                |                +-[-- id=480 --]-+-[-- id=1764 --]-+-[-- id=1912 --]
                      |                                |                |                |                 +-[-- id=2975 --]
                      |                                |                |                |
                      |                                |                |                +-[-- id=2047 --]
                      |                                |                |
                      |                                |                +-[-- id=3124 --]
                      |                                |
                      |                                +-[-- id=548 --]-+-[-- id=1436 --]---[-- id=1699 --]
                      |                                |                +-[-- id=2194 --]
                      |                                |                +-[-- id=3250 --]
                      |                                |                +-[-- id=3281 --]
                      |                                |                +-[-- id=3589 --]
                      |                                |
                      |                                +-[-- id=651 --]-+-[-- id=943 --]-+-[-- id=1024 --]-+-[-- id=1070 --]-+-[-- id=1477 --]-+-[-- id=1939 --]
                      |                                |                |                |                 |                 |                 +-[-- id=3457 --]
                      |                                |                |                |                 |                 |
                      |                                |                |                |                 |                 +-[-- id=2475 --]
                      |                                |                |                |                 |
                      |                                |                |                |                 +-[-- id=1929 --]
                      |                                |                |                |
                      |                                |                |                +-[-- id=1314 --]---[-- id=2677 --]
                      |                                |                |
                      |                                |                +-[-- id=1793 --]---[-- id=2792 --]
                      |                                |
                      |                                +-[-- id=1391 --]-+-[-- id=2548 --]
                      |                                                  +-[-- id=2743 --]
                      |
                      |
                      +-[-- id=1621 --]---[-- id=3879 --]
                      +-[-- id=1914 --]-+-[-- id=2572 --]
                      |                 +-[-- id=3385 --]
                      |
                      +-[-- id=3991 --]
```
and the same snippet in the equivalent morphology
```
                   +-<-- 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=344 len=1 -->-+-<-- id=714 len=1 -->-+-<-- id=937 len=2 -->
                   |                      |                      |                      |                      +-<-- id=1292 len=2 -->
                   |                      |                      |                      |                      +-<-- id=1698 len=1 -->-+-<-- id=2036 len=1 -->
                   |                      |                      |                      |                                              +-<-- id=2382 len=1 -->
                   |                      |                      |                      |
                   |                      |                      |                      |
                   |                      |                      |                      +-<-- id=864 len=4 -->
                   |                      |                      |                      +-<-- id=2412 len=2 -->
                   |                      |                      |
                   |                      |                      +-<-- id=1570 len=1 -->
                   |                      |
                   |                      +-<-- id=355 len=1 -->-+-<-- id=928 len=1 -->-+-<-- id=2200 len=2 -->
                   |                      |                      |                      +-<-- id=2427 len=1 -->
                   |                      |                      |                      +-<-- id=2523 len=1 -->
                   |                      |                      |                      +-<-- id=2988 len=1 -->
                   |                      |                      |
                   |                      |                      +-<-- id=1303 len=1 -->-+-<-- id=2169 len=1 -->-+-<-- id=2334 len=1 -->
                   |                      |                      |                       |                       +-<-- id=2708 len=2 -->
                   |                      |                      |                       |
                   |                      |                      |                       +-<-- id=2647 len=1 -->
                   |                      |                      |
                   |                      |                      +-<-- id=1399 len=1 -->
                   |                      |
                   |                      +-<-- id=704 len=2 -->
                   |
                   +-<-- id=1157 len=2 -->
```

Closes #2132
  • Loading branch information
thorstenhater authored Aug 15, 2024
1 parent 1050ff4 commit 2c08a56
Show file tree
Hide file tree
Showing 11 changed files with 253 additions and 11 deletions.
3 changes: 1 addition & 2 deletions arbor/include/arbor/morph/segment_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,4 @@ apply(const segment_tree&, const isometry&);
// Roots of regions of specific tag in segment tree
ARB_ARBOR_API std::vector<msize_t> tag_roots(const segment_tree& in, int tag);


} // namespace arb
} // namespace arb
2 changes: 0 additions & 2 deletions arbor/morph/segment_tree.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#include <stdexcept>
#include <map>
#include <vector>

Expand Down Expand Up @@ -246,6 +245,5 @@ ARB_ARBOR_API std::vector<msize_t> tag_roots(const segment_tree& t, int tag) {
return tag_roots;
}


} // namespace arb

3 changes: 2 additions & 1 deletion arborio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ set(arborio-sources
label_parse.cpp
neuroml.cpp
networkio.cpp
nml_parse_morphology.cpp)
nml_parse_morphology.cpp
debug.cpp)

add_library(arborio ${arborio-sources})

Expand Down
99 changes: 99 additions & 0 deletions arborio/debug.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#include <arborio/debug.hpp>

#include <arbor/morph/primitives.hpp>

#include <map>
#include <numeric>

namespace arborio {

template <typename T, typename P>
std::vector<std::string> render(const T& tree,
arb::msize_t root,
const std::multimap<arb::msize_t, arb::msize_t>& 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);

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);
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() += split + row;
} else {
// Other children get connected to the vertical line
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 ? vline : blank) + 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&) {
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()) + " -->" ;
}

ARB_ARBORIO_API std::string show(const arb::segment_tree& tree) {
if (tree.empty()) return "";

std::multimap<arb::msize_t, arb::msize_t> children;
const auto& ps = tree.parents();
for (arb::msize_t idx = 0; idx < tree.size(); ++idx) {
auto parent = ps[idx];
children.emplace(parent, idx);
}

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"; });
}

ARB_ARBORIO_API std::string show(const arb::morphology& mrf) {
if (mrf.empty()) return "";

std::multimap<arb::msize_t, arb::msize_t> children;
for (arb::msize_t 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"; });
}
}
16 changes: 16 additions & 0 deletions arborio/include/arborio/debug.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include <string>
#include <functional>
#include <vector>

#include <arbor/export.hpp>
#include <arborio/export.hpp>

#include <arbor/morph/segment_tree.hpp>
#include <arbor/morph/morphology.hpp>

namespace arborio {
ARB_ARBORIO_API std::string show(const arb::segment_tree&);
ARB_ARBORIO_API std::string show(const arb::morphology&);
}
69 changes: 66 additions & 3 deletions doc/cpp/morphology.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -66,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<segment_tree, segment_tree> split_at(const segment_tree& t, msize_t id)

Split a segment_tree into a pair of subtrees at the given id,
Expand Down Expand Up @@ -100,9 +103,47 @@ 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

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.

Describe morphology methods.
.. cpp:function:: const std::vector<msize_t>& branch_children(msize_t b) const

The child branches of branch ``b``. If b is ``mnpos``, return root branches.

.. cpp:function:: const std::vector<msize_t>& terminal_branches() const

Branches with no children.

.. cpp:function:: const std::vector<msegment>& branch_segments(msize_t b) const

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:

Expand Down Expand Up @@ -200,6 +241,28 @@ 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.

Example for an arbitrary segment tree

.. code::
[-- id=0 --]-+-[-- id=1 --]
+-[-- id=2 --]-+-[-- id=3 --]
+-[-- id=4 --]
and for the equivalent morphology

.. code::
<-- id=0 len=1 -->-+-<-- id=1 len=1 -->
+-<-- id=2 len=1 -->-+-<-- id=3 len=1 -->
+-<-- id=4 len=1 -->
.. _locsets-and-regions:

Expand Down
12 changes: 12 additions & 0 deletions doc/python/morphology.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -352,6 +358,12 @@ Cable cell morphology
:param int i: branch index
:rtype: list[msegment]

.. 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
Expand Down
7 changes: 7 additions & 0 deletions python/morphology.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <arborio/swcio.hpp>
#include <arborio/neurolucida.hpp>
#include <arborio/neuroml.hpp>
#include <arborio/debug.hpp>

#include "util.hpp"
#include "error.hpp"
Expand Down Expand Up @@ -292,6 +293,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",
[] (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("<arbor.segment_tree:\n{}>", s);});

Expand Down Expand Up @@ -321,6 +325,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("<arbor.morphology:\n{}>", m);
Expand Down
1 change: 1 addition & 0 deletions test/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 0 additions & 3 deletions test/unit/test_asc.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
#include <iostream>
#include <fstream>

#include <arbor/cable_cell.hpp>
#include <arbor/morph/primitives.hpp>
#include <arbor/morph/segment_tree.hpp>
Expand Down
49 changes: 49 additions & 0 deletions test/unit/test_debug.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include <arbor/morph/morphology.hpp>
#include <arbor/morph/segment_tree.hpp>

#include <arborio/debug.hpp>

#include <gtest/gtest.h>

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}));
}

0 comments on commit 2c08a56

Please sign in to comment.