Anupam251272 commited on
Commit
c44f410
·
verified ·
1 Parent(s): f1b0428

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +448 -0
app.py ADDED
@@ -0,0 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import numpy as np
3
+ import torch
4
+ import matplotlib.pyplot as plt
5
+ import networkx as nx
6
+ import gradio as gr
7
+ from matplotlib.colors import LinearSegmentedColormap
8
+ import matplotlib.patches as mpatches
9
+
10
+ # Check if GPU is available
11
+ device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
12
+ print(f"Using device: {device}")
13
+
14
+ class EnhancedMindMapGenerator:
15
+ def __init__(self):
16
+ self.graph = nx.DiGraph() # Using DiGraph for directed edges
17
+ self.node_positions = {}
18
+ self.node_colors = {}
19
+ self.edge_colors = {}
20
+ self.node_sizes = {}
21
+ self.node_depth = {}
22
+ self.levels = {}
23
+
24
+ def reset(self):
25
+ self.graph = nx.DiGraph()
26
+ self.node_positions = {}
27
+ self.node_colors = {}
28
+ self.edge_colors = {}
29
+ self.node_sizes = {}
30
+ self.node_depth = {}
31
+ self.levels = {}
32
+ return "Mind map reset successfully"
33
+
34
+ def parse_input(self, text):
35
+ """Parse the input text into nodes and relationships"""
36
+ lines = text.strip().split('\n')
37
+ root_node = None
38
+ parent_map = {} # Track parent nodes based on indent level
39
+ current_indent_level = -1
40
+ current_parent = None
41
+
42
+ # First pass: Build hierarchy based on indentation
43
+ for line in lines:
44
+ original_line = line
45
+ line = line.strip()
46
+ if not line or '->' in line:
47
+ continue # Skip empty lines and relationship lines for now
48
+
49
+ # Calculate indent level
50
+ indent_level = len(original_line) - len(original_line.lstrip())
51
+
52
+ if root_node is None:
53
+ # This is the root node
54
+ root_node = line
55
+ self.add_node(root_node, is_root=True, depth=0)
56
+ parent_map[0] = root_node
57
+ current_indent_level = indent_level
58
+ current_parent = root_node
59
+ self.levels[0] = [root_node]
60
+ else:
61
+ # Handle indentation to determine parent-child relationships
62
+ if indent_level > current_indent_level:
63
+ # This is a child of the previous node
64
+ parent_map[indent_level] = current_parent
65
+
66
+ parent = None
67
+ if indent_level in parent_map:
68
+ parent = parent_map[indent_level]
69
+ # If this is a new indent level, set the parent to the previous node
70
+ if indent_level > current_indent_level:
71
+ parent = current_parent
72
+ else:
73
+ # Find the closest parent based on indent
74
+ closest_indent = max([i for i in parent_map.keys() if i < indent_level], default=0)
75
+ parent = parent_map[closest_indent]
76
+
77
+ # Calculate depth based on parent's depth
78
+ parent_depth = self.node_depth.get(parent, 0)
79
+ current_depth = parent_depth + 1
80
+
81
+ # Add node and edge
82
+ self.add_node(line, depth=current_depth)
83
+ self.add_edge(parent, line, "hierarchy")
84
+
85
+ # Add to level structure
86
+ if current_depth not in self.levels:
87
+ self.levels[current_depth] = []
88
+ self.levels[current_depth].append(line)
89
+
90
+ # Update tracking variables
91
+ current_indent_level = indent_level
92
+ current_parent = line
93
+ parent_map[indent_level] = line
94
+
95
+ # Second pass: Process explicit relationships (->)
96
+ for line in lines:
97
+ line = line.strip()
98
+ if '->' in line:
99
+ parts = line.split('->')
100
+ if len(parts) == 2:
101
+ source = parts[0].strip()
102
+ target = parts[1].strip()
103
+ self.add_edge(source, target, "relationship")
104
+
105
+ return f"Parsed mind map with root: {root_node}"
106
+
107
+ def add_node(self, node_name, is_root=False, depth=0):
108
+ """Add a node to the graph"""
109
+ if node_name not in self.graph.nodes:
110
+ self.graph.add_node(node_name)
111
+ self.node_depth[node_name] = depth
112
+
113
+ # Set color based on depth
114
+ if is_root:
115
+ self.node_colors[node_name] = '#FF5733' # Root is red
116
+ self.node_sizes[node_name] = 2500
117
+ else:
118
+ # Use a color scheme based on depth
119
+ color_map = {
120
+ 1: '#3498DB', # Blue
121
+ 2: '#F39C12', # Orange
122
+ 3: '#2ECC71', # Green
123
+ 4: '#9B59B6', # Purple
124
+ 5: '#E74C3C', # Red
125
+ }
126
+ self.node_colors[node_name] = color_map.get(depth % len(color_map), '#95A5A6') # Gray as default
127
+ self.node_sizes[node_name] = 2000 - (depth * 200) # Size decreases with depth
128
+
129
+ def add_edge(self, source, target, edge_type="hierarchy"):
130
+ """Add an edge between two nodes"""
131
+ if source not in self.graph.nodes:
132
+ self.add_node(source)
133
+ if target not in self.graph.nodes:
134
+ self.add_node(target)
135
+
136
+ if not self.graph.has_edge(source, target):
137
+ self.graph.add_edge(source, target)
138
+
139
+ # Color edges based on type
140
+ if edge_type == "relationship":
141
+ self.edge_colors[(source, target)] = 'green'
142
+ else:
143
+ self.edge_colors[(source, target)] = 'gray'
144
+
145
+ def calculate_hierarchical_layout(self):
146
+ """Calculate a hierarchical layout based on node depth"""
147
+ # Use hierarchical layout with depth levels
148
+ pos = {}
149
+ max_nodes_per_level = max([len(nodes) for nodes in self.levels.values()])
150
+
151
+ for level, nodes in self.levels.items():
152
+ y = -level * 2 # Vertical position based on level
153
+
154
+ # Center the nodes at each level
155
+ width = max(max_nodes_per_level, len(nodes))
156
+ for i, node in enumerate(nodes):
157
+ x = (i - (len(nodes) - 1) / 2) * 3 # Horizontal spacing
158
+ pos[node] = np.array([x, y])
159
+
160
+ return pos
161
+
162
+ def optimize_layout(self):
163
+ """Use GPU-accelerated optimization for node layout (if available)"""
164
+ # First set initial positions using hierarchical layout
165
+ initial_pos = self.calculate_hierarchical_layout()
166
+ self.node_positions = initial_pos
167
+
168
+ if device.type == "cuda":
169
+ print("Optimizing layout using GPU...")
170
+ # Implement GPU optimization if needed
171
+ nodes = list(self.graph.nodes)
172
+ positions = torch.tensor([self.node_positions[node] for node in nodes], device=device)
173
+
174
+ # Simple force-directed algorithm using PyTorch (maintains hierarchical structure)
175
+ for _ in range(50):
176
+ # Calculate attractive forces (edges)
177
+ attractive_force = torch.zeros_like(positions)
178
+ for u, v in self.graph.edges:
179
+ u_idx = nodes.index(u)
180
+ v_idx = nodes.index(v)
181
+ direction = positions[v_idx] - positions[u_idx]
182
+ distance = torch.norm(direction) + 1e-5
183
+ force = direction * torch.log(distance / 2) * 0.1
184
+ attractive_force[u_idx] += force
185
+ attractive_force[v_idx] -= force
186
+
187
+ # Calculate repulsive forces (nodes at same level)
188
+ repulsive_force = torch.zeros_like(positions)
189
+ for level_nodes in self.levels.values():
190
+ level_indices = [nodes.index(node) for node in level_nodes if node in nodes]
191
+ for i_idx, i in enumerate(level_indices):
192
+ for j in level_indices[i_idx+1:]:
193
+ direction = positions[j] - positions[i]
194
+ distance = torch.norm(direction) + 1e-5
195
+ if distance < 3.0: # Only apply repulsion when nodes are close
196
+ force = direction / (distance ** 2) * 0.5
197
+ repulsive_force[i] -= force
198
+ repulsive_force[j] += force
199
+
200
+ # Update positions but maintain y-coordinate (level)
201
+ new_pos = positions + (attractive_force + repulsive_force) * 0.1
202
+
203
+ # Preserve y-coordinates to maintain hierarchical layout
204
+ for i, node in enumerate(nodes):
205
+ level = self.node_depth[node]
206
+ new_pos[i, 1] = positions[i, 1] # Keep original y-coordinate
207
+
208
+ positions = new_pos
209
+
210
+ # Copy back to CPU and update positions
211
+ positions_cpu = positions.cpu().numpy()
212
+ for i, node in enumerate(nodes):
213
+ self.node_positions[node] = positions_cpu[i]
214
+
215
+ return "Layout optimized using GPU acceleration while preserving hierarchy"
216
+ else:
217
+ # CPU-based optimization
218
+ # Adjust positions to prevent overlaps while maintaining hierarchy
219
+ pos = nx.spring_layout(
220
+ self.graph,
221
+ pos=self.node_positions,
222
+ fixed=None, # Don't fix positions
223
+ k=1.5, # Increase node separation
224
+ iterations=50,
225
+ weight=None
226
+ )
227
+
228
+ # Preserve y-coordinates to maintain hierarchical layout
229
+ for node in self.graph.nodes:
230
+ pos[node][1] = self.node_positions[node][1] # Keep original y-coordinate
231
+
232
+ self.node_positions = pos
233
+ return "Layout optimized using CPU while preserving hierarchy"
234
+
235
+ def visualize(self):
236
+ """Generate a visualization of the mind map"""
237
+ if not self.graph.nodes:
238
+ return None
239
+
240
+ plt.figure(figsize=(16, 12), dpi=100)
241
+
242
+ # Use calculated positions from hierarchical layout or optimization
243
+ pos = self.node_positions
244
+
245
+ # Create a legend for depth levels
246
+ depth_colors = {}
247
+ for node, depth in self.node_depth.items():
248
+ if depth not in depth_colors:
249
+ depth_colors[depth] = self.node_colors[node]
250
+
251
+ # Draw edges with curved arrows for relationships
252
+ for edge in self.graph.edges:
253
+ edge_color = self.edge_colors.get(edge, 'gray')
254
+
255
+ # Use curved edges for explicit relationships, straight for hierarchy
256
+ if edge_color == 'green':
257
+ nx.draw_networkx_edges(
258
+ self.graph,
259
+ pos,
260
+ edgelist=[edge],
261
+ width=2.5,
262
+ edge_color=edge_color,
263
+ alpha=0.8,
264
+ arrows=True,
265
+ arrowsize=15,
266
+ connectionstyle="arc3,rad=0.3"
267
+ )
268
+ else:
269
+ nx.draw_networkx_edges(
270
+ self.graph,
271
+ pos,
272
+ edgelist=[edge],
273
+ width=1.5,
274
+ edge_color=edge_color,
275
+ alpha=0.7,
276
+ arrows=True,
277
+ arrowsize=12
278
+ )
279
+
280
+ # Draw nodes with depth-based colors
281
+ for node in self.graph.nodes:
282
+ nx.draw_networkx_nodes(
283
+ self.graph,
284
+ pos,
285
+ nodelist=[node],
286
+ node_color=self.node_colors.get(node, 'blue'),
287
+ node_size=self.node_sizes.get(node, 1000),
288
+ alpha=0.9,
289
+ edgecolors='black',
290
+ linewidths=1
291
+ )
292
+
293
+ # Draw labels with white background for better readability
294
+ label_pos = {node: (pos[node][0], pos[node][1]) for node in self.graph.nodes}
295
+ nx.draw_networkx_labels(
296
+ self.graph,
297
+ label_pos,
298
+ font_size=10,
299
+ font_family='sans-serif',
300
+ font_weight='bold',
301
+ bbox=dict(facecolor='white', alpha=0.7, edgecolor='none', boxstyle='round,pad=0.3')
302
+ )
303
+
304
+ # Add a legend
305
+ legend_elements = [
306
+ mpatches.Patch(color='#FF5733', label='Root'),
307
+ mpatches.Patch(color='#3498DB', label='Level 1'),
308
+ mpatches.Patch(color='#F39C12', label='Level 2'),
309
+ mpatches.Patch(color='#2ECC71', label='Level 3'),
310
+ mpatches.Patch(color='#9B59B6', label='Level 4+'),
311
+ mpatches.Patch(color='green', label='Explicit Relationship'),
312
+ mpatches.Patch(color='gray', label='Hierarchical Relationship')
313
+ ]
314
+ plt.legend(handles=legend_elements, loc='upper right')
315
+
316
+ plt.title("Mind Map Visualization", fontsize=16, fontweight='bold')
317
+ plt.axis('off')
318
+ plt.tight_layout()
319
+
320
+ # Save to a temporary file
321
+ temp_path = "mindmap_output.png"
322
+ plt.savefig(temp_path, format="png", dpi=300, bbox_inches='tight', facecolor='white')
323
+ plt.close()
324
+
325
+ return temp_path
326
+
327
+ # Create the Gradio interface
328
+ def create_mind_map(input_text, optimization):
329
+ """Create a mind map from input text"""
330
+ generator = EnhancedMindMapGenerator()
331
+ message = generator.parse_input(input_text)
332
+ print(message)
333
+
334
+ if optimization:
335
+ message = generator.optimize_layout()
336
+ print(message)
337
+
338
+ image_path = generator.visualize()
339
+ return image_path
340
+
341
+ # For Colab, use this function to create and launch the demo
342
+ def create_and_launch():
343
+ """Create and launch the Gradio demo"""
344
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
345
+ gr.Markdown("# Enhanced Mind Map Generator")
346
+ gr.Markdown("Enter your mind map structure below. Use indentation to represent hierarchy or use -> for direct relationships.")
347
+
348
+ with gr.Row():
349
+ with gr.Column(scale=2):
350
+ input_text = gr.Textbox(
351
+ placeholder="Project Name\n Task 1\n Subtask 1.1\n Subtask 1.2\n Task 2\nTask 1 -> Task 2",
352
+ label="Mind Map Structure",
353
+ lines=15
354
+ )
355
+
356
+ with gr.Row():
357
+ optimization = gr.Checkbox(label="Use Layout Optimization", value=True)
358
+ generate_btn = gr.Button("Generate Mind Map", variant="primary")
359
+
360
+ gr.Markdown("### Format Guide:")
361
+ gr.Markdown("""
362
+ - Use indentation (spaces) to create parent-child relationships
363
+ - Each level of indentation creates a new depth level
364
+ - Use '-> ' to create explicit connections (e.g., 'NodeA -> NodeB')
365
+ - The first non-indented line becomes the root node
366
+ """)
367
+
368
+ with gr.Column(scale=3):
369
+ output_image = gr.Image(label="Generated Mind Map", type="filepath")
370
+
371
+ generate_btn.click(fn=create_mind_map, inputs=[input_text, optimization], outputs=output_image)
372
+
373
+ # Add examples
374
+ example_input1 = """Software Project
375
+ Planning
376
+ Requirements Gathering
377
+ Project Timeline
378
+ Resource Allocation
379
+ Development
380
+ Frontend
381
+ UI Design
382
+ React Components
383
+ Backend
384
+ API Development
385
+ Database Setup
386
+ Testing
387
+ Unit Tests
388
+ Integration Tests
389
+ Deployment
390
+ CI/CD Pipeline
391
+ Production Release
392
+ Planning -> Development
393
+ Development -> Testing
394
+ Testing -> Deployment"""
395
+
396
+ example_input2 = """Business Strategy
397
+ Market Analysis
398
+ Customer Demographics
399
+ Competitor Research
400
+ Market Trends
401
+ Internal Assessment
402
+ SWOT Analysis
403
+ Resource Inventory
404
+ Strategic Goals
405
+ Short-term Objectives
406
+ Long-term Vision
407
+ Implementation
408
+ Action Plans
409
+ Market Analysis -> Strategic Goals
410
+ Internal Assessment -> Strategic Goals
411
+ Strategic Goals -> Implementation"""
412
+
413
+ gr.Examples(
414
+ examples=[[example_input1, True], [example_input2, True]],
415
+ inputs=[input_text, optimization],
416
+ outputs=output_image,
417
+ fn=create_mind_map,
418
+ cache_examples=True,
419
+ )
420
+
421
+ # Launch with sharing enabled for Colab
422
+ demo.launch(share=True, debug=True)
423
+ return demo
424
+
425
+ # Main execution
426
+ def run_in_colab():
427
+ # Install necessary packages
428
+ print("Installing required packages...")
429
+ try:
430
+ import gradio
431
+ import networkx
432
+ except ImportError:
433
+ !pip install gradio networkx matplotlib
434
+ print("Packages installed!")
435
+
436
+ # Create and launch the demo
437
+ print("Launching the Enhanced Mind Map Generator...")
438
+ create_and_launch()
439
+
440
+ # For Google Colab, use this
441
+ try:
442
+ import google.colab
443
+ print("Running in Google Colab environment")
444
+ run_in_colab()
445
+ except:
446
+ print("Running in local environment")
447
+ # If not in Colab, just create and launch
448
+ create_and_launch()