Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increasing altitudes asserts #193

Merged
merged 4 commits into from
Apr 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()