dsa.draw

Module to access graphic drawing functions for Trees, Heaps, Tries and Graphs.

  1""" Module to access graphic drawing functions for Trees, Heaps, Tries and Graphs. """
  2
  3from dsa.tree import Tree, TreeNode
  4from dsa.heap import Heap
  5from dsa.trie import Trie
  6
  7import networkx as nx
  8import matplotlib.pyplot as plt
  9
 10class Draw:
 11    """
 12    A base class for drawing various data structures.
 13    
 14    This class provides basic functionalities for rendering, saving, and displaying
 15    visual representations of data structures.
 16    """
 17    def __init__(self):
 18        """
 19        Initialize the Draw class with default figure size.
 20        """
 21        self.figsize = (5, 3)
 22        
 23    def render(self, **kwargs):
 24        """
 25        Render the visual representation of the data structure.
 26        
 27        This method should be overridden by subclasses to provide specific rendering logic.
 28        Args:
 29            **kwargs: Additional keyword arguments.
 30        """
 31        pass
 32
 33    def set_figsize(self, figsize):
 34        """
 35        Set the figure size for the plot.
 36        
 37        Args:
 38            figsize (tuple): A tuple representing the figure size (width, height).
 39        """
 40        self.figsize = figsize
 41
 42    def save(self, filename, **kwargs):
 43        """
 44        Save the rendered plot to a file.
 45        
 46        Args:
 47            filename (str): The name of the file to save the plot to.
 48            **kwargs: Additional keyword arguments.
 49        """
 50        plt = self.render(**kwargs)
 51        plt.axis('off')
 52        plt.savefig(filename)
 53
 54    def draw(self, **kwargs):
 55        """
 56        Display the rendered plot.
 57
 58        Args:
 59            **kwargs: Additional keyword arguments.
 60        """
 61        plt = self.render(**kwargs)
 62        plt.axis('off')
 63        plt.show()
 64
 65class TreeDraw(Draw):
 66    """
 67    A class for drawing a tree structure using the NetworkX library.
 68
 69    This class extends the `Draw` class to visualize tree structures. It organizes the nodes
 70    in a hierarchical tree layout and provides options for customization through the `render` method.
 71
 72    Attributes:
 73        tree (Tree): The tree structure to be drawn.
 74
 75    Usage Example:
 76        t = Tree(root_node)  # Define your tree structure with a root node
 77        td = TreeDraw(t)
 78        td.draw()
 79    """
 80
 81    def __init__(self, tree: Tree):
 82        """ 
 83        Initializes the TreeDraw object.
 84
 85        Args:
 86            tree (Tree): The tree structure to be drawn.
 87        """
 88        super().__init__()
 89        self.tree = tree
 90        
 91    def add_edges(self, graph, node, pos=None, x: float=0, y: float=0, layer=1):
 92        """
 93        Recursively adds edges to the graph and positions the nodes in a tree layout.
 94    
 95        Args:
 96            graph (networkx.DiGraph): The graph object where edges are added.
 97            node (TreeNode): The current node in the tree.
 98            pos (dict, optional): A dictionary to store the positions of the nodes. Defaults to None.
 99            x (float): The x-coordinate for the current node. Defaults to 0.
100            y (float): The y-coordinate for the current node. Defaults to 0.
101            layer (int): The current layer/level of the node in the tree. Defaults to 1.
102
103        Returns:
104            dict: A dictionary containing the positions of all nodes in the tree.
105        """
106        if pos is None:
107            pos = {}
108        if node is not None:
109            pos[node.value] = (x, y)
110            if node.left:
111                graph.add_edge(node.value, node.left.value)
112                pos = self.add_edges(graph, node.left, pos=pos, x=x-1/layer, y=y-1, layer=layer+1)
113            if node.right:
114                graph.add_edge(node.value, node.right.value)
115                pos = self.add_edges(graph, node.right, pos=pos, x=x+1/layer, y=y-1, layer=layer+1)
116        return pos
117    
118    def render(self, **kwargs):
119        """ 
120        Renders the tree using Matplotlib. Not to be called directly. Call draw() instead.
121
122        This method generates a graphical representation of the tree with nodes positioned
123        in a hierarchical layout. Customization options can be provided via keyword arguments.
124
125        Args:
126            **kwargs: Additional keyword arguments for customization.
127
128        Returns:
129            matplotlib.pyplot: The Matplotlib plot object for further customization or display.
130        """
131        super().render(**kwargs)
132        graph = nx.DiGraph()
133        pos = self.add_edges(graph, self.tree.root)
134        plt.figure(figsize=self.figsize)
135        nx.draw(graph, pos, with_labels=True, arrows=False, node_color="tab:blue", node_size=800, font_size=12, font_color="white") 
136        return plt
137
138class HeapDraw(Draw):
139    """
140    A class for drawing a heap structure using the NetworkX library.
141
142    This class extends the `Draw` class to visualize heap structures, such as binary heaps or min-heaps.
143
144    Attributes:
145        heap (Heap): The heap structure to be drawn.
146
147    Usage Example:
148        h = MinHeap()  # Define your heap, e.g., MinHeap or Heap
149        hd = HeapDraw(h)
150        hd.draw()
151    """
152
153    def __init__(self, heap: Heap, **kwargs):
154        """
155        Initializes the HeapDraw object.
156
157        Args:
158            heap (Heap): The heap structure to be drawn.
159            **kwargs: Additional keyword arguments passed to the parent `Draw` class.
160        """
161        super().__init__(**kwargs)
162        self.heap = heap
163
164    def array_to_node(self, index: int, array):
165        """
166        Converts an array-based heap into a tree node structure.
167
168        This helper function recursively constructs a tree from the array representation
169        of the heap, reflecting the binary tree structure of the heap.
170
171        Args:
172            index (int): The current index in the array representing the node.
173            array (list): The array containing heap values, organized as a complete binary tree.
174
175        Returns:
176            TreeNode: The root node of the constructed subtree.
177        """
178        if index >= len(array):
179            return None
180        else:
181            value = array[index]
182            left_index = index * 2 + 1
183            right_index = index * 2 + 2
184            node = TreeNode(value)
185            node.left = self.array_to_node(left_index, array)
186            node.right = self.array_to_node(right_index, array)
187            return node
188
189    def render(self, **kwargs):
190        """
191        Renders the heap as a tree using Matplotlib. Not to be called directly. Call draw() instead.
192
193        This method converts the heap into a tree structure and then uses the `TreeDraw` class
194        to render it visually. Customization options can be provided via keyword arguments.
195
196        Returns:
197            matplotlib.pyplot: The Matplotlib plot object for further customization or display.
198        """
199        node = self.array_to_node(0, [node[1] for node in self.heap.enumerate()])
200        tree = Tree(node)
201
202        tree_draw = TreeDraw(tree)
203        tree_draw.set_figsize(self.figsize)
204        return tree_draw.render(**kwargs)
205    
206class TrieDraw(Draw):
207    """
208    A class for drawing a Trie structure using the NetworkX library.
209
210    This class extends the `Draw` class to visualize Trie structures, commonly used for storing strings
211    or prefix trees. It provides methods to convert the Trie into a networkx graph, arrange nodes
212    hierarchically, and render the visualization using Matplotlib.
213
214    Attributes:
215        trie (Trie): The Trie structure to be drawn.
216
217    Usage Example:
218        trie = Trie()  # Initialize your Trie and populate it with words
219        trd = TrieDraw(trie)
220        trd.draw()
221    """
222
223    def __init__(self, trie: Trie, **kwargs):
224        """
225        Initializes the TrieDraw object.
226
227        Args:
228            trie (Trie): The Trie structure to be drawn.
229            **kwargs: Additional keyword arguments passed to the parent `Draw` class.
230        """
231        super().__init__(**kwargs)
232        self.figsize = (10, 6)
233        self.trie = trie
234        
235    def _add_edges(self, graph, node, current_path):
236        """
237        Recursively adds edges to the networkx graph based on the Trie structure.
238
239        This helper function traverses the Trie, adding edges between nodes in the
240        networkx graph and associating them with characters from the Trie.
241
242        Args:
243            graph (networkx.DiGraph): The directed graph to which edges are added.
244            node (TrieNode): The current node in the Trie.
245            current_path (str): The path representing the current prefix in the Trie.
246        """
247        if node is None:
248            return
249        for char, child in node.children.items():
250            new_path = current_path + char
251            graph.add_edge(current_path, new_path, label=char)
252            self._add_edges(graph, child, new_path)
253    
254    def to_networkx(self) -> nx.DiGraph:
255        """
256        Converts the Trie into a NetworkX directed graph (DiGraph).
257
258        This method creates a networkx graph representation of the Trie, where each node
259        represents a prefix and each edge represents a character transition.
260
261        Returns:
262            networkx.DiGraph: The networkx graph representation of the Trie.
263        """
264        graph = nx.DiGraph()
265        self._add_edges(graph, self.trie.root, "")
266        return graph
267    
268    def hierarchical_pos(self, G, root=None, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5):
269        """
270        Computes the hierarchical position of nodes in the graph for visualization.
271
272        This method arranges the nodes of the Trie in a hierarchical layout, which is
273        particularly useful for visualizing tree-like structures such as Tries.
274
275        Args:
276            G (networkx.Graph): The graph for which to compute positions.
277            root (str, optional): The root node of the graph. Defaults to None, which means
278                                  the root will be determined automatically.
279            width (float): The width of the entire drawing. Defaults to 1.
280            vert_gap (float): The gap between levels in the hierarchy. Defaults to 0.2.
281            vert_loc (float): The vertical location of the root node. Defaults to 0.
282            xcenter (float): The horizontal center of the root node. Defaults to 0.5.
283
284        Returns:
285            dict: A dictionary mapping each node to its (x, y) position in the layout.
286        """
287        if root is None:
288            root = next(iter(nx.topological_sort(G)))
289    
290        def _hierarchical_pos(G, node, width: float=1., vert_gap: float=0.2, vert_loc:float=0, xcenter:float=0.5, pos=None, parent=None, parsed=[]):
291            if pos is None:
292                pos = {node: (xcenter, vert_loc)}
293            else:
294                pos[node] = (xcenter, vert_loc)
295    
296            children = list(G.neighbors(node))
297            if not isinstance(G, nx.DiGraph) and parent is not None:
298                children.remove(parent)
299    
300            if len(children) != 0:
301                dx = width / len(children)
302                nextx = xcenter - width / 2 - dx / 2
303                for child in children:
304                    nextx += dx
305                    pos = _hierarchical_pos(G, child, width=dx, vert_gap=vert_gap, vert_loc=vert_loc - vert_gap, xcenter=nextx, pos=pos, parent=node, parsed=parsed)
306            return pos
307    
308        return _hierarchical_pos(G, root, width, vert_gap, vert_loc, xcenter)
309    
310    def render_rectangle(self, **kwargs):
311        """
312        Renders the Trie as a hierarchical graph using Matplotlib. Not to be called directly. Call draw() instead.
313
314        This method uses the hierarchical positions of the nodes to render the Trie
315        as a directed graph. Nodes are drawn as rectangles, and edges represent the transitions
316        between prefixes.
317
318        Returns:
319            matplotlib.pyplot: The Matplotlib plot object for further customization or display.
320        """
321        super().render(**kwargs)
322        trie_graph = self.to_networkx()
323        
324        pos = self.hierarchical_pos(trie_graph)
325        
326        plt.figure(figsize=self.figsize)
327        nx.draw_networkx_edges(trie_graph, pos, arrows=True)
328        
329        ax = plt.gca()
330        rect_width = 0.05
331        rect_height = 0.15
332        for node in pos:
333            x, y = pos[node]
334            rectangle = plt.Rectangle((x - (rect_width / 2), y - (rect_height / 2)), rect_width, rect_height, color="tab:blue")
335            ax.add_patch(rectangle)
336            plt.text(x, y, node[-1] if node else "", verticalalignment='center', horizontalalignment='center', fontsize=12, color="white")
337        return plt
338
339    def render(self, **kwargs):
340        """
341        Renders the Trie as a hierarchical graph using Matplotlib. Not to be called directly. Call draw() instead.
342
343        This version uses NetworkX's default drawing tools with circular nodes for simplicity and clarity.
344        """
345        super().render(**kwargs)
346        trie_graph = self.to_networkx()
347        pos = self.hierarchical_pos(trie_graph)
348
349        plt.figure(figsize=self.figsize)
350
351        # Draw the graph using built-in node and edge drawing
352        nx.draw(
353            trie_graph,
354            pos,
355            with_labels=True,
356            labels={node: node[-1] if node else "" for node in trie_graph.nodes},
357            node_shape='o',
358            node_color='tab:blue',
359            font_color='white',
360            font_size=10,
361            edgecolors='none',
362            arrows=False,
363            **kwargs
364        )
365
366        return plt
367
368class GraphDraw(Draw):
369    """
370    A class for drawing graphs using the NetworkX library.
371
372    This class extends the `Draw` class to visualize graphs. It supports both directed 
373    and undirected graphs, as well as weighted and unweighted graphs. Additionally, 
374    it provides an option to display the Minimum Spanning Tree (MST) of the graph.
375
376    Attributes:
377        graph: The graph to be drawn
378        directed (bool): Specifies if the graph is directed. Defaults to False
379        weighted (bool): Specifies if the graph has weighted edges. Defaults to False
380
381    Usage Example:
382        gd = GraphDraw(g) # g is a Graph type (AdjacencyMatrixGraph, AdjacencyMatrixWeightedGraph, AdjacencyListWeightedGraph, AdjacencyListGraph)
383        gd.draw()
384    """
385    def __init__(self, graph, directed=False, weighted=False):
386        """
387        Initializes the GraphDraw object.
388        """
389        super().__init__()
390        self.graph = graph
391        self.directed = directed
392        self.weighted = weighted
393            
394    def render(self, pos=None, show_mst=False, mst_only=False, **kwargs):
395        """
396        Renders the graph using Matplotlib. Not to be called directly. Call draw() instead.
397        """
398        super().render(**kwargs)
399        edges = self.graph.edges()
400
401        if self.directed:
402            g = nx.DiGraph()
403        else:
404            g = nx.Graph()
405
406        if self.weighted:
407            g.add_weighted_edges_from(edges)
408        else:
409            g.add_edges_from(edges)
410    
411        if pos is None:
412            pos = nx.shell_layout(g) 
413
414        plt.figure(figsize=self.figsize)
415
416        if mst_only:
417            show_mst = True
418            nx.draw_networkx_nodes(g, pos, node_color="tab:blue", node_size=800)
419            nx.draw_networkx_labels(g, pos, font_size=10, font_color="white")
420        else:
421            nx.draw(g, pos, with_labels=True, node_color="tab:blue", node_size=800, font_size=10, font_color="white")
422
423        if self.weighted:
424            edge_labels = {(u, v): d["weight"] for u, v, d in g.edges(data=True)}
425            nx.draw_networkx_edge_labels(g, pos, edge_labels=edge_labels, font_size=14, label_pos=0.5)
426
427        if show_mst:
428            T = nx.minimum_spanning_tree(g)
429            nx.draw_networkx_edges(T, pos, edge_color="tab:red", width=2)
430        return plt
class Draw:
11class Draw:
12    """
13    A base class for drawing various data structures.
14    
15    This class provides basic functionalities for rendering, saving, and displaying
16    visual representations of data structures.
17    """
18    def __init__(self):
19        """
20        Initialize the Draw class with default figure size.
21        """
22        self.figsize = (5, 3)
23        
24    def render(self, **kwargs):
25        """
26        Render the visual representation of the data structure.
27        
28        This method should be overridden by subclasses to provide specific rendering logic.
29        Args:
30            **kwargs: Additional keyword arguments.
31        """
32        pass
33
34    def set_figsize(self, figsize):
35        """
36        Set the figure size for the plot.
37        
38        Args:
39            figsize (tuple): A tuple representing the figure size (width, height).
40        """
41        self.figsize = figsize
42
43    def save(self, filename, **kwargs):
44        """
45        Save the rendered plot to a file.
46        
47        Args:
48            filename (str): The name of the file to save the plot to.
49            **kwargs: Additional keyword arguments.
50        """
51        plt = self.render(**kwargs)
52        plt.axis('off')
53        plt.savefig(filename)
54
55    def draw(self, **kwargs):
56        """
57        Display the rendered plot.
58
59        Args:
60            **kwargs: Additional keyword arguments.
61        """
62        plt = self.render(**kwargs)
63        plt.axis('off')
64        plt.show()

A base class for drawing various data structures.

This class provides basic functionalities for rendering, saving, and displaying visual representations of data structures.

Draw()
18    def __init__(self):
19        """
20        Initialize the Draw class with default figure size.
21        """
22        self.figsize = (5, 3)

Initialize the Draw class with default figure size.

figsize
def render(self, **kwargs):
24    def render(self, **kwargs):
25        """
26        Render the visual representation of the data structure.
27        
28        This method should be overridden by subclasses to provide specific rendering logic.
29        Args:
30            **kwargs: Additional keyword arguments.
31        """
32        pass

Render the visual representation of the data structure.

This method should be overridden by subclasses to provide specific rendering logic. Args: **kwargs: Additional keyword arguments.

def set_figsize(self, figsize):
34    def set_figsize(self, figsize):
35        """
36        Set the figure size for the plot.
37        
38        Args:
39            figsize (tuple): A tuple representing the figure size (width, height).
40        """
41        self.figsize = figsize

Set the figure size for the plot.

Args: figsize (tuple): A tuple representing the figure size (width, height).

def save(self, filename, **kwargs):
43    def save(self, filename, **kwargs):
44        """
45        Save the rendered plot to a file.
46        
47        Args:
48            filename (str): The name of the file to save the plot to.
49            **kwargs: Additional keyword arguments.
50        """
51        plt = self.render(**kwargs)
52        plt.axis('off')
53        plt.savefig(filename)

Save the rendered plot to a file.

Args: filename (str): The name of the file to save the plot to. **kwargs: Additional keyword arguments.

def draw(self, **kwargs):
55    def draw(self, **kwargs):
56        """
57        Display the rendered plot.
58
59        Args:
60            **kwargs: Additional keyword arguments.
61        """
62        plt = self.render(**kwargs)
63        plt.axis('off')
64        plt.show()

Display the rendered plot.

Args: **kwargs: Additional keyword arguments.

class TreeDraw(Draw):
 66class TreeDraw(Draw):
 67    """
 68    A class for drawing a tree structure using the NetworkX library.
 69
 70    This class extends the `Draw` class to visualize tree structures. It organizes the nodes
 71    in a hierarchical tree layout and provides options for customization through the `render` method.
 72
 73    Attributes:
 74        tree (Tree): The tree structure to be drawn.
 75
 76    Usage Example:
 77        t = Tree(root_node)  # Define your tree structure with a root node
 78        td = TreeDraw(t)
 79        td.draw()
 80    """
 81
 82    def __init__(self, tree: Tree):
 83        """ 
 84        Initializes the TreeDraw object.
 85
 86        Args:
 87            tree (Tree): The tree structure to be drawn.
 88        """
 89        super().__init__()
 90        self.tree = tree
 91        
 92    def add_edges(self, graph, node, pos=None, x: float=0, y: float=0, layer=1):
 93        """
 94        Recursively adds edges to the graph and positions the nodes in a tree layout.
 95    
 96        Args:
 97            graph (networkx.DiGraph): The graph object where edges are added.
 98            node (TreeNode): The current node in the tree.
 99            pos (dict, optional): A dictionary to store the positions of the nodes. Defaults to None.
100            x (float): The x-coordinate for the current node. Defaults to 0.
101            y (float): The y-coordinate for the current node. Defaults to 0.
102            layer (int): The current layer/level of the node in the tree. Defaults to 1.
103
104        Returns:
105            dict: A dictionary containing the positions of all nodes in the tree.
106        """
107        if pos is None:
108            pos = {}
109        if node is not None:
110            pos[node.value] = (x, y)
111            if node.left:
112                graph.add_edge(node.value, node.left.value)
113                pos = self.add_edges(graph, node.left, pos=pos, x=x-1/layer, y=y-1, layer=layer+1)
114            if node.right:
115                graph.add_edge(node.value, node.right.value)
116                pos = self.add_edges(graph, node.right, pos=pos, x=x+1/layer, y=y-1, layer=layer+1)
117        return pos
118    
119    def render(self, **kwargs):
120        """ 
121        Renders the tree using Matplotlib. Not to be called directly. Call draw() instead.
122
123        This method generates a graphical representation of the tree with nodes positioned
124        in a hierarchical layout. Customization options can be provided via keyword arguments.
125
126        Args:
127            **kwargs: Additional keyword arguments for customization.
128
129        Returns:
130            matplotlib.pyplot: The Matplotlib plot object for further customization or display.
131        """
132        super().render(**kwargs)
133        graph = nx.DiGraph()
134        pos = self.add_edges(graph, self.tree.root)
135        plt.figure(figsize=self.figsize)
136        nx.draw(graph, pos, with_labels=True, arrows=False, node_color="tab:blue", node_size=800, font_size=12, font_color="white") 
137        return plt

A class for drawing a tree structure using the NetworkX library.

This class extends the Draw class to visualize tree structures. It organizes the nodes in a hierarchical tree layout and provides options for customization through the render method.

Attributes: tree (Tree): The tree structure to be drawn.

Usage Example: t = Tree(root_node) # Define your tree structure with a root node td = TreeDraw(t) td.draw()

TreeDraw(tree: dsa.tree.Tree)
82    def __init__(self, tree: Tree):
83        """ 
84        Initializes the TreeDraw object.
85
86        Args:
87            tree (Tree): The tree structure to be drawn.
88        """
89        super().__init__()
90        self.tree = tree

Initializes the TreeDraw object.

Args: tree (Tree): The tree structure to be drawn.

tree
def add_edges(self, graph, node, pos=None, x: float = 0, y: float = 0, layer=1):
 92    def add_edges(self, graph, node, pos=None, x: float=0, y: float=0, layer=1):
 93        """
 94        Recursively adds edges to the graph and positions the nodes in a tree layout.
 95    
 96        Args:
 97            graph (networkx.DiGraph): The graph object where edges are added.
 98            node (TreeNode): The current node in the tree.
 99            pos (dict, optional): A dictionary to store the positions of the nodes. Defaults to None.
100            x (float): The x-coordinate for the current node. Defaults to 0.
101            y (float): The y-coordinate for the current node. Defaults to 0.
102            layer (int): The current layer/level of the node in the tree. Defaults to 1.
103
104        Returns:
105            dict: A dictionary containing the positions of all nodes in the tree.
106        """
107        if pos is None:
108            pos = {}
109        if node is not None:
110            pos[node.value] = (x, y)
111            if node.left:
112                graph.add_edge(node.value, node.left.value)
113                pos = self.add_edges(graph, node.left, pos=pos, x=x-1/layer, y=y-1, layer=layer+1)
114            if node.right:
115                graph.add_edge(node.value, node.right.value)
116                pos = self.add_edges(graph, node.right, pos=pos, x=x+1/layer, y=y-1, layer=layer+1)
117        return pos

Recursively adds edges to the graph and positions the nodes in a tree layout.

Args: graph (networkx.DiGraph): The graph object where edges are added. node (TreeNode): The current node in the tree. pos (dict, optional): A dictionary to store the positions of the nodes. Defaults to None. x (float): The x-coordinate for the current node. Defaults to 0. y (float): The y-coordinate for the current node. Defaults to 0. layer (int): The current layer/level of the node in the tree. Defaults to 1.

Returns: dict: A dictionary containing the positions of all nodes in the tree.

def render(self, **kwargs):
119    def render(self, **kwargs):
120        """ 
121        Renders the tree using Matplotlib. Not to be called directly. Call draw() instead.
122
123        This method generates a graphical representation of the tree with nodes positioned
124        in a hierarchical layout. Customization options can be provided via keyword arguments.
125
126        Args:
127            **kwargs: Additional keyword arguments for customization.
128
129        Returns:
130            matplotlib.pyplot: The Matplotlib plot object for further customization or display.
131        """
132        super().render(**kwargs)
133        graph = nx.DiGraph()
134        pos = self.add_edges(graph, self.tree.root)
135        plt.figure(figsize=self.figsize)
136        nx.draw(graph, pos, with_labels=True, arrows=False, node_color="tab:blue", node_size=800, font_size=12, font_color="white") 
137        return plt

Renders the tree using Matplotlib. Not to be called directly. Call draw() instead.

This method generates a graphical representation of the tree with nodes positioned in a hierarchical layout. Customization options can be provided via keyword arguments.

Args: **kwargs: Additional keyword arguments for customization.

Returns: matplotlib.pyplot: The Matplotlib plot object for further customization or display.

Inherited Members
Draw
figsize
set_figsize
save
draw
class HeapDraw(Draw):
139class HeapDraw(Draw):
140    """
141    A class for drawing a heap structure using the NetworkX library.
142
143    This class extends the `Draw` class to visualize heap structures, such as binary heaps or min-heaps.
144
145    Attributes:
146        heap (Heap): The heap structure to be drawn.
147
148    Usage Example:
149        h = MinHeap()  # Define your heap, e.g., MinHeap or Heap
150        hd = HeapDraw(h)
151        hd.draw()
152    """
153
154    def __init__(self, heap: Heap, **kwargs):
155        """
156        Initializes the HeapDraw object.
157
158        Args:
159            heap (Heap): The heap structure to be drawn.
160            **kwargs: Additional keyword arguments passed to the parent `Draw` class.
161        """
162        super().__init__(**kwargs)
163        self.heap = heap
164
165    def array_to_node(self, index: int, array):
166        """
167        Converts an array-based heap into a tree node structure.
168
169        This helper function recursively constructs a tree from the array representation
170        of the heap, reflecting the binary tree structure of the heap.
171
172        Args:
173            index (int): The current index in the array representing the node.
174            array (list): The array containing heap values, organized as a complete binary tree.
175
176        Returns:
177            TreeNode: The root node of the constructed subtree.
178        """
179        if index >= len(array):
180            return None
181        else:
182            value = array[index]
183            left_index = index * 2 + 1
184            right_index = index * 2 + 2
185            node = TreeNode(value)
186            node.left = self.array_to_node(left_index, array)
187            node.right = self.array_to_node(right_index, array)
188            return node
189
190    def render(self, **kwargs):
191        """
192        Renders the heap as a tree using Matplotlib. Not to be called directly. Call draw() instead.
193
194        This method converts the heap into a tree structure and then uses the `TreeDraw` class
195        to render it visually. Customization options can be provided via keyword arguments.
196
197        Returns:
198            matplotlib.pyplot: The Matplotlib plot object for further customization or display.
199        """
200        node = self.array_to_node(0, [node[1] for node in self.heap.enumerate()])
201        tree = Tree(node)
202
203        tree_draw = TreeDraw(tree)
204        tree_draw.set_figsize(self.figsize)
205        return tree_draw.render(**kwargs)

A class for drawing a heap structure using the NetworkX library.

This class extends the Draw class to visualize heap structures, such as binary heaps or min-heaps.

Attributes: heap (Heap): The heap structure to be drawn.

Usage Example: h = MinHeap() # Define your heap, e.g., MinHeap or Heap hd = HeapDraw(h) hd.draw()

HeapDraw(heap: dsa.heap.Heap, **kwargs)
154    def __init__(self, heap: Heap, **kwargs):
155        """
156        Initializes the HeapDraw object.
157
158        Args:
159            heap (Heap): The heap structure to be drawn.
160            **kwargs: Additional keyword arguments passed to the parent `Draw` class.
161        """
162        super().__init__(**kwargs)
163        self.heap = heap

Initializes the HeapDraw object.

Args: heap (Heap): The heap structure to be drawn. **kwargs: Additional keyword arguments passed to the parent Draw class.

heap
def array_to_node(self, index: int, array):
165    def array_to_node(self, index: int, array):
166        """
167        Converts an array-based heap into a tree node structure.
168
169        This helper function recursively constructs a tree from the array representation
170        of the heap, reflecting the binary tree structure of the heap.
171
172        Args:
173            index (int): The current index in the array representing the node.
174            array (list): The array containing heap values, organized as a complete binary tree.
175
176        Returns:
177            TreeNode: The root node of the constructed subtree.
178        """
179        if index >= len(array):
180            return None
181        else:
182            value = array[index]
183            left_index = index * 2 + 1
184            right_index = index * 2 + 2
185            node = TreeNode(value)
186            node.left = self.array_to_node(left_index, array)
187            node.right = self.array_to_node(right_index, array)
188            return node

Converts an array-based heap into a tree node structure.

This helper function recursively constructs a tree from the array representation of the heap, reflecting the binary tree structure of the heap.

Args: index (int): The current index in the array representing the node. array (list): The array containing heap values, organized as a complete binary tree.

Returns: TreeNode: The root node of the constructed subtree.

def render(self, **kwargs):
190    def render(self, **kwargs):
191        """
192        Renders the heap as a tree using Matplotlib. Not to be called directly. Call draw() instead.
193
194        This method converts the heap into a tree structure and then uses the `TreeDraw` class
195        to render it visually. Customization options can be provided via keyword arguments.
196
197        Returns:
198            matplotlib.pyplot: The Matplotlib plot object for further customization or display.
199        """
200        node = self.array_to_node(0, [node[1] for node in self.heap.enumerate()])
201        tree = Tree(node)
202
203        tree_draw = TreeDraw(tree)
204        tree_draw.set_figsize(self.figsize)
205        return tree_draw.render(**kwargs)

Renders the heap as a tree using Matplotlib. Not to be called directly. Call draw() instead.

This method converts the heap into a tree structure and then uses the TreeDraw class to render it visually. Customization options can be provided via keyword arguments.

Returns: matplotlib.pyplot: The Matplotlib plot object for further customization or display.

Inherited Members
Draw
figsize
set_figsize
save
draw
class TrieDraw(Draw):
207class TrieDraw(Draw):
208    """
209    A class for drawing a Trie structure using the NetworkX library.
210
211    This class extends the `Draw` class to visualize Trie structures, commonly used for storing strings
212    or prefix trees. It provides methods to convert the Trie into a networkx graph, arrange nodes
213    hierarchically, and render the visualization using Matplotlib.
214
215    Attributes:
216        trie (Trie): The Trie structure to be drawn.
217
218    Usage Example:
219        trie = Trie()  # Initialize your Trie and populate it with words
220        trd = TrieDraw(trie)
221        trd.draw()
222    """
223
224    def __init__(self, trie: Trie, **kwargs):
225        """
226        Initializes the TrieDraw object.
227
228        Args:
229            trie (Trie): The Trie structure to be drawn.
230            **kwargs: Additional keyword arguments passed to the parent `Draw` class.
231        """
232        super().__init__(**kwargs)
233        self.figsize = (10, 6)
234        self.trie = trie
235        
236    def _add_edges(self, graph, node, current_path):
237        """
238        Recursively adds edges to the networkx graph based on the Trie structure.
239
240        This helper function traverses the Trie, adding edges between nodes in the
241        networkx graph and associating them with characters from the Trie.
242
243        Args:
244            graph (networkx.DiGraph): The directed graph to which edges are added.
245            node (TrieNode): The current node in the Trie.
246            current_path (str): The path representing the current prefix in the Trie.
247        """
248        if node is None:
249            return
250        for char, child in node.children.items():
251            new_path = current_path + char
252            graph.add_edge(current_path, new_path, label=char)
253            self._add_edges(graph, child, new_path)
254    
255    def to_networkx(self) -> nx.DiGraph:
256        """
257        Converts the Trie into a NetworkX directed graph (DiGraph).
258
259        This method creates a networkx graph representation of the Trie, where each node
260        represents a prefix and each edge represents a character transition.
261
262        Returns:
263            networkx.DiGraph: The networkx graph representation of the Trie.
264        """
265        graph = nx.DiGraph()
266        self._add_edges(graph, self.trie.root, "")
267        return graph
268    
269    def hierarchical_pos(self, G, root=None, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5):
270        """
271        Computes the hierarchical position of nodes in the graph for visualization.
272
273        This method arranges the nodes of the Trie in a hierarchical layout, which is
274        particularly useful for visualizing tree-like structures such as Tries.
275
276        Args:
277            G (networkx.Graph): The graph for which to compute positions.
278            root (str, optional): The root node of the graph. Defaults to None, which means
279                                  the root will be determined automatically.
280            width (float): The width of the entire drawing. Defaults to 1.
281            vert_gap (float): The gap between levels in the hierarchy. Defaults to 0.2.
282            vert_loc (float): The vertical location of the root node. Defaults to 0.
283            xcenter (float): The horizontal center of the root node. Defaults to 0.5.
284
285        Returns:
286            dict: A dictionary mapping each node to its (x, y) position in the layout.
287        """
288        if root is None:
289            root = next(iter(nx.topological_sort(G)))
290    
291        def _hierarchical_pos(G, node, width: float=1., vert_gap: float=0.2, vert_loc:float=0, xcenter:float=0.5, pos=None, parent=None, parsed=[]):
292            if pos is None:
293                pos = {node: (xcenter, vert_loc)}
294            else:
295                pos[node] = (xcenter, vert_loc)
296    
297            children = list(G.neighbors(node))
298            if not isinstance(G, nx.DiGraph) and parent is not None:
299                children.remove(parent)
300    
301            if len(children) != 0:
302                dx = width / len(children)
303                nextx = xcenter - width / 2 - dx / 2
304                for child in children:
305                    nextx += dx
306                    pos = _hierarchical_pos(G, child, width=dx, vert_gap=vert_gap, vert_loc=vert_loc - vert_gap, xcenter=nextx, pos=pos, parent=node, parsed=parsed)
307            return pos
308    
309        return _hierarchical_pos(G, root, width, vert_gap, vert_loc, xcenter)
310    
311    def render_rectangle(self, **kwargs):
312        """
313        Renders the Trie as a hierarchical graph using Matplotlib. Not to be called directly. Call draw() instead.
314
315        This method uses the hierarchical positions of the nodes to render the Trie
316        as a directed graph. Nodes are drawn as rectangles, and edges represent the transitions
317        between prefixes.
318
319        Returns:
320            matplotlib.pyplot: The Matplotlib plot object for further customization or display.
321        """
322        super().render(**kwargs)
323        trie_graph = self.to_networkx()
324        
325        pos = self.hierarchical_pos(trie_graph)
326        
327        plt.figure(figsize=self.figsize)
328        nx.draw_networkx_edges(trie_graph, pos, arrows=True)
329        
330        ax = plt.gca()
331        rect_width = 0.05
332        rect_height = 0.15
333        for node in pos:
334            x, y = pos[node]
335            rectangle = plt.Rectangle((x - (rect_width / 2), y - (rect_height / 2)), rect_width, rect_height, color="tab:blue")
336            ax.add_patch(rectangle)
337            plt.text(x, y, node[-1] if node else "", verticalalignment='center', horizontalalignment='center', fontsize=12, color="white")
338        return plt
339
340    def render(self, **kwargs):
341        """
342        Renders the Trie as a hierarchical graph using Matplotlib. Not to be called directly. Call draw() instead.
343
344        This version uses NetworkX's default drawing tools with circular nodes for simplicity and clarity.
345        """
346        super().render(**kwargs)
347        trie_graph = self.to_networkx()
348        pos = self.hierarchical_pos(trie_graph)
349
350        plt.figure(figsize=self.figsize)
351
352        # Draw the graph using built-in node and edge drawing
353        nx.draw(
354            trie_graph,
355            pos,
356            with_labels=True,
357            labels={node: node[-1] if node else "" for node in trie_graph.nodes},
358            node_shape='o',
359            node_color='tab:blue',
360            font_color='white',
361            font_size=10,
362            edgecolors='none',
363            arrows=False,
364            **kwargs
365        )
366
367        return plt

A class for drawing a Trie structure using the NetworkX library.

This class extends the Draw class to visualize Trie structures, commonly used for storing strings or prefix trees. It provides methods to convert the Trie into a networkx graph, arrange nodes hierarchically, and render the visualization using Matplotlib.

Attributes: trie (Trie): The Trie structure to be drawn.

Usage Example: trie = Trie() # Initialize your Trie and populate it with words trd = TrieDraw(trie) trd.draw()

TrieDraw(trie: dsa.trie.Trie, **kwargs)
224    def __init__(self, trie: Trie, **kwargs):
225        """
226        Initializes the TrieDraw object.
227
228        Args:
229            trie (Trie): The Trie structure to be drawn.
230            **kwargs: Additional keyword arguments passed to the parent `Draw` class.
231        """
232        super().__init__(**kwargs)
233        self.figsize = (10, 6)
234        self.trie = trie

Initializes the TrieDraw object.

Args: trie (Trie): The Trie structure to be drawn. **kwargs: Additional keyword arguments passed to the parent Draw class.

figsize
trie
def to_networkx(self) -> networkx.classes.digraph.DiGraph:
255    def to_networkx(self) -> nx.DiGraph:
256        """
257        Converts the Trie into a NetworkX directed graph (DiGraph).
258
259        This method creates a networkx graph representation of the Trie, where each node
260        represents a prefix and each edge represents a character transition.
261
262        Returns:
263            networkx.DiGraph: The networkx graph representation of the Trie.
264        """
265        graph = nx.DiGraph()
266        self._add_edges(graph, self.trie.root, "")
267        return graph

Converts the Trie into a NetworkX directed graph (DiGraph).

This method creates a networkx graph representation of the Trie, where each node represents a prefix and each edge represents a character transition.

Returns: networkx.DiGraph: The networkx graph representation of the Trie.

def hierarchical_pos(self, G, root=None, width=1.0, vert_gap=0.2, vert_loc=0, xcenter=0.5):
269    def hierarchical_pos(self, G, root=None, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5):
270        """
271        Computes the hierarchical position of nodes in the graph for visualization.
272
273        This method arranges the nodes of the Trie in a hierarchical layout, which is
274        particularly useful for visualizing tree-like structures such as Tries.
275
276        Args:
277            G (networkx.Graph): The graph for which to compute positions.
278            root (str, optional): The root node of the graph. Defaults to None, which means
279                                  the root will be determined automatically.
280            width (float): The width of the entire drawing. Defaults to 1.
281            vert_gap (float): The gap between levels in the hierarchy. Defaults to 0.2.
282            vert_loc (float): The vertical location of the root node. Defaults to 0.
283            xcenter (float): The horizontal center of the root node. Defaults to 0.5.
284
285        Returns:
286            dict: A dictionary mapping each node to its (x, y) position in the layout.
287        """
288        if root is None:
289            root = next(iter(nx.topological_sort(G)))
290    
291        def _hierarchical_pos(G, node, width: float=1., vert_gap: float=0.2, vert_loc:float=0, xcenter:float=0.5, pos=None, parent=None, parsed=[]):
292            if pos is None:
293                pos = {node: (xcenter, vert_loc)}
294            else:
295                pos[node] = (xcenter, vert_loc)
296    
297            children = list(G.neighbors(node))
298            if not isinstance(G, nx.DiGraph) and parent is not None:
299                children.remove(parent)
300    
301            if len(children) != 0:
302                dx = width / len(children)
303                nextx = xcenter - width / 2 - dx / 2
304                for child in children:
305                    nextx += dx
306                    pos = _hierarchical_pos(G, child, width=dx, vert_gap=vert_gap, vert_loc=vert_loc - vert_gap, xcenter=nextx, pos=pos, parent=node, parsed=parsed)
307            return pos
308    
309        return _hierarchical_pos(G, root, width, vert_gap, vert_loc, xcenter)

Computes the hierarchical position of nodes in the graph for visualization.

This method arranges the nodes of the Trie in a hierarchical layout, which is particularly useful for visualizing tree-like structures such as Tries.

Args: G (networkx.Graph): The graph for which to compute positions. root (str, optional): The root node of the graph. Defaults to None, which means the root will be determined automatically. width (float): The width of the entire drawing. Defaults to 1. vert_gap (float): The gap between levels in the hierarchy. Defaults to 0.2. vert_loc (float): The vertical location of the root node. Defaults to 0. xcenter (float): The horizontal center of the root node. Defaults to 0.5.

Returns: dict: A dictionary mapping each node to its (x, y) position in the layout.

def render_rectangle(self, **kwargs):
311    def render_rectangle(self, **kwargs):
312        """
313        Renders the Trie as a hierarchical graph using Matplotlib. Not to be called directly. Call draw() instead.
314
315        This method uses the hierarchical positions of the nodes to render the Trie
316        as a directed graph. Nodes are drawn as rectangles, and edges represent the transitions
317        between prefixes.
318
319        Returns:
320            matplotlib.pyplot: The Matplotlib plot object for further customization or display.
321        """
322        super().render(**kwargs)
323        trie_graph = self.to_networkx()
324        
325        pos = self.hierarchical_pos(trie_graph)
326        
327        plt.figure(figsize=self.figsize)
328        nx.draw_networkx_edges(trie_graph, pos, arrows=True)
329        
330        ax = plt.gca()
331        rect_width = 0.05
332        rect_height = 0.15
333        for node in pos:
334            x, y = pos[node]
335            rectangle = plt.Rectangle((x - (rect_width / 2), y - (rect_height / 2)), rect_width, rect_height, color="tab:blue")
336            ax.add_patch(rectangle)
337            plt.text(x, y, node[-1] if node else "", verticalalignment='center', horizontalalignment='center', fontsize=12, color="white")
338        return plt

Renders the Trie as a hierarchical graph using Matplotlib. Not to be called directly. Call draw() instead.

This method uses the hierarchical positions of the nodes to render the Trie as a directed graph. Nodes are drawn as rectangles, and edges represent the transitions between prefixes.

Returns: matplotlib.pyplot: The Matplotlib plot object for further customization or display.

def render(self, **kwargs):
340    def render(self, **kwargs):
341        """
342        Renders the Trie as a hierarchical graph using Matplotlib. Not to be called directly. Call draw() instead.
343
344        This version uses NetworkX's default drawing tools with circular nodes for simplicity and clarity.
345        """
346        super().render(**kwargs)
347        trie_graph = self.to_networkx()
348        pos = self.hierarchical_pos(trie_graph)
349
350        plt.figure(figsize=self.figsize)
351
352        # Draw the graph using built-in node and edge drawing
353        nx.draw(
354            trie_graph,
355            pos,
356            with_labels=True,
357            labels={node: node[-1] if node else "" for node in trie_graph.nodes},
358            node_shape='o',
359            node_color='tab:blue',
360            font_color='white',
361            font_size=10,
362            edgecolors='none',
363            arrows=False,
364            **kwargs
365        )
366
367        return plt

Renders the Trie as a hierarchical graph using Matplotlib. Not to be called directly. Call draw() instead.

This version uses NetworkX's default drawing tools with circular nodes for simplicity and clarity.

Inherited Members
Draw
set_figsize
save
draw
class GraphDraw(Draw):
369class GraphDraw(Draw):
370    """
371    A class for drawing graphs using the NetworkX library.
372
373    This class extends the `Draw` class to visualize graphs. It supports both directed 
374    and undirected graphs, as well as weighted and unweighted graphs. Additionally, 
375    it provides an option to display the Minimum Spanning Tree (MST) of the graph.
376
377    Attributes:
378        graph: The graph to be drawn
379        directed (bool): Specifies if the graph is directed. Defaults to False
380        weighted (bool): Specifies if the graph has weighted edges. Defaults to False
381
382    Usage Example:
383        gd = GraphDraw(g) # g is a Graph type (AdjacencyMatrixGraph, AdjacencyMatrixWeightedGraph, AdjacencyListWeightedGraph, AdjacencyListGraph)
384        gd.draw()
385    """
386    def __init__(self, graph, directed=False, weighted=False):
387        """
388        Initializes the GraphDraw object.
389        """
390        super().__init__()
391        self.graph = graph
392        self.directed = directed
393        self.weighted = weighted
394            
395    def render(self, pos=None, show_mst=False, mst_only=False, **kwargs):
396        """
397        Renders the graph using Matplotlib. Not to be called directly. Call draw() instead.
398        """
399        super().render(**kwargs)
400        edges = self.graph.edges()
401
402        if self.directed:
403            g = nx.DiGraph()
404        else:
405            g = nx.Graph()
406
407        if self.weighted:
408            g.add_weighted_edges_from(edges)
409        else:
410            g.add_edges_from(edges)
411    
412        if pos is None:
413            pos = nx.shell_layout(g) 
414
415        plt.figure(figsize=self.figsize)
416
417        if mst_only:
418            show_mst = True
419            nx.draw_networkx_nodes(g, pos, node_color="tab:blue", node_size=800)
420            nx.draw_networkx_labels(g, pos, font_size=10, font_color="white")
421        else:
422            nx.draw(g, pos, with_labels=True, node_color="tab:blue", node_size=800, font_size=10, font_color="white")
423
424        if self.weighted:
425            edge_labels = {(u, v): d["weight"] for u, v, d in g.edges(data=True)}
426            nx.draw_networkx_edge_labels(g, pos, edge_labels=edge_labels, font_size=14, label_pos=0.5)
427
428        if show_mst:
429            T = nx.minimum_spanning_tree(g)
430            nx.draw_networkx_edges(T, pos, edge_color="tab:red", width=2)
431        return plt

A class for drawing graphs using the NetworkX library.

This class extends the Draw class to visualize graphs. It supports both directed and undirected graphs, as well as weighted and unweighted graphs. Additionally, it provides an option to display the Minimum Spanning Tree (MST) of the graph.

Attributes: graph: The graph to be drawn directed (bool): Specifies if the graph is directed. Defaults to False weighted (bool): Specifies if the graph has weighted edges. Defaults to False

Usage Example: gd = GraphDraw(g) # g is a Graph type (AdjacencyMatrixGraph, AdjacencyMatrixWeightedGraph, AdjacencyListWeightedGraph, AdjacencyListGraph) gd.draw()

GraphDraw(graph, directed=False, weighted=False)
386    def __init__(self, graph, directed=False, weighted=False):
387        """
388        Initializes the GraphDraw object.
389        """
390        super().__init__()
391        self.graph = graph
392        self.directed = directed
393        self.weighted = weighted

Initializes the GraphDraw object.

graph
directed
weighted
def render(self, pos=None, show_mst=False, mst_only=False, **kwargs):
395    def render(self, pos=None, show_mst=False, mst_only=False, **kwargs):
396        """
397        Renders the graph using Matplotlib. Not to be called directly. Call draw() instead.
398        """
399        super().render(**kwargs)
400        edges = self.graph.edges()
401
402        if self.directed:
403            g = nx.DiGraph()
404        else:
405            g = nx.Graph()
406
407        if self.weighted:
408            g.add_weighted_edges_from(edges)
409        else:
410            g.add_edges_from(edges)
411    
412        if pos is None:
413            pos = nx.shell_layout(g) 
414
415        plt.figure(figsize=self.figsize)
416
417        if mst_only:
418            show_mst = True
419            nx.draw_networkx_nodes(g, pos, node_color="tab:blue", node_size=800)
420            nx.draw_networkx_labels(g, pos, font_size=10, font_color="white")
421        else:
422            nx.draw(g, pos, with_labels=True, node_color="tab:blue", node_size=800, font_size=10, font_color="white")
423
424        if self.weighted:
425            edge_labels = {(u, v): d["weight"] for u, v, d in g.edges(data=True)}
426            nx.draw_networkx_edge_labels(g, pos, edge_labels=edge_labels, font_size=14, label_pos=0.5)
427
428        if show_mst:
429            T = nx.minimum_spanning_tree(g)
430            nx.draw_networkx_edges(T, pos, edge_color="tab:red", width=2)
431        return plt

Renders the graph using Matplotlib. Not to be called directly. Call draw() instead.

Inherited Members
Draw
figsize
set_figsize
save
draw