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
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"))
def test_vertex_perimeter2(self): g = hg.get_4_adjacency_graph((2, 3)) hg.set_attribute(g, "no_border_vertex_out_degree", None) ref = np.asarray(((2, 3, 2), (2, 3, 2))) res = hg.attribute_vertex_perimeter(g) self.assertTrue(np.allclose(ref, res))
def get_nd_regular_implicit_graph(shape, neighbour_list): """ Creates an implicit 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_implicit_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_implicit_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: an implicit regular graph """ neighbour_list = np.asarray(neighbour_list) if not np.issubdtype(neighbour_list.dtype, np.integer): raise ValueError("'neighbour_list' must be of integral type.") if neighbour_list.ndim != 2: raise ValueError("'neighbour_list' must be a 2d array.") shape = hg.normalize_shape(shape) if len(shape) != neighbour_list.shape[1]: raise ValueError( "Shape size does not match provided adjacency dimension.") if len(shape) > 5 or len(shape) == 0: raise ValueError("Shape size must between 1 and 5 (included).") if len(shape) == 1: graph = hg.RegularGraph1d(shape, neighbour_list) elif len(shape) == 2: graph = hg.RegularGraph2d(shape, neighbour_list) elif len(shape) == 3: graph = hg.RegularGraph3d(shape, neighbour_list) elif len(shape) == 4: graph = hg.RegularGraph4d(shape, neighbour_list) elif len(shape) == 5: graph = hg.RegularGraph5d(shape, neighbour_list) hg.CptGridGraph.link(graph, shape) hg.set_attribute(graph, "no_border_vertex_out_degree", neighbour_list.shape[0]) return graph
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()
def test_contour_length_partition_tree2(self): tree, altitudes = TestAttributes.get_test_tree() hg.set_attribute(hg.CptHierarchy.get_leaf_graph(tree), "no_border_vertex_out_degree", None) ref_attribute = np.asarray( [2, 3, 2, 3, 4, 3, 2, 3, 2, 3, 3, 5, 3, 3, 4, 5, 0], dtype=np.float64) attribute = hg.attribute_contour_length(tree) self.assertTrue(np.allclose(ref_attribute, attribute))
def get_8_adjacency_implicit_graph(shape): """ Create an implicit undirected 8 adjacency graph of the given shape (edges are not stored). :param shape: a pair (height, width) :return: a graph (Concept :class:`~higra.CptGridGraph`) """ shape = hg.normalize_shape(shape) graph = hg.cpp._get_8_adjacency_implicit_graph(shape) hg.CptGridGraph.link(graph, shape) hg.set_attribute(graph, "no_border_vertex_out_degree", 8) return graph
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
def read_tree(filename): """ Read a tree stored in mixed ascii/binary format. Attributes are also registered as tree object attributes. :param filename: path to the tree file :return: a pair (tree, attribute_map) """ tree, attribute_map = hg.cpp._read_tree(filename) for k in attribute_map: hg.set_attribute(tree, k, attribute_map[k]) return tree, attribute_map
def test_contour_strength_partition_tree2(self): tree, altitudes = TestAttributes.get_test_tree() edge_weights = np.asarray((0, 6, 2, 6, 0, 0, 5, 4, 5, 3, 0, 1), dtype=np.float64) hg.set_attribute(hg.CptHierarchy.get_leaf_graph(tree), "no_border_vertex_out_degree", None) attribute = hg.attribute_contour_strength(tree, edge_weights) ref_perimeter = np.asarray( [2, 3, 2, 3, 4, 3, 2, 3, 2, 3, 3, 5, 3, 3, 4, 5, 1], dtype=np.float64) ref_weights = np.asarray( [6, 8, 2, 11, 15, 7, 5, 6, 4, 14, 9, 26, 11, 13, 19, 26, 0], dtype=np.float64) self.assertTrue(np.allclose(ref_weights / ref_perimeter, attribute))
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
def khalimsky_2_graph_4_adjacency(khalimsky, extra_border=False): """ Create a 4 adjacency edge-weighted graph from a contour image in the Khalimsky grid. :param khalimsky: a 2d array :param extra_border: if False the shape of the Khalimsky image is 2 * shape - 1 and 2 * shape + 1 otherwise, where shape is the shape of the resulting grid graph :return: a graph (Concept :class:`~higra.CptGridGraph`) and its edge weights """ graph, embedding, edge_weights = hg.cpp._khalimsky_2_graph_4_adjacency( khalimsky, extra_border) hg.CptGridGraph.link(graph, embedding.shape()) hg.set_attribute(graph, "no_border_vertex_out_degree", 4) return graph, edge_weights
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"))
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
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"))
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"))
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"))
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()
def get_4_adjacency_implicit_graph(shape): """ Create an implicit undirected 4 adjacency graph of the given shape (edges are not stored). :param shape: a pair (height, width) :return: a graph (Concept :class:`~higra.CptGridGraph`) """ shape = hg.normalize_shape(shape) if len(shape) != 2: raise ValueError("Shape must be a 1d array of size 2.") neighbours = np.array(((-1, 0), (0, -1), (0, 1), (1, 0)), dtype=np.int64) graph = hg.RegularGraph2d(shape, neighbours) hg.CptGridGraph.link(graph, shape) hg.set_attribute(graph, "no_border_vertex_out_degree", 4) return graph
def __lowest_common_ancestor_preprocess(self, algorithm="sparse_table_block", block_size=1024, force_recompute=False): """ 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. Calling twice this function does nothing except if :attr:`force_recompute` is ``True``. Two algorithms are available: - ``sparse_table`` has a preprocessing time and space complexity in :math:`\\mathcal{O}(n\log(n))` with :math:`n` the number of vertices in the tree and performs every query in constant time :math:`\\mathcal{O}(1)`. - ``sparse_table_block`` (default) has a linear preprocessing time and space complexity in :math:`\\mathcal{O}(n)` and performs queries in average-case constant time :math:`\\mathcal{O}(1)`. With this algorithm the user can specify the block size to be used, the general rule of thumb being that larger block size will decrease the pre-processing time but increase the query time. :param algorithm: specify the algorithm to be used, can be either ``sparse_table`` or ``sparse_table_block``. :param block_size: if :attr:`algorithm` is ``sparse_table_block``, specify the block size to be used (default 1024) :param force_recompute: if ``False`` (default) calling this function twice won't re-preprocess the tree, even if the specified algorithm or algorithm parameter have changed. :return: An object of type :class:`~higra.hg.LCA_rmq_sparse_table_block` or :class:`~higra.hg.LCA_rmq_sparse_table` """ lca_fast = hg.get_attribute(self, "lca_fast") if lca_fast is None or force_recompute: if algorithm == "sparse_table": lca_fast = hg.LCA_rmq_sparse_table(self) elif algorithm == "sparse_table_block": block_size = int(block_size) if block_size <= 0: raise ValueError("Invalid block size: " + str(block_size)) lca_fast = hg.LCA_rmq_sparse_table_block(self, block_size) else: raise ValueError("Unknown LCA algorithm: " + str(algorithm)) hg.set_attribute(self, "lca_fast", lca_fast) return lca_fast
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"))
def link(graph, shape): hg.add_tag(graph, CptGridGraph) hg.set_attribute(graph, "shape", shape)
def link(caA, ea1): hg.add_tag(caA, CptA) hg.set_attribute(caA, "ea1", ea1)
def link(rag, pre_graph, vertex_map, edge_map): hg.add_tag(rag, CptRegionAdjacencyGraph) hg.set_attribute(rag, "vertex_map", vertex_map) hg.set_attribute(rag, "edge_map", edge_map) hg.set_attribute(rag, "pre_graph", pre_graph)
def link(mst, base_graph, mst_edge_map): hg.add_tag(mst, CptMinimumSpanningTree) hg.set_attribute(mst, "base_graph", base_graph) hg.set_attribute(mst, "mst_edge_map", mst_edge_map)
def link(hierarchy, mst_edge_map, mst): hg.add_tag(hierarchy, CptBinaryHierarchy) hg.set_attribute(hierarchy, "mst_edge_map", mst_edge_map) hg.set_attribute(hierarchy, "mst", mst)
def link(caB, caA, eb2): hg.add_tag(caB, CptB) hg.set_attribute(caB, "caA", caA) hg.set_attribute(caB, "eb2", eb2)
def link(tree, leaf_graph): hg.add_tag(tree, CptHierarchy) hg.set_attribute(tree, "leaf_graph", leaf_graph)
def link(caC, ec1): hg.add_tag(caC, CptC) hg.set_attribute(caC, "ec1", ec1)
def test_perimeter_length_partition_tree2(self): tree, altitudes = TestAttributes.get_test_tree() hg.set_attribute(hg.CptHierarchy.get_leaf_graph(tree), "no_border_vertex_out_degree", None) ref_attribute = [2, 3, 2, 3, 4, 3, 2, 3, 2, 3, 3, 5, 3, 3, 4, 5, 0] attribute = hg.attribute_perimeter_length(tree, no_cache=True) self.assertTrue(np.allclose(ref_attribute, attribute))