From dd38a55c77df6b1b4f535f0d31cb643a75246518 Mon Sep 17 00:00:00 2001 From: Jeevan Iyadurai Date: Mon, 17 Feb 2025 17:31:15 -0500 Subject: [PATCH 1/2] Adds bs_tree.py and BSTree class --- src/manim_data_structures/.bs_tree.py.swp | Bin 0 -> 20480 bytes src/manim_data_structures/.nary_tree.py.swp | Bin 0 -> 12288 bytes src/manim_data_structures/TestScene.mp4 | Bin 0 -> 19151 bytes src/manim_data_structures/bs_tree.py | 144 ++++++++++++++++++++ src/manim_data_structures/m_tree.py | 144 ++++++++++++++++++++ src/manim_data_structures/nary_tree.py | 119 ++++++++++++++++ 6 files changed, 407 insertions(+) create mode 100644 src/manim_data_structures/.bs_tree.py.swp create mode 100644 src/manim_data_structures/.nary_tree.py.swp create mode 100644 src/manim_data_structures/TestScene.mp4 create mode 100644 src/manim_data_structures/bs_tree.py create mode 100644 src/manim_data_structures/m_tree.py create mode 100644 src/manim_data_structures/nary_tree.py diff --git a/src/manim_data_structures/.bs_tree.py.swp b/src/manim_data_structures/.bs_tree.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..23ba17cfcdb29b350c07249e9d3216389b362390 GIT binary patch literal 20480 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 literal 0 HcmV?d00001 diff --git a/src/manim_data_structures/.nary_tree.py.swp b/src/manim_data_structures/.nary_tree.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..ba510f4ffc4489415bb13f23a20bf3711841c71b GIT binary patch literal 12288 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 literal 0 HcmV?d00001 diff --git a/src/manim_data_structures/bs_tree.py b/src/manim_data_structures/bs_tree.py new file mode 100644 index 0000000..4213abe --- /dev/null +++ b/src/manim_data_structures/bs_tree.py @@ -0,0 +1,144 @@ +from typing import Any, Callable, Hashable + +import nary_tree as nt +import networkx as nx +from manim import Mobject + + +class BSTree(nt.NaryTree): + + # BSTree is a NaryTree with num_children=2. + # BSTree also keeps track of the values of the nodes in the tree with __values + def __init__( + self, + nodes: dict[int, Any], + vertex_type: Callable[..., Mobject], + edge_buff=0.4, + layout_config={"vertex_spacing": (0.6, -0.6)}, + **kwargs + ): + self.__values = nodes + super().__init__(nodes, 2, vertex_type, edge_buff, layout_config, **kwargs) + + def insert_node(self, node: Any): + """ + Inserts a node into the BST, overriding definition in NaryTree + """ + # find the place to insert based on BST properties, starts at index=0 + ind = self.find_insertion_index(node) + # take note of the new node's value + self.__values[ind] = node + # insert the new node into the correct position using NaryTree's implementation + return super().insert_node(node, ind) + + def remove_node(self, node: Any): + """ + Removes a node with value 'node' from the BST + """ + ind = -1 + # does not support removing the root + for k, v in self.__values.items(): + if k != 0 and v == node: + ind = k + break + if ind == -1: + return + removal_ind = ind + # if left child exists + if ind * 2 + 1 in self.__values: + ind = ind * 2 + 1 + # find rightmost + while ind * 2 + 2 in self.__values: + ind = ind * 2 + 2 + # swap the rightmost down its left + while ind * 2 + 1 in self.__values: + self.__values[ind], self.__values[ind * 2 + 1] = ( + self.__values[ind * 2 + 1], + self.__values[ind], + ) + ind = ind * 2 + 1 + # remove + self.__values[ind], self.__values[removal_ind] = ( + self.__values[removal_ind], + self.__values[ind], + ) + del self.__values[ind] + self._graph.remove_vertices(ind) + # get rid of graph + for vert in self.__values: + if vert != 0: + self._graph.remove_vertices(vert) + # rebuild; this also makes edges + for k, v in self.__values.items(): + if k != 0: + super().insert_node(v, k) + # choose right child if left is not present + elif ind * 2 + 2 in self._graph.vertices: + ind = ind * 2 + 2 + while ind * 2 + 1 in self.__values: + ind = ind * 2 + 1 + while ind * 2 + 2 in self.__values: + self.__values[ind], self.__values[ind * 2 + 2] = ( + self.__values[ind * 2 + 2], + self.__values[ind], + ) + ind = ind * 2 + 2 + self.__values[ind], self.__values[removal_ind] = ( + self.__values[removal_ind], + self.__values[ind], + ) + del self.__values[ind] + self._graph.remove_vertices(ind) + + for vert in self.__values: + if vert != 0: + self._graph.remove_vertices(vert) + for k, v in self.__values.items(): + if k != 0: + super().insert_node(v, k) + # just delete it if it has no children + else: + self._graph.remove_vertices(removal_ind) + del self.__values[removal_ind] + + def find_insertion_index(self, node: Any, index=0): + """ + Finds the position where a value 'node' should be placed + """ + # if we reach an index not yet created, we are done + if index not in self._graph.vertices: + return index + # otherwise, try to find position at left or right based on value + if node <= self.__values[index]: + return self.find_insertion_index(node, index * 2 + 1) + else: + return self.find_insertion_index(node, index * 2 + 2) + + +if __name__ == "__main__": + from manim import * + + class TestScene(Scene): + def construct(self): + tree = BSTree({0: 10}, vertex_type=Integer) + self.play(Create(tree)) + tree.insert_node(5) + self.wait() + tree.insert_node(15) + self.wait() + tree.insert_node(20) + self.wait() + tree.insert_node(12) + self.wait() + tree.insert_node(14) + self.wait() + tree.insert_node(13) + self.wait() + tree.remove_node(15) + self.wait() + self.wait() + + config.preview = True + config.renderer = "cairo" + config.quality = "high_quality" + TestScene().render() diff --git a/src/manim_data_structures/m_tree.py b/src/manim_data_structures/m_tree.py new file mode 100644 index 0000000..625ac5a --- /dev/null +++ b/src/manim_data_structures/m_tree.py @@ -0,0 +1,144 @@ +import operator as op +import random +from collections import defaultdict +from copy import copy +from functools import partialmethod, reduce +from typing import Any, Callable, Dict, Hashable, List, Tuple + +import numpy as np +from manim import * +from manim import WHITE, Graph, Mobject, VMobject + + +class Tree(VMobject): + """Computer Science Tree Data Structure""" + + _graph: Graph + __layout_config: dict + __layout_scale: float + __layout: str | dict + __vertex_type: Callable[..., Mobject] + + # __parents: list + # __children: dict[Hashable, list] = defaultdict(list) + + def __init__( + self, + nodes: dict[int, Any], + edges: list[tuple[int, int]], + vertex_type: Callable[..., Mobject], + edge_buff=0.4, + layout="tree", + layout_config={"vertex_spacing": (-1, 1)}, + root_vertex=0, + **kwargs + ): + super().__init__(**kwargs) + vertex_mobjects = {k: vertex_type(v) for k, v in nodes.items()} + self.__layout_config = layout_config + self.__layout_scale = len(nodes) * 0.5 + self.__layout = layout + self.__vertex_type = vertex_type + self._graph = Graph( + list(nodes), + edges, + vertex_mobjects=vertex_mobjects, + layout=layout, + root_vertex=0, + layout_config=self.__layout_config, + layout_scale=len(nodes) * 0.5, + edge_config={"stroke_width": 1, "stroke_color": WHITE}, + ) + + def update_edges(graph: Graph): + """Updates edges of graph""" + for (u, v), edge in graph.edges.items(): + buff_vec = ( + edge_buff + * (graph[u].get_center() - graph[v].get_center()) + / np.linalg.norm(graph[u].get_center() - graph[v].get_center()) + ) + edge.put_start_and_end_on( + graph[u].get_center() - buff_vec, graph[v].get_center() + buff_vec + ) + + self._graph.updaters.clear() + self._graph.updaters.append(update_edges) + self.add(self._graph) + + def insert_node(self, node: Any, edge: tuple[Hashable, Hashable]): + """Inserts a node into the graph as (parent, node)""" + self._graph.add_vertices( + edge[1], vertex_mobjects={edge[1]: self.__vertex_type(node)} + ) + self._graph.add_edges(edge) + return self + + def insert_node2(self, node: Any, edge: tuple[Hashable, Hashable]): + """Inserts a node into the graph as (parent, node)""" + self._graph.change_layout( + self.__layout, + layout_scale=self.__layout_scale, + layout_config=self.__layout_config, + root_vertex=0, + ) + for mob in self.family_members_with_points(): + if (mob.get_center() == self._graph[edge[1]].get_center()).all(): + mob.points = mob.points.astype("float") + return self + + def insert_node3(self, node: Any, edge: tuple[Hashable, Hashable]): + """Inserts a node into the graph as (parent, node)""" + self.suspend_updating() + self.insert_node(node, edge) + # self.resume_updating() + self.insert_node2(node, edge) + + return self + + def remove_node(self, node: Hashable): + """Removes a node from the graph""" + self._graph.remove_vertices(node) + + # def insert_node2(self): + # """Shift by the given vectors. + # + # Parameters + # ---------- + # vectors + # Vectors to shift by. If multiple vectors are given, they are added + # together. + # + # Returns + # ------- + # :class:`Mobject` + # ``self`` + # + # See also + # -------- + # :meth:`move_to` + # """ + # + # total_vector = reduce(op.add, vectors) + # for mob in self.family_members_with_points(): + # mob.points = mob.points.astype("float") + # mob.points += total_vector + # + # return self + + +if __name__ == "__main__": + + class TestScene(Scene): + def construct(self): + # make a parent list for a tree + tree = Tree({0: 0, 1: 1, 2: 2, 3: 3}, [(0, 1), (0, 2), (1, 3)], Integer) + self.play(Create(tree)) + self.wait() + self.play(tree.animate.insert_node3(4, (2, 4)), run_time=0) + self.wait() + + config.preview = True + config.renderer = "cairo" + config.quality = "low_quality" + TestScene().render(preview=True) diff --git a/src/manim_data_structures/nary_tree.py b/src/manim_data_structures/nary_tree.py new file mode 100644 index 0000000..a4fc9c4 --- /dev/null +++ b/src/manim_data_structures/nary_tree.py @@ -0,0 +1,119 @@ +from typing import Any, Callable, Hashable + +import networkx as nx +import numpy as np +from m_tree import Tree +from manim import Mobject + + +def _nary_layout( + T: nx.classes.graph.Graph, + vertex_spacing: tuple | None = None, + n: int | None = None, +): + if not n: + raise ValueError("the n-ary tree layout requires the n parameter") + if not nx.is_tree(T): + raise ValueError("The tree layout must be used with trees") + + max_height = NaryTree.calc_loc(max(T), n)[1] + + def calc_pos(x, y): + """ + Scales the coordinates to the desired spacing + """ + return (x - (n**y - 1) / 2) * vertex_spacing[0] * n ** ( + max_height - y + ), y * vertex_spacing[1] + + return { + i: np.array([x, y, 0]) + for i, (x, y) in ((i, calc_pos(*NaryTree.calc_loc(i, n))) for i in T) + } + + +class NaryTree(Tree): + def __init__( + self, + nodes: dict[int, Any], + num_child: int, + vertex_type: Callable[..., Mobject], + edge_buff=0.4, + layout_config=None, + **kwargs + ): + if layout_config is None: + layout_config = {"vertex_spacing": (-1, 1)} + self.__layout_config = layout_config + self.num_child = num_child + + edges = [(self.get_parent(e), e) for e in nodes if e != 0] + super().__init__(nodes, edges, vertex_type, edge_buff, **kwargs) + dict_layout = _nary_layout(self._graph._graph, n=num_child, **layout_config) + self._graph.change_layout(dict_layout) + + @staticmethod + def calc_loc(i, n): + """ + Calculates the coordinates in terms of the shifted level order x position and level height + """ + if n == 1: + return 1, i + 1 + height = int(np.emath.logn(n, i * (n - 1) + 1)) + node_shift = (1 - n**height) // (1 - n) + return i - node_shift, height + + @staticmethod + def calc_idx(loc, n): + """ + Calculates the index from the coordinates + """ + x, y = loc + if n == 1: + return y - 1 + + return int(x + (1 - n**y) // (1 - n)) + + def get_parent(self, idx): + """ + Returns the index of the parent of the node at the given index + """ + x, y = NaryTree.calc_loc(idx, self.num_child) + new_loc = x // self.num_child, y - 1 + return NaryTree.calc_idx(new_loc, self.num_child) + + def insert_node(self, node: Any, index: Hashable): + """Inserts a node into the graph""" + res = super().insert_node(node, (self.get_parent(index), index)) + dict_layout = _nary_layout( + self._graph._graph, n=self.num_child, **self.__layout_config + ) + self._graph.change_layout(dict_layout) + self.update() + return res + + +if __name__ == "__main__": + from manim import * + + class TestScene(Scene): + def construct(self): + tree = NaryTree( + {0: 0, 1: 1, 4: 4}, + num_child=2, + vertex_type=Integer, + layout_config={"vertex_spacing": (1, -1)}, + ) + # tree._graph.change_layout(root_vertex=0, layout_config=tree._Tree__layout_config, + # layout_scale=tree._Tree__layout_scale) + self.play(Create(tree)) + self.wait() + tree.insert_node(3, 3) + self.wait() + tree.remove_node(4) + self.wait() + + config.preview = True + config.renderer = "cairo" + config.quality = "low_quality" + TestScene().render(preview=True) From df93d0792fe0e00e21c4609c6d164a5687969f11 Mon Sep 17 00:00:00 2001 From: Jeevan Iyadurai Date: Mon, 17 Feb 2025 18:12:05 -0500 Subject: [PATCH 2/2] Removes unwanted files --- src/manim_data_structures/.bs_tree.py.swp | Bin 20480 -> 0 bytes src/manim_data_structures/.nary_tree.py.swp | Bin 12288 -> 0 bytes src/manim_data_structures/TestScene.mp4 | Bin 19151 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/manim_data_structures/.bs_tree.py.swp delete mode 100644 src/manim_data_structures/.nary_tree.py.swp delete mode 100644 src/manim_data_structures/TestScene.mp4 diff --git a/src/manim_data_structures/.bs_tree.py.swp b/src/manim_data_structures/.bs_tree.py.swp deleted file mode 100644 index 23ba17cfcdb29b350c07249e9d3216389b362390..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 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 diff --git a/src/manim_data_structures/.nary_tree.py.swp b/src/manim_data_structures/.nary_tree.py.swp deleted file mode 100644 index ba510f4ffc4489415bb13f23a20bf3711841c71b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 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