Spaces:
Runtime error
Runtime error
| import os | |
| import numpy as np | |
| import torch | |
| import matplotlib.pyplot as plt | |
| import networkx as nx | |
| import gradio as gr | |
| from matplotlib.colors import LinearSegmentedColormap | |
| import matplotlib.patches as mpatches | |
| # Check if GPU is available | |
| device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") | |
| print(f"Using device: {device}") | |
| class EnhancedMindMapGenerator: | |
| def __init__(self): | |
| self.graph = nx.DiGraph() # Using DiGraph for directed edges | |
| self.node_positions = {} | |
| self.node_colors = {} | |
| self.edge_colors = {} | |
| self.node_sizes = {} | |
| self.node_depth = {} | |
| self.levels = {} | |
| def reset(self): | |
| self.graph = nx.DiGraph() | |
| self.node_positions = {} | |
| self.node_colors = {} | |
| self.edge_colors = {} | |
| self.node_sizes = {} | |
| self.node_depth = {} | |
| self.levels = {} | |
| return "Mind map reset successfully" | |
| def parse_input(self, text): | |
| """Parse the input text into nodes and relationships""" | |
| lines = text.strip().split('\n') | |
| root_node = None | |
| parent_map = {} # Track parent nodes based on indent level | |
| current_indent_level = -1 | |
| current_parent = None | |
| # First pass: Build hierarchy based on indentation | |
| for line in lines: | |
| original_line = line | |
| line = line.strip() | |
| if not line or '->' in line: | |
| continue # Skip empty lines and relationship lines for now | |
| # Calculate indent level | |
| indent_level = len(original_line) - len(original_line.lstrip()) | |
| if root_node is None: | |
| # This is the root node | |
| root_node = line | |
| self.add_node(root_node, is_root=True, depth=0) | |
| parent_map[0] = root_node | |
| current_indent_level = indent_level | |
| current_parent = root_node | |
| self.levels[0] = [root_node] | |
| else: | |
| # Handle indentation to determine parent-child relationships | |
| if indent_level > current_indent_level: | |
| # This is a child of the previous node | |
| parent_map[indent_level] = current_parent | |
| parent = None | |
| if indent_level in parent_map: | |
| parent = parent_map[indent_level] | |
| # If this is a new indent level, set the parent to the previous node | |
| if indent_level > current_indent_level: | |
| parent = current_parent | |
| else: | |
| # Find the closest parent based on indent | |
| closest_indent = max([i for i in parent_map.keys() if i < indent_level], default=0) | |
| parent = parent_map[closest_indent] | |
| # Calculate depth based on parent's depth | |
| parent_depth = self.node_depth.get(parent, 0) | |
| current_depth = parent_depth + 1 | |
| # Add node and edge | |
| self.add_node(line, depth=current_depth) | |
| self.add_edge(parent, line, "hierarchy") | |
| # Add to level structure | |
| if current_depth not in self.levels: | |
| self.levels[current_depth] = [] | |
| self.levels[current_depth].append(line) | |
| # Update tracking variables | |
| current_indent_level = indent_level | |
| current_parent = line | |
| parent_map[indent_level] = line | |
| # Second pass: Process explicit relationships (->) | |
| for line in lines: | |
| line = line.strip() | |
| if '->' in line: | |
| parts = line.split('->') | |
| if len(parts) == 2: | |
| source = parts[0].strip() | |
| target = parts[1].strip() | |
| self.add_edge(source, target, "relationship") | |
| return f"Parsed mind map with root: {root_node}" | |
| def add_node(self, node_name, is_root=False, depth=0): | |
| """Add a node to the graph""" | |
| if node_name not in self.graph.nodes: | |
| self.graph.add_node(node_name) | |
| self.node_depth[node_name] = depth | |
| # Set color based on depth | |
| if is_root: | |
| self.node_colors[node_name] = '#FF5733' # Root is red | |
| self.node_sizes[node_name] = 2500 | |
| else: | |
| # Use a color scheme based on depth | |
| color_map = { | |
| 1: '#3498DB', # Blue | |
| 2: '#F39C12', # Orange | |
| 3: '#2ECC71', # Green | |
| 4: '#9B59B6', # Purple | |
| 5: '#E74C3C', # Red | |
| } | |
| self.node_colors[node_name] = color_map.get(depth % len(color_map), '#95A5A6') # Gray as default | |
| self.node_sizes[node_name] = 2000 - (depth * 200) # Size decreases with depth | |
| def add_edge(self, source, target, edge_type="hierarchy"): | |
| """Add an edge between two nodes""" | |
| if source not in self.graph.nodes: | |
| self.add_node(source) | |
| if target not in self.graph.nodes: | |
| self.add_node(target) | |
| if not self.graph.has_edge(source, target): | |
| self.graph.add_edge(source, target) | |
| # Color edges based on type | |
| if edge_type == "relationship": | |
| self.edge_colors[(source, target)] = 'green' | |
| else: | |
| self.edge_colors[(source, target)] = 'gray' | |
| def calculate_hierarchical_layout(self): | |
| """Calculate a hierarchical layout based on node depth""" | |
| # Use hierarchical layout with depth levels | |
| pos = {} | |
| max_nodes_per_level = max([len(nodes) for nodes in self.levels.values()]) | |
| for level, nodes in self.levels.items(): | |
| y = -level * 2 # Vertical position based on level | |
| # Center the nodes at each level | |
| width = max(max_nodes_per_level, len(nodes)) | |
| for i, node in enumerate(nodes): | |
| x = (i - (len(nodes) - 1) / 2) * 3 # Horizontal spacing | |
| pos[node] = np.array([x, y]) | |
| return pos | |
| def optimize_layout(self): | |
| """Use GPU-accelerated optimization for node layout (if available)""" | |
| # First set initial positions using hierarchical layout | |
| initial_pos = self.calculate_hierarchical_layout() | |
| self.node_positions = initial_pos | |
| if device.type == "cuda": | |
| print("Optimizing layout using GPU...") | |
| # Implement GPU optimization if needed | |
| nodes = list(self.graph.nodes) | |
| positions = torch.tensor([self.node_positions[node] for node in nodes], device=device) | |
| # Simple force-directed algorithm using PyTorch (maintains hierarchical structure) | |
| for _ in range(50): | |
| # Calculate attractive forces (edges) | |
| attractive_force = torch.zeros_like(positions) | |
| for u, v in self.graph.edges: | |
| u_idx = nodes.index(u) | |
| v_idx = nodes.index(v) | |
| direction = positions[v_idx] - positions[u_idx] | |
| distance = torch.norm(direction) + 1e-5 | |
| force = direction * torch.log(distance / 2) * 0.1 | |
| attractive_force[u_idx] += force | |
| attractive_force[v_idx] -= force | |
| # Calculate repulsive forces (nodes at same level) | |
| repulsive_force = torch.zeros_like(positions) | |
| for level_nodes in self.levels.values(): | |
| level_indices = [nodes.index(node) for node in level_nodes if node in nodes] | |
| for i_idx, i in enumerate(level_indices): | |
| for j in level_indices[i_idx+1:]: | |
| direction = positions[j] - positions[i] | |
| distance = torch.norm(direction) + 1e-5 | |
| if distance < 3.0: # Only apply repulsion when nodes are close | |
| force = direction / (distance ** 2) * 0.5 | |
| repulsive_force[i] -= force | |
| repulsive_force[j] += force | |
| # Update positions but maintain y-coordinate (level) | |
| new_pos = positions + (attractive_force + repulsive_force) * 0.1 | |
| # Preserve y-coordinates to maintain hierarchical layout | |
| for i, node in enumerate(nodes): | |
| level = self.node_depth[node] | |
| new_pos[i, 1] = positions[i, 1] # Keep original y-coordinate | |
| positions = new_pos | |
| # Copy back to CPU and update positions | |
| positions_cpu = positions.cpu().numpy() | |
| for i, node in enumerate(nodes): | |
| self.node_positions[node] = positions_cpu[i] | |
| return "Layout optimized using GPU acceleration while preserving hierarchy" | |
| else: | |
| # CPU-based optimization | |
| # Adjust positions to prevent overlaps while maintaining hierarchy | |
| pos = nx.spring_layout( | |
| self.graph, | |
| pos=self.node_positions, | |
| fixed=None, # Don't fix positions | |
| k=1.5, # Increase node separation | |
| iterations=50, | |
| weight=None | |
| ) | |
| # Preserve y-coordinates to maintain hierarchical layout | |
| for node in self.graph.nodes: | |
| pos[node][1] = self.node_positions[node][1] # Keep original y-coordinate | |
| self.node_positions = pos | |
| return "Layout optimized using CPU while preserving hierarchy" | |
| def visualize(self): | |
| """Generate a visualization of the mind map""" | |
| if not self.graph.nodes: | |
| return None | |
| plt.figure(figsize=(16, 12), dpi=100) | |
| # Use calculated positions from hierarchical layout or optimization | |
| pos = self.node_positions | |
| # Create a legend for depth levels | |
| depth_colors = {} | |
| for node, depth in self.node_depth.items(): | |
| if depth not in depth_colors: | |
| depth_colors[depth] = self.node_colors[node] | |
| # Draw edges with curved arrows for relationships | |
| for edge in self.graph.edges: | |
| edge_color = self.edge_colors.get(edge, 'gray') | |
| # Use curved edges for explicit relationships, straight for hierarchy | |
| if edge_color == 'green': | |
| nx.draw_networkx_edges( | |
| self.graph, | |
| pos, | |
| edgelist=[edge], | |
| width=2.5, | |
| edge_color=edge_color, | |
| alpha=0.8, | |
| arrows=True, | |
| arrowsize=15, | |
| connectionstyle="arc3,rad=0.3" | |
| ) | |
| else: | |
| nx.draw_networkx_edges( | |
| self.graph, | |
| pos, | |
| edgelist=[edge], | |
| width=1.5, | |
| edge_color=edge_color, | |
| alpha=0.7, | |
| arrows=True, | |
| arrowsize=12 | |
| ) | |
| # Draw nodes with depth-based colors | |
| for node in self.graph.nodes: | |
| nx.draw_networkx_nodes( | |
| self.graph, | |
| pos, | |
| nodelist=[node], | |
| node_color=self.node_colors.get(node, 'blue'), | |
| node_size=self.node_sizes.get(node, 1000), | |
| alpha=0.9, | |
| edgecolors='black', | |
| linewidths=1 | |
| ) | |
| # Draw labels with white background for better readability | |
| label_pos = {node: (pos[node][0], pos[node][1]) for node in self.graph.nodes} | |
| nx.draw_networkx_labels( | |
| self.graph, | |
| label_pos, | |
| font_size=10, | |
| font_family='sans-serif', | |
| font_weight='bold', | |
| bbox=dict(facecolor='white', alpha=0.7, edgecolor='none', boxstyle='round,pad=0.3') | |
| ) | |
| # Add a legend | |
| legend_elements = [ | |
| mpatches.Patch(color='#FF5733', label='Root'), | |
| mpatches.Patch(color='#3498DB', label='Level 1'), | |
| mpatches.Patch(color='#F39C12', label='Level 2'), | |
| mpatches.Patch(color='#2ECC71', label='Level 3'), | |
| mpatches.Patch(color='#9B59B6', label='Level 4+'), | |
| mpatches.Patch(color='green', label='Explicit Relationship'), | |
| mpatches.Patch(color='gray', label='Hierarchical Relationship') | |
| ] | |
| plt.legend(handles=legend_elements, loc='upper right') | |
| plt.title("Mind Map Visualization", fontsize=16, fontweight='bold') | |
| plt.axis('off') | |
| plt.tight_layout() | |
| # Save to a temporary file | |
| temp_path = "mindmap_output.png" | |
| plt.savefig(temp_path, format="png", dpi=300, bbox_inches='tight', facecolor='white') | |
| plt.close() | |
| return temp_path | |
| # Create the Gradio interface | |
| def create_mind_map(input_text, optimization): | |
| """Create a mind map from input text""" | |
| generator = EnhancedMindMapGenerator() | |
| message = generator.parse_input(input_text) | |
| print(message) | |
| if optimization: | |
| message = generator.optimize_layout() | |
| print(message) | |
| image_path = generator.visualize() | |
| return image_path | |
| # For Colab, use this function to create and launch the demo | |
| def create_and_launch(): | |
| """Create and launch the Gradio demo""" | |
| with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# Enhanced Mind Map Generator") | |
| gr.Markdown("Enter your mind map structure below. Use indentation to represent hierarchy or use -> for direct relationships.") | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| input_text = gr.Textbox( | |
| placeholder="Project Name\n Task 1\n Subtask 1.1\n Subtask 1.2\n Task 2\nTask 1 -> Task 2", | |
| label="Mind Map Structure", | |
| lines=15 | |
| ) | |
| with gr.Row(): | |
| optimization = gr.Checkbox(label="Use Layout Optimization", value=True) | |
| generate_btn = gr.Button("Generate Mind Map", variant="primary") | |
| gr.Markdown("### Format Guide:") | |
| gr.Markdown(""" | |
| - Use indentation (spaces) to create parent-child relationships | |
| - Each level of indentation creates a new depth level | |
| - Use '-> ' to create explicit connections (e.g., 'NodeA -> NodeB') | |
| - The first non-indented line becomes the root node | |
| """) | |
| with gr.Column(scale=3): | |
| output_image = gr.Image(label="Generated Mind Map", type="filepath") | |
| generate_btn.click(fn=create_mind_map, inputs=[input_text, optimization], outputs=output_image) | |
| # Add examples | |
| example_input1 = """Software Project | |
| Planning | |
| Requirements Gathering | |
| Project Timeline | |
| Resource Allocation | |
| Development | |
| Frontend | |
| UI Design | |
| React Components | |
| Backend | |
| API Development | |
| Database Setup | |
| Testing | |
| Unit Tests | |
| Integration Tests | |
| Deployment | |
| CI/CD Pipeline | |
| Production Release | |
| Planning -> Development | |
| Development -> Testing | |
| Testing -> Deployment""" | |
| example_input2 = """Business Strategy | |
| Market Analysis | |
| Customer Demographics | |
| Competitor Research | |
| Market Trends | |
| Internal Assessment | |
| SWOT Analysis | |
| Resource Inventory | |
| Strategic Goals | |
| Short-term Objectives | |
| Long-term Vision | |
| Implementation | |
| Action Plans | |
| Market Analysis -> Strategic Goals | |
| Internal Assessment -> Strategic Goals | |
| Strategic Goals -> Implementation""" | |
| gr.Examples( | |
| examples=[[example_input1, True], [example_input2, True]], | |
| inputs=[input_text, optimization], | |
| outputs=output_image, | |
| fn=create_mind_map, | |
| cache_examples=True, | |
| ) | |
| # Launch with sharing enabled for Colab | |
| demo.launch(share=True, debug=True) | |
| return demo | |
| # Main execution | |
| def run_in_colab(): | |
| # Install necessary packages | |
| print("Installing required packages...") | |
| try: | |
| import gradio | |
| import networkx | |
| except ImportError: | |
| !pip install gradio networkx matplotlib | |
| print("Packages installed!") | |
| # Create and launch the demo | |
| print("Launching the Enhanced Mind Map Generator...") | |
| create_and_launch() | |
| # For Google Colab, use this | |
| try: | |
| import google.colab | |
| print("Running in Google Colab environment") | |
| run_in_colab() | |
| except: | |
| print("Running in local environment") | |
| # If not in Colab, just create and launch | |
| create_and_launch() |