Skip to content

Commit

Permalink
Merge pull request #193 from higra/increasing_altitudes
Browse files Browse the repository at this point in the history
Increasing altitudes asserts
  • Loading branch information
PerretB authored Apr 29, 2020
2 parents df2199b + 5815aff commit 7e0303d
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 32 deletions.
2 changes: 1 addition & 1 deletion doc/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sphinx==2.1.0
sphinx==3.0.3
sphinx-tabs
breathe
higra==0.5.1
3 changes: 3 additions & 0 deletions doc/source/python/tree_algorithm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Algorithm for trees
labelisation_hierarchy_supervertices
reconstruct_leaf_data
sort_hierarchy_with_altitudes
test_altitudes_increasingness
test_tree_isomorphism
tree_fusion_depth_map

Expand All @@ -31,6 +32,8 @@ Algorithm for trees

.. autofunction:: higra.sort_hierarchy_with_altitudes

.. autofunction:: higra.test_altitudes_increasingness

.. autofunction:: higra.test_tree_isomorphism

.. autofunction:: higra.tree_fusion_depth_map
Expand Down
37 changes: 20 additions & 17 deletions higra/algo/horizontal_cuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,23 @@ def __labelisation_leaves(self, tree, leaf_graph, handle_rag=True):
return labels


# @hg.extend_class(hg.HorizontalCutExplorer, method_name="__new__")
# def __make_HorizontalCutExplorer(cls, tree, altitudes):
# """
# Creates an horizontal cut explorer for th given valued hierarchy.
#
# Altitudes must be increasing
#
# :param tree: input tree
# :param altitudes: tree nodes altitudes
# :return: an ``HorizontalCutExplorer``
# """
# return cls._make_HorizontalCutExplorer(tree, altitudes)
#
#
# @hg.extend_class(hg.HorizontalCutExplorer, method_name="__init__")
# def __dummy_init_HorizontalCutExplorer(*_):
# pass
@hg.extend_class(hg.HorizontalCutExplorer, method_name="__new__")
def __make_HorizontalCutExplorer(cls, tree, altitudes):
"""
Creates an horizontal cut explorer for the given valued hierarchy.
Altitudes must be increasing
:param tree: input tree
:param altitudes: tree nodes altitudes
:return: an ``HorizontalCutExplorer``
"""
if not hg.test_altitudes_increasingness(tree, altitudes):
raise ValueError("'altitudes' must be increasing for 'tree'.")

return cls._make_HorizontalCutExplorer(tree, altitudes)


@hg.extend_class(hg.HorizontalCutExplorer, method_name="__init__")
def __dummy_init_HorizontalCutExplorer(*_):
pass
14 changes: 3 additions & 11 deletions higra/algo/py_horizontal_cuts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ struct def_horizontal_cut_explorer_ctr {
template<typename type, typename C>
static
void def(C &c, const char *doc) {
c.def(py::init(
c.def_static("_make_HorizontalCutExplorer",
[](const typename c_t::tree_type &tree, const xt::pyarray<type> &altitudes) {
return c_t(tree, altitudes);
}),
},
doc,
py::arg("tree"),
py::arg("altitudes"));
Expand All @@ -92,15 +92,7 @@ Each cut of the hierarchy can be accessed through:
- the altitude of the cut. This operations runs in :math:`\mathcal{O}(k*\log(n))`, with :math:`k` the number of regions in the retrieved cut.)"""
);
add_type_overloads<def_horizontal_cut_explorer_ctr<class_t>, HG_TEMPLATE_NUMERIC_TYPES>
(c,
R"(Create an horizontal cut explorer for the provided valued hierarchy.
Altitudes must be increasing
:param tree: input tree
:param altitudes: tree nodes altitudes
:return: an ``HorizontalCutExplorer``
)");
(c,"");
c.def("num_cuts", &class_t::num_cuts, "Number of horizontal cuts in the hierarchy.");
c.def("num_regions_cut",
&class_t::num_regions_cut,
Expand Down
22 changes: 20 additions & 2 deletions higra/algo/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,6 @@ def binary_labelisation_from_markers(tree, object_marker, background_marker, lea
return labels


@hg.argument_helper(hg.CptHierarchy)
def sort_hierarchy_with_altitudes(tree, altitudes):
"""
Sort the nodes of a tree according to their altitudes.
Expand All @@ -303,11 +302,30 @@ def sort_hierarchy_with_altitudes(tree, altitudes):
The returned "node_map" is an array that maps any node index :math:`i` of the new tree,
to the index of this node in the original tree.
:param tree: input tree (Concept :class:`~higra.CptHierarchy`)
:param tree: input tree
:param altitudes: node altitudes of the input tree
:return: the sorted tree (Concept :class:`~higra.CptHierarchy`), its node altitudes, and the node map
"""
if not test_altitudes_increasingness(tree, altitudes):
raise ValueError("'altitudes' must be increasing for 'tree'.")

new_tree, node_map = hg.cpp._sort_hierarchy_with_altitudes(tree, altitudes)
new_altitudes = altitudes[node_map]

return new_tree, new_altitudes, node_map


def test_altitudes_increasingness(tree, altitudes):
"""
Test if the altitudes of the given tree are increasing; i.e. if for any nodes :math:`i, j` such that :math:`j`
is an ancestor of :math:`i`, then :math:`altitudes[i] \leq altitudes[j]`.
:param tree: input tree
:param altitudes: node altitudes of the input tree
:return: ``True`` if :attr:`altitudes` are increasing for :attr:`tree`, ``False`` otherwise.
"""
if altitudes.ndim != 1 or altitudes.size != tree.num_vertices():
raise ValueError("'altitudes' must be a 1d array of size 'tree.num_vertices()'.")

return np.all(altitudes <= altitudes[tree.parents()])
6 changes: 6 additions & 0 deletions test/python/test_algo/test_horizontal_cuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ def test_horizontal_cut_explorer_indexed(self):
self.assertTrue(np.all(np.sort(c.nodes()) == np.sort(cut_nodes[i])))
self.assertTrue(c.altitude() == alt_cuts[i])

def test_horizontal_cut_explorer_assert(self):
tree = hg.Tree(np.asarray((5, 5, 6, 6, 7, 7, 7, 7)))
altitudes = np.asarray((0, 0, 1, 0, 0, 2, 1, 1))
with self.assertRaises(ValueError):
hch = hg.HorizontalCutExplorer(tree, altitudes)

def test_horizontal_cut_explorer_altitudes(self):
tree = hg.Tree((11, 11, 11, 12, 12, 16, 13, 13, 13, 14, 14, 17, 16, 15, 15, 18, 17, 18, 18))
altitudes = np.asarray((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 3, 1, 2, 3))
Expand Down
27 changes: 26 additions & 1 deletion test/python/test_algo/test_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ def functor(tree, altitudes):
sm_ref = np.asarray((0, 0, 0, 0, 1, 0, 0))
self.assertTrue(np.all(sm == sm_ref))


def test_filter_non_relevant_node_from_tree(self):
g = hg.get_4_adjacency_graph((1, 8))
edge_weights = np.asarray((0, 2, 0, 0, 1, 0, 0))
Expand Down Expand Up @@ -225,6 +224,32 @@ def test_sort_hierarchy_with_altitudes(self):
ref_altitudes = np.asarray((0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7))
self.assertTrue(np.all(ref_altitudes == naltitudes))

tree = hg.Tree(np.asarray((5, 5, 6, 6, 7, 7, 7, 7)))
altitudes = np.asarray((0, 0, 1, 0, 0, 2, 1, 1))
with self.assertRaises(ValueError):
hg.sort_hierarchy_with_altitudes(tree, altitudes)

def test_test_altitudes_increasingness(self):
tree = hg.Tree(np.asarray((5, 5, 6, 6, 7, 7, 7, 7)))

altitudes = np.asarray((0, 0, 0, 0, 0, 2, 1, 3))
self.assertTrue(hg.test_altitudes_increasingness(tree, altitudes))

altitudes = np.asarray((0, 0, 0, 0, 0, 1, 2, 2))
self.assertTrue(hg.test_altitudes_increasingness(tree, altitudes))

altitudes = np.asarray((2, 0, 0, 1, 2, 2, 1, 3))
self.assertTrue(hg.test_altitudes_increasingness(tree, altitudes))

altitudes = np.asarray((0, 0, 0, 0, 0, 0, 0, 0))
self.assertTrue(hg.test_altitudes_increasingness(tree, altitudes))

altitudes = np.asarray((0, 0, 2, 0, 0, 2, 1, 3))
self.assertFalse(hg.test_altitudes_increasingness(tree, altitudes))

altitudes = np.asarray((0, 0, 1, 0, 0, 2, 1, 1))
self.assertFalse(hg.test_altitudes_increasingness(tree, altitudes))


if __name__ == '__main__':
unittest.main()

0 comments on commit 7e0303d

Please sign in to comment.