import gradio as gr import matplotlib.pyplot as plt import numpy as np from io import BytesIO import networkx as nx from collections import deque import traceback class DSAVisualizer: def __init__(self): self.reset_state() def reset_state(self): self.current_data = [] self.steps = [] self.current_step = 0 self.pseudocode = [] self.active_lines = [] self.graph_dict = {} self.error_message = "" def generate_data(self, data_size=10, data_type="Random"): try: if data_size < 5: data_size = 5 elif data_size > 50: data_size = 50 if data_type == "Random": self.current_data = np.random.randint(1, 100, data_size) elif data_type == "Ascending": self.current_data = np.arange(1, data_size + 1) elif data_type == "Descending": self.current_data = np.arange(data_size, 0, -1) elif data_type == "Nearly Sorted": self.current_data = np.arange(1, data_size + 1) for _ in range(max(1, data_size // 10)): i, j = np.random.randint(0, data_size, 2) self.current_data[i], self.current_data[j] = self.current_data[j], self.current_data[i] return list(self.current_data) except Exception as e: self.error_message = f"Data generation error: {str(e)}" return [] def bubble_sort(self, arr): try: arr = [int(x) for x in arr] # Ensure integers n = len(arr) steps = [arr.copy()] pseudocode = [ "procedure bubbleSort(A : list)", " n = length(A)", " repeat", " swapped = false", " for i from 1 to n-1:", " if A[i-1] > A[i]:", " swap(A[i-1], A[i])", " swapped = true", " until not swapped" ] active_line = [0] swapped = True while swapped: swapped = False for j in range(1, n): active_line.append(5) if steps[-1][j-1] > steps[-1][j]: active_line.append(6) new_step = steps[-1].copy() new_step[j-1], new_step[j] = new_step[j], new_step[j-1] steps.append(new_step) swapped = True active_line.append(7) active_line.append(4) active_line.append(3) return steps, pseudocode, active_line except Exception as e: self.error_message = f"Bubble sort error: {str(e)}" return [], [], [] def insertion_sort(self, arr): try: arr = [int(x) for x in arr] # Ensure integers steps = [arr.copy()] pseudocode = [ "procedure insertionSort(A : list)", " for j from 1 to length(A)-1:", " key = A[j]", " i = j-1", " while i >= 0 and A[i] > key:", " A[i+1] = A[i]", " i = i-1", " A[i+1] = key" ] active_line = [0] arr = arr.copy() n = len(arr) for j in range(1, n): active_line.append(1) key = arr[j] active_line.append(2) i = j-1 active_line.append(3) while i >= 0 and arr[i] > key: active_line.append(5) arr[i+1] = arr[i] steps.append(arr.copy()) active_line.append(6) i = i-1 active_line.append(3) active_line.append(7) arr[i+1] = key steps.append(arr.copy()) return steps, pseudocode, active_line except Exception as e: self.error_message = f"Insertion sort error: {str(e)}" return [], [], [] def dfs(self, graph_dict, start): try: graph = self.parse_graph(graph_dict) if not graph: return [], [], [] visited = set() stack = [start] steps = [] pseudocode = [ "procedure DFS(G, start):", " visited = set()", " stack = [start]", " while stack not empty:", " vertex = stack.pop()", " if vertex not in visited:", " visited.add(vertex)", " for neighbor in G[vertex]:", " if neighbor not in visited:", " stack.push(neighbor)" ] active_line = [0] steps.append({"visited": set(), "current": None, "stack": stack.copy(), "graph": graph}) active_line.append(1) while stack: active_line.append(3) vertex = stack.pop() active_line.append(4) if vertex not in visited: active_line.append(5) visited.add(vertex) active_line.append(6) for neighbor in graph.get(vertex, []): active_line.append(7) if neighbor not in visited: stack.append(neighbor) steps.append({"visited": visited.copy(), "current": vertex, "stack": stack.copy(), "graph": graph}) active_line.append(3) return steps, pseudocode, active_line except Exception as e: self.error_message = f"DFS error: {str(e)}" return [], [], [] def bfs(self, graph_dict, start): try: graph = self.parse_graph(graph_dict) if not graph: return [], [], [] visited = set() queue = deque([start]) steps = [] pseudocode = [ "procedure BFS(G, start):", " visited = set()", " queue = deque([start])", " while queue not empty:", " vertex = queue.popleft()", " if vertex not in visited:", " visited.add(vertex)", " for neighbor in G[vertex]:", " if neighbor not in visited:", " queue.append(neighbor)" ] active_line = [0] steps.append({"visited": set(), "current": None, "queue": list(queue), "graph": graph}) active_line.append(1) while queue: active_line.append(3) vertex = queue.popleft() active_line.append(4) if vertex not in visited: active_line.append(5) visited.add(vertex) active_line.append(6) for neighbor in graph.get(vertex, []): active_line.append(7) if neighbor not in visited: queue.append(neighbor) steps.append({"visited": visited.copy(), "current": vertex, "queue": list(queue), "graph": graph}) active_line.append(3) return steps, pseudocode, active_line except Exception as e: self.error_message = f"BFS error: {str(e)}" return [], [], [] def visualize_sorting(self, algorithm, data): try: # Convert data to list of integers if isinstance(data, str): data = [int(np.int64(x.strip())) for x in data.strip('[]').split(',') if x.strip()] elif not isinstance(data, list): data = list(data) if algorithm == "Bubble Sort": self.steps, self.pseudocode, self.active_lines = self.bubble_sort(data) elif algorithm == "Insertion Sort": self.steps, self.pseudocode, self.active_lines = self.insertion_sort(data) else: self.error_message = f"Algorithm '{algorithm}' not implemented yet" return None, self.create_pseudocode(0), self.error_message self.current_step = 0 return self.create_plot(), self.create_pseudocode(0), "" except Exception as e: self.error_message = f"Visualization error: {str(e)}" return None, "", self.error_message def visualize_graph(self, algorithm, graph_input, start): try: self.steps, self.pseudocode, self.active_lines = ( self.dfs(graph_input, start) if algorithm == "DFS" else self.bfs(graph_input, start) ) self.current_step = 0 return self.create_graph_plot(), self.create_pseudocode(0), "" except Exception as e: self.error_message = f"Graph visualization error: {str(e)}" return None, "", self.error_message def create_plot(self): try: fig, ax = plt.subplots(figsize=(10, 6)) if self.steps and self.current_step < len(self.steps): current_data = self.steps[self.current_step] colors = ['#1f77b4' for _ in current_data] # Highlight recently swapped elements if self.current_step > 0 and self.current_step < len(self.steps): prev = self.steps[self.current_step-1] for i in range(len(current_data)): if i < len(prev) and current_data[i] != prev[i]: colors[i] = '#ff7f0e' ax.bar(range(len(current_data)), current_data, color=colors) ax.set_title(f'Step {self.current_step}/{len(self.steps)-1}') ax.set_xlabel('Index') ax.set_ylabel('Value') ax.grid(axis='y', linestyle='--', alpha=0.7) else: ax.text(0.5, 0.5, "No data to visualize", ha='center', va='center', fontsize=16) buf = BytesIO() plt.savefig(buf, format='png', dpi=100, bbox_inches='tight') plt.close(fig) return buf.getvalue() except Exception as e: self.error_message = f"Plot creation error: {str(e)}" return None def create_graph_plot(self): try: if not self.steps or self.current_step >= len(self.steps): fig, ax = plt.subplots(figsize=(10, 8)) ax.text(0.5, 0.5, "No graph data to visualize", ha='center', va='center', fontsize=16) buf = BytesIO() plt.savefig(buf, format='png') plt.close(fig) return buf.getvalue() state = self.steps[self.current_step] graph_dict = state["graph"] G = nx.Graph() for node, neighbors in graph_dict.items(): for neighbor in neighbors: if neighbor in graph_dict: # Ensure neighbor exists G.add_edge(node, neighbor) # Add isolated nodes for node in graph_dict: if node not in G: G.add_node(node) pos = nx.spring_layout(G, seed=42) fig, ax = plt.subplots(figsize=(10, 8)) node_colors = [] for node in G.nodes(): if node == state.get('current'): node_colors.append('#d62728') # Current node elif node in state.get('visited', set()): node_colors.append('#2ca02c') # Visited nodes elif node in state.get('stack', []) or node in state.get('queue', []): node_colors.append('#ff7f0e') # Nodes in stack/queue else: node_colors.append('#1f77b4') # Unexplored nodes nx.draw_networkx_nodes(G, pos, node_size=800, node_color=node_colors, alpha=0.9, ax=ax) nx.draw_networkx_edges(G, pos, width=1.5, alpha=0.5, ax=ax) nx.draw_networkx_labels(G, pos, font_size=12, font_weight='bold', font_color='white', ax=ax) algorithm = "DFS" if 'stack' in state else "BFS" title = f"{algorithm} Traversal - Step {self.current_step}/{len(self.steps)-1}\n" title += f"Current: {state.get('current', 'None')} | " if 'stack' in state: title += f"Stack: {state['stack']}" elif 'queue' in state: title += f"Queue: {state['queue']}" ax.set_title(title, fontsize=14) ax.set_axis_off() buf = BytesIO() plt.savefig(buf, format='png', dpi=100, bbox_inches='tight') plt.close(fig) return buf.getvalue() except Exception as e: self.error_message = f"Graph plot error: {str(e)}" return None def create_pseudocode(self, step): try: if not self.pseudocode or not self.active_lines: return "" # Find the active line for this step if step < len(self.active_lines): active_line = self.active_lines[step] else: active_line = self.active_lines[-1] if self.active_lines else 0 html = "
Interactive Data Structures & Algorithms Visualization Platform