def subdominant_ultrametric(graph, edge_weights, return_hierarchy=False, dtype=tc.float64):
    """ 
    Subdominant (single linkage) ultrametric of an edge weighted graph.
    
    :param graph: input graph (class ``higra.UndirectedGraph``)
    :param edge_weights: edge weights of the input graph (pytorch tensor, autograd is supported)
    :param return_hierarchy: if ``True``,  the dendrogram representing the hierarchy is also returned as a tuple ``(tree, altitudes)``
    :return: the subdominant ultrametric of the input edge weighted graph (pytorch tensor) (and the hierarchy if ``return_hierarchy`` is ``True``)
    """  
    # compute single linkage if not already provided
    
    tree, altitudes_ = hg.bpt_canonical(graph, edge_weights.detach().numpy())

    # lowest common ancestors of every edge of the graph
    lca_map = hg.attribute_lca_map(tree)
    # the following is used to map lca node indices to their corresponding edge indices in the input graph
    # associated minimum spanning
    mst = hg.get_attribute(tree, "mst")
    # map mst edges to graph edges
    mst_map = hg.get_attribute(mst, "mst_edge_map")
    # bijection between single linkage node and mst edges
    mst_idx = lca_map - tree.num_leaves()
    # mst edge indices in the input graph
    edge_idx = mst_map[mst_idx]

    altitudes = edge_weights[mst_map]
    # sanity check
    # assert(np.all(altitudes.detach().numpy() == altitudes_[tree.num_leaves():]))
    ultrametric = edge_weights[edge_idx]
    
    if return_hierarchy:
        return ultrametric, (tree, tc.cat((tc.zeros(tree.num_leaves(), dtype=dtype), altitudes)))
    else:
        return ultrametric
Ejemplo n.º 2
0
    def test_pickle(self):
        import pickle

        tests = ((hg.RegularGraph1d, (3, ), ((-1, ), (1, ))),
                 (hg.RegularGraph2d, (3, 2), ((-1, 0), (1, 0))),
                 (hg.RegularGraph3d, (3, 2, 1),
                  ((-1, 0, -1), (1, 0, 1))), (hg.RegularGraph4d, (3, 2, 1, 2),
                                              ((-1, 0, -1, 0), (1, 0, 1, 0))),
                 (hg.RegularGraph5d, (3, 2, 1, 2, 1), ((-1, 0, -1, 0, 1),
                                                       (1, 0, 1, 0, -1))))

        for c, s, n in tests:
            e = c(s, n)
            hg.set_attribute(e, "test", (1, 2, 3))
            hg.add_tag(e, "foo")

            data = pickle.dumps(e)
            e2 = pickle.loads(data)

            self.assertTrue(np.all(e.shape() == e2.shape()))
            self.assertTrue(np.all(e.neighbour_list() == e2.neighbour_list()))

            self.assertTrue(
                hg.get_attribute(e, "test") == hg.get_attribute(e2, "test"))
            self.assertTrue(e.test == e2.test)
            self.assertTrue(hg.has_tag(e2, "foo"))
Ejemplo n.º 3
0
 def test_consumer_compoundPath(self):
     obj7 = Dummy(7)
     obj8 = Dummy(8)
     self.assertRaises(Exception, consumer3, obj7)
     hg.set_attribute(obj7, "dep", obj8)
     self.assertTrue(consumer3(obj7) == 3)
     self.assertTrue(hg.get_attribute(obj8, "attr2") == 2)
     self.assertTrue(hg.get_attribute(obj8, "attr1") == 1)
     hg.clear_all_attributes()
Ejemplo n.º 4
0
 def test_provider_rename_attribute(self):
     obj1 = Dummy(1)
     self.assertTrue(provider1(obj1, False, attribute_name="xxx") == 1)
     self.assertRaises(Exception, provider1, obj1, True)
     self.assertTrue(hg.get_attribute(obj1, "xxx") == 1)
     self.assertTrue(provider1(obj1, True, attribute_name="xxx") == 1)
     hg.clear_all_attributes()
Ejemplo n.º 5
0
    def test_BPT(self):
        graph = hg.get_4_adjacency_graph((2, 3))

        edge_weights = np.asarray((1, 0, 2, 1, 1, 1, 2))

        tree, altitudes = hg.bpt_canonical(graph, edge_weights)
        mst = hg.CptBinaryHierarchy.construct(tree)["mst"]
        mst_edge_map = hg.get_attribute(mst, "mst_edge_map")

        self.assertTrue(tree.num_vertices() == 11)
        self.assertTrue(tree.num_edges() == 10)
        self.assertTrue(
            np.allclose(tree.parents(), (6, 7, 9, 6, 8, 9, 7, 8, 10, 10, 10)))
        self.assertTrue(
            np.allclose(altitudes, (0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2)))
        self.assertTrue(mst.num_vertices() == 6)
        self.assertTrue(mst.num_edges() == 5)

        ref = [(0, 3), (0, 1), (1, 4), (2, 5), (1, 2)]
        test = []
        for e in mst.edges():
            test.append((e[0], e[1]))
        self.assertTrue(ref == test)

        self.assertTrue(np.all(mst_edge_map == (1, 0, 3, 4, 2)))
Ejemplo n.º 6
0
def __lowest_common_ancestor(self, vertices1, vertices2):
    """
    Compute the lowest common ancestor between pairs of vertices defined by :attr:`vertices1` and :attr:`vertices2`.

    :attr:`vertices1` and :attr:`vertices2` must be either:

    - two positive integers strictly smaller than the number of vertices in the tree;
    - two 1d arrays of positive integers strictly smaller than the number of vertices in the tree and of the same size.

    :Complexity:

    The worst case time complexity is :math:`\mathcal{O}(qn)` with :math:`q` the number of lowest ancestors to compute and :math:`n`
    the number of vertices in the tree.

    If many lowest ancestors are needed, this time complexity can be reduced to :math:`\mathcal{O}(q)` at the cost of a linearithmic
    time :math:`\\mathcal{O}(n\log(n))` preprocessing by calling the function :func:`~higra.Tree.lowest_common_ancestor_preprocess`.

    :param vertices1: a vertex index or an array of vertex indices
    :param vertices2: a vertex index or an array of vertex indices
    :return: the lowest common ancestor(s) of every pair of input vertices (a single index or an array of indices)
    """

    lca_fast = hg.get_attribute(self, "lca_fast")
    if lca_fast is None:
        return self._lowest_common_ancestor(vertices1, vertices2)
    else:
        return lca_fast.lca(vertices1, vertices2)
Ejemplo n.º 7
0
def get_nd_regular_graph(shape, neighbour_list):
    """
    Creates a regular graph of the given :attr:`shape` with the adjacency given as a
    :attr:`neighbour_list`.

    See the helper function :func:`~higra.mask_2_neighbours` to create a suitable :attr:`neighbour_list`.

    :Example:

    Create a 2d 4-adjacency implicit graph of size ``(13, 24)``:

    >>> graph = get_nd_regular_graph((13, 24), ((-1, 0), (0, -1), (0, 1), (1, 0)))

    Create a 3d 6-adjacency implicit graph of size ``(10, 13, 24)``:

    >>> mask = [[[0, 0, 0], [0, 1, 0], [0, 0, 0]],
    >>>         [[0, 1, 0], [1, 0, 1], [0, 1, 0]],
    >>>         [[0, 0, 0], [0, 1, 0], [0, 0, 0]]]
    >>> neighbours = mask_2_neighbours(mask)
    >>> graph = get_nd_regular_graph((10, 13, 24), neighbours)

    :param shape: a tuple of :math:`n` elements representing the dimension of the graph vertices.
    :param neighbour_list: a 2d array of :math:`k` :math:`n`-d integer vectors
    :return: a regular graph
    """

    graph_implicit = get_nd_regular_implicit_graph(shape, neighbour_list)
    graph = graph_implicit.as_explicit_graph()

    hg.CptGridGraph.link(graph, hg.CptGridGraph.get_shape(graph_implicit))
    hg.set_attribute(
        graph, "no_border_vertex_out_degree",
        hg.get_attribute(graph_implicit, "no_border_vertex_out_degree"))

    return graph
Ejemplo n.º 8
0
def project_fine_to_coarse_rag(fine_rag, coarse_rag):
    """
    Find for each region of the fine rag, the region of the
    coarse rag that maximises the intersection with the "fine" region.

    See: :func:`~higra.project_fine_to_coarse_labelisation`

    :param fine_rag: reference region adjacency graph (Concept :class:`~higra.CptRegionAdjacencyGraph`)
    :param coarse_rag: region adjacency graph to align (Concept :class:`~higra.CptRegionAdjacencyGraph`)
    :return: a 1d array of size ``fine_rag.num_vertices()``
    """
    return hg.project_fine_to_coarse_labelisation(
        hg.get_attribute(fine_rag, "vertex_map"),
        hg.get_attribute(coarse_rag, "vertex_map"),
        fine_rag.num_vertices(),
        coarse_rag.num_vertices())
Ejemplo n.º 9
0
def attribute_vertex_perimeter(graph, edge_length=None):
    """
    Vertex perimeter of the given graph.
    The perimeter of a vertex is defined as the sum of the length of out-edges of the vertex.

    If the input graph has an attribute value `no_border_vertex_out_degree`, then each vertex perimeter is assumed to be
    equal to this attribute value. This is a convenient method to handle image type graphs where an outer border has to be
    considered.

    :param graph: input graph
    :param edge_length: length of the edges of the input graph (provided by :func:`~higra.attribute_edge_length` on `graph`)
    :return: a nd array
    """
    if edge_length is None:
        edge_length = hg.attribute_edge_length(graph)

    special_case_border_graph = hg.get_attribute(
        graph, "no_border_vertex_out_degree")

    if special_case_border_graph is not None:
        res = np.full((graph.num_vertices(), ),
                      special_case_border_graph,
                      dtype=np.float64)
        res = hg.delinearize_vertex_weights(res, graph)
        return res

    res = hg.accumulate_graph_edges(graph, edge_length, hg.Accumulators.sum)
    res = hg.delinearize_vertex_weights(res, graph)
    return res
Ejemplo n.º 10
0
    def test_pickle(self):
        import pickle
        tree = hg.Tree((4, 4, 5, 5, 6, 6, 6))
        lca = hg.LCAFast(tree)
        hg.set_attribute(lca, "test", (1, 2, 3))
        hg.add_tag(lca, "foo")

        data = pickle.dumps(lca)
        lca2 = pickle.loads(data)

        res = lca2.lca((0, 0, 1, 3), (0, 3, 0, 0))
        self.assertTrue(np.all(res == (0, 6, 4, 6)))

        self.assertTrue(
            hg.get_attribute(lca, "test") == hg.get_attribute(lca2, "test"))
        self.assertTrue(lca.test == lca2.test)
        self.assertTrue(hg.has_tag(lca2, "foo"))
Ejemplo n.º 11
0
    def get_mst(tree):
        """
        The minimum spanning tree of the leaf graph of the hierarchy.

        :param tree:
        :return:
        """
        return hg.get_attribute(tree, "mst")
Ejemplo n.º 12
0
    def get_base_graph(mst):
        """
        The graph on which the given mst was computed.

        :param mst:
        :return:
        """
        return hg.get_attribute(mst, "base_graph")
Ejemplo n.º 13
0
    def get_edge_map(mst):
        """
        The map that gives for each edge index i of the mst, the corresponding edge index in the base graph

        :param mst:
        :return:
        """
        return hg.get_attribute(mst, "mst_edge_map")
Ejemplo n.º 14
0
    def get_shape(graph):
        """
        The shape (a tuple of positive integers) of the graph.

        :param graph:
        :return:
        """
        return hg.get_attribute(graph, "shape")
Ejemplo n.º 15
0
    def get_vertex_map(rag):
        """
        The map giving for each vertex of pre_graph of the given rag, i.e. the graph the rag was built on, the corresponding vertex of rag.

        :param rag:
        :return:
        """
        return hg.get_attribute(rag, "vertex_map")
Ejemplo n.º 16
0
    def get_leaf_graph(tree):
        """
        The graph the tree was built on, i.e. the graph associated to the leaves of the tree.

        :param tree:
        :return:
        """
        return hg.get_attribute(tree, "leaf_graph")
Ejemplo n.º 17
0
    def get_pre_graph(rag):
        """
        The graph the rag was built on.

        :param rag:
        :return:
        """
        return hg.get_attribute(rag, "pre_graph")
Ejemplo n.º 18
0
    def get_edge_map(rag):
        """
        The map giving for each edge of pre_graph of the given rag, i.e. the graph the rag was built on, the corresponding edge of rag (and -1 if not such edge exists).

        :param rag:
        :return:
        """
        return hg.get_attribute(rag, "edge_map")
Ejemplo n.º 19
0
    def test_pickle(self):
        import pickle
        g = TestUndirectedGraph.test_graph()
        hg.set_attribute(g, "test", (1, 2, 3))
        hg.add_tag(g, "foo")

        data = pickle.dumps(g)
        g2 = pickle.loads(data)

        self.assertTrue(g.num_vertices() == g2.num_vertices())
        gs, gt = g.edge_list()
        g2s, g2t = g2.edge_list()
        self.assertTrue(np.all(gs == g2s))
        self.assertTrue(np.all(gt == g2t))

        self.assertTrue(hg.get_attribute(g, "test") == hg.get_attribute(g2, "test"))
        self.assertTrue(g.test == g2.test)
        self.assertTrue(hg.has_tag(g2, "foo"))
Ejemplo n.º 20
0
    def test_pickle(self):
        import pickle
        t = hg.Tree((5, 5, 6, 6, 6, 7, 7, 7))
        hg.set_attribute(t, "test", (1, 2, 3))
        hg.add_tag(t, "foo")

        data = pickle.dumps(t)
        t2 = pickle.loads(data)

        self.assertTrue(np.all(t.parents() == t2.parents()))

        for v in t.vertices():
            self.assertTrue(np.all(t.children(v) == t2.children(v)))

        self.assertTrue(
            hg.get_attribute(t, "test") == hg.get_attribute(t2, "test"))
        self.assertTrue(t.test == t2.test)
        self.assertTrue(hg.has_tag(t2, "foo"))
Ejemplo n.º 21
0
    def test_consumer(self):
        obj3 = Dummy(3)
        self.assertRaises(Exception, consumer0, obj3)
        self.assertTrue(consumer0(obj3, 1) == 1)
        self.assertTrue(consumer0(obj3, attr0=1) == 1)
        hg.set_attribute(obj3, "attr0", 1)
        self.assertTrue(consumer0(obj3) == 1)
        hg.clear_all_attributes()

        obj4 = Dummy(4)
        self.assertTrue(consumer1(obj4) == 1)
        self.assertTrue(hg.get_attribute(obj4, "attr1") == 1)
        hg.clear_all_attributes()

        obj5 = Dummy(5)
        self.assertTrue(consumer2(obj5) == 1)
        self.assertTrue(hg.get_attribute(obj5, "attr1") == 1)
        hg.clear_all_attributes()
Ejemplo n.º 22
0
    def test_pickle(self):
        import pickle
        tree = hg.Tree((4, 4, 5, 5, 6, 6, 6))
        for lca_t in [hg.LCA_rmq_sparse_table, hg.LCA_rmq_sparse_table_block]:
            with self.subTest(lca_type=lca_t):
                lca = lca_t(tree)
                hg.set_attribute(lca, "test", (1, 2, 3))
                hg.add_tag(lca, "foo")

                data = pickle.dumps(lca)
                lca2 = pickle.loads(data)

                res = lca2.lca((0, 0, 1, 3), (0, 3, 0, 0))
                self.assertTrue(np.all(res == (0, 6, 4, 6)))

                self.assertTrue(hg.get_attribute(lca, "test") == hg.get_attribute(lca2, "test"))
                self.assertTrue(lca.test == lca2.test)
                self.assertTrue(hg.has_tag(lca2, "foo"))
Ejemplo n.º 23
0
    def get_mst_edge_map(tree):
        """
        The :math:`mst\_edge\_map` of the :attr:`tree` is an array that maps each internal vertex :math:`i` of the tree
        to the edge :math:`mst\_edge\_map[i - tree.num_leaves()]` of the leaf graph of the hierarchy corresponding
        to a minimum spanning tree edge.

        :param tree:
        :return:
        """
        return hg.get_attribute(tree, "mst_edge_map")
Ejemplo n.º 24
0
    def test_pickle(self):
        import pickle

        tests = ((hg.EmbeddingGrid1d, (3, )), (hg.EmbeddingGrid2d, (3, 2)),
                 (hg.EmbeddingGrid3d, (3, 2, 1)),
                 (hg.EmbeddingGrid4d, (3, 2, 1, 2)), (hg.EmbeddingGrid5d,
                                                      (3, 2, 1, 2, 1)))

        for c, s in tests:
            e = c(s)
            hg.set_attribute(e, "test", (1, 2, 3))
            hg.add_tag(e, "foo")

            data = pickle.dumps(e)
            e2 = pickle.loads(data)

            self.assertTrue(np.all(e.shape() == e2.shape()))

            self.assertTrue(
                hg.get_attribute(e, "test") == hg.get_attribute(e2, "test"))
            self.assertTrue(e.test == e2.test)
            self.assertTrue(hg.has_tag(e2, "foo"))
Ejemplo n.º 25
0
    def construct(cls, canonical_element, strict=True, data_cache=None):
        """
        Tries to construct a dictionary holding all the data elements of the concept associated to the given canonical element.

        :param canonical_element: an object
        :param strict: if `True` an exception is raised if all the data elements of the Concept cannot be recovered from the given canonical element, otherwise the missing data element is simply not included in the result.
        :param data_cache: specify a data cache where to search for data elements. If `None`, the default Higra global data cache is used.
        :return: a dictionary
        """
        if canonical_element is None:
            return {}
        if strict and not cls.validate(canonical_element):
            raise Exception("Cannot construct concept '" + str(cls) +
                            "': the element '" + str(canonical_element) +
                            "' does not satisfy this concept.")
        result = {cls._canonical_data_element: canonical_element}

        classes = inspect.getmro(cls)
        for c in classes:
            if issubclass(c, Concept) and c is not Concept:
                ce_name = c._canonical_data_element
                if ce_name not in result:
                    if strict:
                        raise Exception(
                            "Construction of concept '" + str(cls) +
                            "' failed: "
                            "cannot find canonical element named '" + ce_name +
                            "'('" + str(c) + "')")
                else:
                    ce = result[ce_name]
                    for data_name, data_description in c._data_elements.items(
                    ):
                        if data_description[1] is not None:
                            if data_cache is None:
                                elem = hg.get_attribute(
                                    ce, data_description[1])
                            else:
                                elem = data_cache.get_data(ce).get(
                                    data_description[1], None)
                            if elem is None:
                                if strict:
                                    raise Exception(
                                        "Construction of concept '" +
                                        str(cls) + "' failed: "
                                        "cannot find data element named '" +
                                        data_description[1] + "'('" + str(ce) +
                                        "')")
                            else:
                                result[data_name] = elem

        return result
Ejemplo n.º 26
0
    def get_mst(tree):
        """
        The minimum spanning tree of the leaf graph of the hierarchy.

        :param tree:
        :return:
        """
        mst = hg.get_attribute(tree, "mst")
        if mst is None:
            mst_edge_map = hg.CptBinaryHierarchy.get_mst_edge_map(tree)
            leaf_graph = hg.CptHierarchy.get_leaf_graph(tree)
            mst = hg.subgraph(leaf_graph, mst_edge_map, spanning=True)
            hg.CptMinimumSpanningTree.link(mst, leaf_graph, mst_edge_map)
            hg.set_attribute(tree, "mst", mst)
        return mst
Ejemplo n.º 27
0
def get_4_adjacency_graph(shape):
    """
    Create an explicit undirected 4 adjacency graph of the given shape.

    :param shape: a pair (height, width)
    :return: a graph (Concept :class:`~higra.CptGridGraph`)
    """
    graph_implicit = get_4_adjacency_implicit_graph(shape)
    graph = graph_implicit.as_explicit_graph()

    hg.CptGridGraph.link(graph, hg.CptGridGraph.get_shape(graph_implicit))
    hg.set_attribute(
        graph, "no_border_vertex_out_degree",
        hg.get_attribute(graph_implicit, "no_border_vertex_out_degree"))

    return graph
Ejemplo n.º 28
0
def __lowest_common_ancestor_preprocess(self):
    """
    Preprocess the tree to obtain a fast constant time :math:`\\mathcal{O}(1)` lowest common ancestor query.
    Once this function has been called on a given tree instance, every following calls to the function
    :func:`~higra.Tree.lowest_common_ancestor` will use this preprocessing.

    :Complexity:

    The preprocessing runs in linearithmic time  :math:`\\mathcal{O}(n\log(n))` with :math:`n` the number of vertices in the tree.

    :return: An object of type :class:`~higra.LCAFast`
    """
    lca_fast = hg.get_attribute(self, "lca_fast")
    if lca_fast is None:
        lca_fast = hg.LCAFast(self)
        hg.set_attribute(self, "lca_fast", lca_fast)
    return lca_fast
Ejemplo n.º 29
0
    def test_bpt_canonical_options(self):
        graph = hg.get_4_adjacency_graph((2, 3))
        edge_weights = np.asarray((1, 0, 2, 1, 1, 1, 2))
        sorted_edge_indices = np.asarray((1, 0, 3, 4, 5, 2, 6))
        ref_parents = np.asarray((6, 7, 9, 6, 8, 9, 7, 8, 10, 10, 10))
        ref_mst_edge_map = np.asarray((1, 0, 3, 4, 2))
        ref_altitudes = np.asarray((0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2))
        ref_altitudes_no_weights = np.asarray(
            (0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 5))

        tree = hg.bpt_canonical(graph,
                                edge_weights,
                                return_altitudes=False,
                                compute_mst=False)
        self.assertTrue(np.all(tree.parents() == ref_parents))
        self.assertTrue(hg.get_attribute(tree, "mst") is None)
        self.assertTrue(np.all(tree.mst_edge_map == ref_mst_edge_map))
        mst = hg.CptBinaryHierarchy.get_mst(tree)
        self.assertTrue(mst.num_vertices() == 6)
        self.assertTrue(mst.num_edges() == 5)
        self.assertTrue(np.all(mst.mst_edge_map == ref_mst_edge_map))

        tree, altitudes = hg.bpt_canonical(graph,
                                           edge_weights,
                                           return_altitudes=True,
                                           compute_mst=True)
        self.assertTrue(np.all(tree.parents() == ref_parents))
        self.assertTrue(tree.mst is not None)
        self.assertTrue(tree.mst.num_vertices() == 6)
        self.assertTrue(tree.mst.num_edges() == 5)
        self.assertTrue(np.all(tree.mst.mst_edge_map == ref_mst_edge_map))
        self.assertTrue(np.all(tree.mst_edge_map == ref_mst_edge_map))
        self.assertTrue(np.all(altitudes == ref_altitudes))

        tree, altitudes = hg.bpt_canonical(graph, edge_weights,
                                           sorted_edge_indices)
        self.assertTrue(np.all(tree.parents() == ref_parents))
        self.assertTrue(np.all(altitudes == ref_altitudes))

        tree, altitudes = hg.bpt_canonical(
            graph, sorted_edge_indices=sorted_edge_indices)
        self.assertTrue(np.all(tree.parents() == ref_parents))
        self.assertTrue(np.all(altitudes == ref_altitudes_no_weights))
Ejemplo n.º 30
0
    def test_graph_read(self):
        global graph_file
        graph, vertex_weights, edge_weights = hg.read_graph_pink(graph_file)

        shape = hg.get_attribute(graph, "shape")

        edges_ref = []
        for i in range(14):
            edges_ref.append((i, i + 1))

        vertex_weights_ref = np.arange(1, 16).reshape(3, 5)
        edges_weights_ref = (3, 0, 0, 1, 3, 0, 1, 0, 2, 0, 1, 0, 3, 0)

        edges = []
        for e in graph.edges():
            edges.append((e[0], e[1]))

        self.assertTrue(shape == [3, 5])
        self.assertTrue(edges == edges_ref)
        self.assertTrue(np.allclose(vertex_weights, vertex_weights_ref))
        self.assertTrue(np.allclose(edge_weights, edges_weights_ref))