summrs commited on
Commit
06dd295
·
verified ·
1 Parent(s): 0eb63e1

topological_index_visualizer.py

Browse files

import subprocess
import sys

# Manually install required libraries (each installed separately)
subprocess.check_call([sys.executable, "-m", "pip", "install", "gradio"])
subprocess.check_call([sys.executable, "-m", "pip", "install", "networkx"])
subprocess.check_call([sys.executable, "-m", "pip", "install", "matplotlib"])
subprocess.check_call([sys.executable, "-m", "pip", "install", "numpy"])
subprocess.check_call([sys.executable, "-m", "pip", "install", "scipy"])

import os
import math
import itertools
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
import gradio as gr
from scipy.spatial import distance

# -------------------------------
# Step-by-Step Index Calculation Functions
# -------------------------------

# Formulas for the indices (written in a typical mathematical style)
formulas = {
"Wiener Index (W)": "W = Σ d(u,v) for all pairs (u,v)",
"First Zagreb Index (M₁)": "M₁ = Σ [d(v)]² for all vertices v",
"Second Zagreb Index (M₂)": "M₂ = Σ [d(u) * d(v)] for all adjacent vertices (u,v)",
"Randić Index (R)": "R = Σ [1 / √(d(u) * d(v))] for all edges (u,v)",
"Balaban Index (J)": "J = (m / (q + 1)) Σ [1 / √(d(u) * d(v))]",
"Harary Index (H)": "H = Σ [1 / d(u,v)] for all pairs (u,v)",
"Atom-Bond Connectivity Index (ABC)": "ABC = Σ [(√d(u) + √d(v))/(d(u) + d(v))] for all edges (u,v)",
"Geometric Arithmetic Index (GA)": "GA = Σ [√(d(u) * d(v))] for all edges (u,v)",
"Harmonic Index (Harm)": "Harm = Σ [1 / d(v)] for all vertices v"
}

def solve_index(index, user_input):
try:
if index == "Wiener Index (W)":
distances = list(map(int, user_input.split(',')))
result = sum(distances)
steps = [
"1. **Extract the shortest path distances:**\n - Distances = " + f"{distances}",
"2. **Sum the distances:**\n - W = " + " + ".join(map(str, distances)) + f" = {result}"
]
output = "### **Wiener Index (W)**\n\n"
output += "**Formula:**\n" + formulas["Wiener Index (W)"] + "\n"
output += "Where d(u,v) is the shortest path distance between vertices u and v.\n\n"
output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
output += f"**Answer:** The Wiener Index W = {result}."
return output

elif index == "First Zagreb Index (M₁)":
degrees = list(map(int, user_input.split(',')))
squared_degrees = [d**2 for d in degrees]
result = sum(squared_degrees)
steps = [
"1. **Retrieve the vertex degrees:**\n - Degrees = " + f"{degrees}",
"2. **Square each degree:**\n - Squared degrees = " + f"{squared_degrees}",
"3. **Sum the squared degrees:**\n - M₁ = " + " + ".join(map(str, squared_degrees)) + f" = {result}"
]
output = "### **First Zagreb Index (M₁)**\n\n"
output += "**Formula:**\n" + formulas["First Zagreb Index (M₁)"] + "\n\n"
output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
output += f"**Answer:** The First Zagreb Index M₁ = {result}."
return output

elif index == "Second Zagreb Index (M₂)":
adj_degrees = [tuple(map(int, pair.split('-'))) for pair in user_input.split(',')]
products = [d1 * d2 for d1, d2 in adj_degrees]
result = sum(products)
steps = [
"1. **Extract the degree pairs for adjacent vertices:**\n - Pairs = " + f"{adj_degrees}",
"2. **Multiply each pair:**\n - Products = " + f"{products}",
"3. **Sum the products:**\n - M₂ = " + " + ".join(map(str, products)) + f" = {result}"
]
output = "### **Second Zagreb Index (M₂)**\n\n"
output += "**Formula:**\n" + formulas["Second Zagreb Index (M₂)"] + "\n"
output += "Where d(u) and d(v) are the degrees of adjacent vertices u and v.\n\n"
output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
output += f"**Answer:** The Second Zagreb Index M₂ = {result}."
return output

elif index == "Randić Index (R)":
edge_degrees = [tuple(map(int, pair.split('-'))) for pair in user_input.split(',')]
values = [1 / (d1 * d2)**0.5 for d1, d2 in edge_degrees]
result = sum(values)
steps = [
"1. **Extract the degree pairs for each edge:**\n - Edge pairs = " + f"{edge_degrees}"
]
for (d1, d2), v in zip(edge_degrees, values):
steps.append(f"2. **For edge {d1}-{d2}:**\n - 1/√({d1}×{d2}) = {v:.4f}")
steps.append("3. **Sum the values:**\n - R = " + " + ".join(f"{v:.4f}" for v in values) + f" = {result:.4f}")
output = "### **Randić Index (R)**\n\n"
output += "**Formula:**\n" + formulas["Randić Index (R)"] + "\n"
output += "Where the sum is taken over all edges (u,v) and d(u) and d(v) are their degrees.\n\n"
output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
output += f"**Answer:** The Randić Index R = {result:.4f}."
return output

elif index == "Balaban Index (J)":
parts = user_input.split(';')
if len(parts) < 3:
return "Invalid input format. Use m;q;edge pairs like 6;4;2-3,3-4."
m = int(parts[0])
q = int(parts[1])
edge_degrees = [tuple(map(int, pair.split('-'))) for pair in parts[2].split(',')]
values = [1 / (d1 * d2)**0.5 for d1, d2 in edge_degrees]
numerator = sum(values)
result = (m / (q + 1)) * numerator
steps = [
"1. **Extract parameters and edge pairs:**",
f" - m = {m}, q = {q}",
f" - Edge pairs = {edge_degrees}"
]
for (d1, d2), v in zip(edge_degrees, values):
steps.append(f"2. **For edge {d1}-{d2}:**\n - 1/√({d1}×{d2}) = {v:.4f}")
steps.append("3. **Sum the computed values:**\n - Sum = " + f"{numerator:.4f}")
steps.append("4. **Multiply by m/(q+1):**\n - J = ({m}/({q}+1)) × {numerator:.4f} = {result:.4f}")
output = "### **Balaban Index (J)**\n\n"
output += "**Formula:**\n" + formulas["Balaban Index (J)"] + "\n"
output += "Where m and q are given parameters and the sum is taken over all edges.\n\n"
output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
output += f"**Answer:** The Balaban Index J = {result:.4f}."
return output

elif index == "Harary Index (H)":
distances = list(map(int, user_input.split(',')))
values = [1 / d for d in distances if d > 0]
result = sum(values)
steps = [
"1. **Extract the shortest path distances:**\n - Distances = " + f"{distances}"
]
for d, v in zip(distances, values):
steps.append(f"2. **For distance {d}:**\n - 1/{d} = {v:.4f}")
steps.append("3. **Sum the reciprocals:**\n - H = " + " + ".join(f"{v:.4f}" for v in values) + f" = {result:.4f}")
output = "### **Harary Index (H)**\n\n"
output += "**Formula:**\n" + formulas["Harary Index (H)"] + "\n"
output += "Where d(u,v) is the shortest path distance between vertices u and v.\n\n"
output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
output += f"**Answer:** The Harary Index H = {result:.4f}."
return output

elif index == "Atom-Bond Connectivity Index (ABC)":
edge_degrees = [tuple(map(int, pair.split('-'))) for pair in user_input.split(',')]
values = [((d1**0.5 + d2**0.5) / (d1 + d2)) for d1, d2 in edge_degrees]
result = sum(values)
steps = [
"1. **Extract the edge pairs:**\n - Pairs = " + f"{edge_degrees}"
]
for (d1, d2), v in zip(edge_degrees, values):
steps.append(f"2. **For edge {d1}-{d2}:**\n - (√{d1} + √{d2})/( {d1} + {d2} ) = {v:.4f}")
steps.append("3. **Sum the values:**\n - ABC = " + " + ".join(f"{v:.4f}" for v in values) + f" = {result:.4f}")
output = "### **Atom-Bond Connectivity Index (ABC)**\n\n"
output += "**Formula:**\n" + formulas["Atom-Bond Connectivity Index (ABC)"] + "\n\n"
output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
output += f"**Answer:** The ABC Index = {result:.4f}."
return output

elif index == "Geometric Arithmetic Index (GA)":
edge_degrees = [tuple(map(int, pair.split('-'))) for pair in user_input.split(',')]
values = [(d1 * d2)**0.5 for d1, d2 in edge_degrees]
result = sum(values)
steps = [
"1. **Extract the edge pairs:**\n - Pairs = " + f"{edge_degrees}"
]
for (d1, d2), v in zip(edge_degrees, values):
steps.append(f"2. **For edge {d1}-{d2}:**\n - √({d1}×{d2}) = {v:.4f}")
steps.append("3. **Sum the values:**\n - GA = " + " + ".join(f"{v:.4f}" for v in values) + f" = {result:.4f}")
output = "### **Geometric Arithmetic Index (GA)**\n\n"
output += "**Formula:**\n" + formulas["Geometric Arithmetic Index (GA)"] + "\n\n"
output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
output += f"**Answer:** The GA Index = {result:.4f}."
return output

elif index == "Harmonic Index (Harm)":
degrees = list(map(int, user_input.split(',')))
values = [1 / d for d in degrees]
result = sum(values)
steps = [
"1. **Extract the vertex degrees:**\n - Degrees = " + f"{degrees}"
]
for d, v in zip(degrees, values):
steps.append(f

Files changed (1) hide show
  1. topological_index_visualizer.py +327 -0
topological_index_visualizer.py ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import sys
3
+
4
+ # Manually install required libraries (each installed separately)
5
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "gradio"])
6
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "networkx"])
7
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "matplotlib"])
8
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "numpy"])
9
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "scipy"])
10
+
11
+ import os
12
+ import math
13
+ import itertools
14
+ import numpy as np
15
+ import networkx as nx
16
+ import matplotlib.pyplot as plt
17
+ import gradio as gr
18
+ from scipy.spatial import distance
19
+
20
+ # -------------------------------
21
+ # Step-by-Step Index Calculation Functions
22
+ # -------------------------------
23
+
24
+ # Formulas for the indices (written in a typical mathematical style)
25
+ formulas = {
26
+ "Wiener Index (W)": "W = Σ d(u,v) for all pairs (u,v)",
27
+ "First Zagreb Index (M₁)": "M₁ = Σ [d(v)]² for all vertices v",
28
+ "Second Zagreb Index (M₂)": "M₂ = Σ [d(u) * d(v)] for all adjacent vertices (u,v)",
29
+ "Randić Index (R)": "R = Σ [1 / √(d(u) * d(v))] for all edges (u,v)",
30
+ "Balaban Index (J)": "J = (m / (q + 1)) Σ [1 / √(d(u) * d(v))]",
31
+ "Harary Index (H)": "H = Σ [1 / d(u,v)] for all pairs (u,v)",
32
+ "Atom-Bond Connectivity Index (ABC)": "ABC = Σ [(√d(u) + √d(v))/(d(u) + d(v))] for all edges (u,v)",
33
+ "Geometric Arithmetic Index (GA)": "GA = Σ [√(d(u) * d(v))] for all edges (u,v)",
34
+ "Harmonic Index (Harm)": "Harm = Σ [1 / d(v)] for all vertices v"
35
+ }
36
+
37
+ def solve_index(index, user_input):
38
+ try:
39
+ if index == "Wiener Index (W)":
40
+ distances = list(map(int, user_input.split(',')))
41
+ result = sum(distances)
42
+ steps = [
43
+ "1. **Extract the shortest path distances:**\n - Distances = " + f"{distances}",
44
+ "2. **Sum the distances:**\n - W = " + " + ".join(map(str, distances)) + f" = {result}"
45
+ ]
46
+ output = "### **Wiener Index (W)**\n\n"
47
+ output += "**Formula:**\n" + formulas["Wiener Index (W)"] + "\n"
48
+ output += "Where d(u,v) is the shortest path distance between vertices u and v.\n\n"
49
+ output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
50
+ output += f"**Answer:** The Wiener Index W = {result}."
51
+ return output
52
+
53
+ elif index == "First Zagreb Index (M₁)":
54
+ degrees = list(map(int, user_input.split(',')))
55
+ squared_degrees = [d**2 for d in degrees]
56
+ result = sum(squared_degrees)
57
+ steps = [
58
+ "1. **Retrieve the vertex degrees:**\n - Degrees = " + f"{degrees}",
59
+ "2. **Square each degree:**\n - Squared degrees = " + f"{squared_degrees}",
60
+ "3. **Sum the squared degrees:**\n - M₁ = " + " + ".join(map(str, squared_degrees)) + f" = {result}"
61
+ ]
62
+ output = "### **First Zagreb Index (M₁)**\n\n"
63
+ output += "**Formula:**\n" + formulas["First Zagreb Index (M₁)"] + "\n\n"
64
+ output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
65
+ output += f"**Answer:** The First Zagreb Index M₁ = {result}."
66
+ return output
67
+
68
+ elif index == "Second Zagreb Index (M₂)":
69
+ adj_degrees = [tuple(map(int, pair.split('-'))) for pair in user_input.split(',')]
70
+ products = [d1 * d2 for d1, d2 in adj_degrees]
71
+ result = sum(products)
72
+ steps = [
73
+ "1. **Extract the degree pairs for adjacent vertices:**\n - Pairs = " + f"{adj_degrees}",
74
+ "2. **Multiply each pair:**\n - Products = " + f"{products}",
75
+ "3. **Sum the products:**\n - M₂ = " + " + ".join(map(str, products)) + f" = {result}"
76
+ ]
77
+ output = "### **Second Zagreb Index (M₂)**\n\n"
78
+ output += "**Formula:**\n" + formulas["Second Zagreb Index (M₂)"] + "\n"
79
+ output += "Where d(u) and d(v) are the degrees of adjacent vertices u and v.\n\n"
80
+ output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
81
+ output += f"**Answer:** The Second Zagreb Index M₂ = {result}."
82
+ return output
83
+
84
+ elif index == "Randić Index (R)":
85
+ edge_degrees = [tuple(map(int, pair.split('-'))) for pair in user_input.split(',')]
86
+ values = [1 / (d1 * d2)**0.5 for d1, d2 in edge_degrees]
87
+ result = sum(values)
88
+ steps = [
89
+ "1. **Extract the degree pairs for each edge:**\n - Edge pairs = " + f"{edge_degrees}"
90
+ ]
91
+ for (d1, d2), v in zip(edge_degrees, values):
92
+ steps.append(f"2. **For edge {d1}-{d2}:**\n - 1/√({d1}×{d2}) = {v:.4f}")
93
+ steps.append("3. **Sum the values:**\n - R = " + " + ".join(f"{v:.4f}" for v in values) + f" = {result:.4f}")
94
+ output = "### **Randić Index (R)**\n\n"
95
+ output += "**Formula:**\n" + formulas["Randić Index (R)"] + "\n"
96
+ output += "Where the sum is taken over all edges (u,v) and d(u) and d(v) are their degrees.\n\n"
97
+ output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
98
+ output += f"**Answer:** The Randić Index R = {result:.4f}."
99
+ return output
100
+
101
+ elif index == "Balaban Index (J)":
102
+ parts = user_input.split(';')
103
+ if len(parts) < 3:
104
+ return "Invalid input format. Use m;q;edge pairs like 6;4;2-3,3-4."
105
+ m = int(parts[0])
106
+ q = int(parts[1])
107
+ edge_degrees = [tuple(map(int, pair.split('-'))) for pair in parts[2].split(',')]
108
+ values = [1 / (d1 * d2)**0.5 for d1, d2 in edge_degrees]
109
+ numerator = sum(values)
110
+ result = (m / (q + 1)) * numerator
111
+ steps = [
112
+ "1. **Extract parameters and edge pairs:**",
113
+ f" - m = {m}, q = {q}",
114
+ f" - Edge pairs = {edge_degrees}"
115
+ ]
116
+ for (d1, d2), v in zip(edge_degrees, values):
117
+ steps.append(f"2. **For edge {d1}-{d2}:**\n - 1/√({d1}×{d2}) = {v:.4f}")
118
+ steps.append("3. **Sum the computed values:**\n - Sum = " + f"{numerator:.4f}")
119
+ steps.append("4. **Multiply by m/(q+1):**\n - J = ({m}/({q}+1)) × {numerator:.4f} = {result:.4f}")
120
+ output = "### **Balaban Index (J)**\n\n"
121
+ output += "**Formula:**\n" + formulas["Balaban Index (J)"] + "\n"
122
+ output += "Where m and q are given parameters and the sum is taken over all edges.\n\n"
123
+ output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
124
+ output += f"**Answer:** The Balaban Index J = {result:.4f}."
125
+ return output
126
+
127
+ elif index == "Harary Index (H)":
128
+ distances = list(map(int, user_input.split(',')))
129
+ values = [1 / d for d in distances if d > 0]
130
+ result = sum(values)
131
+ steps = [
132
+ "1. **Extract the shortest path distances:**\n - Distances = " + f"{distances}"
133
+ ]
134
+ for d, v in zip(distances, values):
135
+ steps.append(f"2. **For distance {d}:**\n - 1/{d} = {v:.4f}")
136
+ steps.append("3. **Sum the reciprocals:**\n - H = " + " + ".join(f"{v:.4f}" for v in values) + f" = {result:.4f}")
137
+ output = "### **Harary Index (H)**\n\n"
138
+ output += "**Formula:**\n" + formulas["Harary Index (H)"] + "\n"
139
+ output += "Where d(u,v) is the shortest path distance between vertices u and v.\n\n"
140
+ output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
141
+ output += f"**Answer:** The Harary Index H = {result:.4f}."
142
+ return output
143
+
144
+ elif index == "Atom-Bond Connectivity Index (ABC)":
145
+ edge_degrees = [tuple(map(int, pair.split('-'))) for pair in user_input.split(',')]
146
+ values = [((d1**0.5 + d2**0.5) / (d1 + d2)) for d1, d2 in edge_degrees]
147
+ result = sum(values)
148
+ steps = [
149
+ "1. **Extract the edge pairs:**\n - Pairs = " + f"{edge_degrees}"
150
+ ]
151
+ for (d1, d2), v in zip(edge_degrees, values):
152
+ steps.append(f"2. **For edge {d1}-{d2}:**\n - (√{d1} + √{d2})/( {d1} + {d2} ) = {v:.4f}")
153
+ steps.append("3. **Sum the values:**\n - ABC = " + " + ".join(f"{v:.4f}" for v in values) + f" = {result:.4f}")
154
+ output = "### **Atom-Bond Connectivity Index (ABC)**\n\n"
155
+ output += "**Formula:**\n" + formulas["Atom-Bond Connectivity Index (ABC)"] + "\n\n"
156
+ output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
157
+ output += f"**Answer:** The ABC Index = {result:.4f}."
158
+ return output
159
+
160
+ elif index == "Geometric Arithmetic Index (GA)":
161
+ edge_degrees = [tuple(map(int, pair.split('-'))) for pair in user_input.split(',')]
162
+ values = [(d1 * d2)**0.5 for d1, d2 in edge_degrees]
163
+ result = sum(values)
164
+ steps = [
165
+ "1. **Extract the edge pairs:**\n - Pairs = " + f"{edge_degrees}"
166
+ ]
167
+ for (d1, d2), v in zip(edge_degrees, values):
168
+ steps.append(f"2. **For edge {d1}-{d2}:**\n - √({d1}×{d2}) = {v:.4f}")
169
+ steps.append("3. **Sum the values:**\n - GA = " + " + ".join(f"{v:.4f}" for v in values) + f" = {result:.4f}")
170
+ output = "### **Geometric Arithmetic Index (GA)**\n\n"
171
+ output += "**Formula:**\n" + formulas["Geometric Arithmetic Index (GA)"] + "\n\n"
172
+ output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
173
+ output += f"**Answer:** The GA Index = {result:.4f}."
174
+ return output
175
+
176
+ elif index == "Harmonic Index (Harm)":
177
+ degrees = list(map(int, user_input.split(',')))
178
+ values = [1 / d for d in degrees]
179
+ result = sum(values)
180
+ steps = [
181
+ "1. **Extract the vertex degrees:**\n - Degrees = " + f"{degrees}"
182
+ ]
183
+ for d, v in zip(degrees, values):
184
+ steps.append(f"2. **For degree {d}:**\n - 1/{d} = {v:.4f}")
185
+ steps.append("3. **Sum the reciprocals:**\n - Harm = " + " + ".join(f"{v:.4f}" for v in values) + f" = {result:.4f}")
186
+ output = "### **Harmonic Index (Harm)**\n\n"
187
+ output += "**Formula:**\n" + formulas["Harmonic Index (Harm)"] + "\n\n"
188
+ output += "**Step-by-step solution:**\n\n" + "\n".join(steps) + "\n\n"
189
+ output += f"**Answer:** The Harmonic Index = {result:.4f}."
190
+ return output
191
+
192
+ else:
193
+ return "Index not recognized."
194
+ except Exception as e:
195
+ return f"Error in processing input: {e}"
196
+
197
+ def update_input_placeholder(index):
198
+ placeholders = {
199
+ "Wiener Index (W)": "Enter shortest path distances (e.g., 3,4,5)",
200
+ "First Zagreb Index (M₁)": "Enter vertex degrees (e.g., 2,3,4)",
201
+ "Second Zagreb Index (M₂)": "Enter adjacent vertex degrees (e.g., 2-3,3-4)",
202
+ "Randić Index (R)": "Enter edge degrees (e.g., 2-3,3-4)",
203
+ "Balaban Index (J)": "Enter m;q;edge pairs (e.g., 6;4;2-3,3-4)",
204
+ "Harary Index (H)": "Enter shortest path distances (e.g., 3,4,5)",
205
+ "Atom-Bond Connectivity Index (ABC)": "Enter edge pairs (e.g., 2-3,3-4)",
206
+ "Geometric Arithmetic Index (GA)": "Enter edge pairs (e.g., 2-3,3-4)",
207
+ "Harmonic Index (Harm)": "Enter vertex degrees (e.g., 2,3,4)"
208
+ }
209
+ return placeholders[index], formulas.get(index, "")
210
+
211
+ # -------------------------------
212
+ # Graph Visualization Section
213
+ # -------------------------------
214
+
215
+ def parse_edges(edge_str):
216
+ """Parses a comma-separated list of edges formatted as 'u-v' and returns a list of tuple edges."""
217
+ try:
218
+ edges = [tuple(map(int, e.strip().split("-"))) for e in edge_str.split(",") if e.strip() != ""]
219
+ return edges
220
+ except Exception as e:
221
+ raise ValueError("Error parsing edges. Ensure format is 'u-v, x-y, ...'")
222
+
223
+ def ensure_connectivity(G, pos):
224
+ """If the graph is disconnected, connects the components using a minimum spanning tree on node positions."""
225
+ if nx.is_connected(G):
226
+ return G, pos
227
+ components = list(nx.connected_components(G))
228
+ for i in range(len(components) - 1):
229
+ comp1 = components[i]
230
+ comp2 = components[i + 1]
231
+ min_dist = float('inf')
232
+ best_pair = None
233
+ for u in comp1:
234
+ for v in comp2:
235
+ d = distance.euclidean(pos[u], pos[v])
236
+ if d < min_dist:
237
+ min_dist = d
238
+ best_pair = (u, v)
239
+ if best_pair is not None:
240
+ G.add_edge(*best_pair)
241
+ return G, pos
242
+
243
+ def draw_graph_from_edges(edge_str):
244
+ """Constructs a graph from edge_str, ensures connectivity, and returns the saved image filename."""
245
+ try:
246
+ edges = parse_edges(edge_str)
247
+ except Exception as e:
248
+ return None, f"Error: {e}"
249
+ nodes = set()
250
+ for u, v in edges:
251
+ nodes.update([u, v])
252
+ G = nx.Graph()
253
+ G.add_nodes_from(sorted(nodes))
254
+ G.add_edges_from(edges)
255
+ pos = nx.spring_layout(G, seed=42)
256
+ G, pos = ensure_connectivity(G, pos)
257
+ plt.figure(figsize=(6, 6))
258
+ node_sizes = [300 + 100 * G.degree(n) for n in G.nodes()]
259
+ nx.draw(G, pos, with_labels=True, node_color="lightblue", node_size=node_sizes, edge_color="gray", font_size=10)
260
+ plt.title("Graph Visualization", fontsize=14)
261
+ filename = "graph.png"
262
+ plt.savefig(filename)
263
+ plt.close()
264
+ return filename, "Graph drawn successfully."
265
+
266
+ # -------------------------------
267
+ # Combined Function: Solve Index and Visualize Graph
268
+ # -------------------------------
269
+
270
+ def solve_index_and_visualize(index, user_input, custom_edges):
271
+ """
272
+ Computes the selected index step-by-step and, if custom_edges is provided,
273
+ draws the corresponding graph. Returns the text explanation and the graph image.
274
+ """
275
+ explanation = solve_index(index, user_input)
276
+ image_file = ""
277
+ # Define which indices are graph-related (require edge-based inputs)
278
+ graph_related = ["Randić Index (R)", "Balaban Index (J)", "Atom-Bond Connectivity Index (ABC)", "Geometric Arithmetic Index (GA)", "Harary Index (H)"]
279
+ if custom_edges.strip() != "" and index in graph_related:
280
+ img, msg = draw_graph_from_edges(custom_edges)
281
+ if img is None:
282
+ image_file = None
283
+ explanation += "\n\n" + msg
284
+ else:
285
+ image_file = img
286
+ explanation += "\n\nGraph visualization based on custom edges is provided below."
287
+ else:
288
+ explanation += "\n\n(No graph visualization available for the given input.)"
289
+ return explanation, image_file
290
+
291
+ # -------------------------------
292
+ # Gradio Interface Setup
293
+ # -------------------------------
294
+
295
+ with gr.Blocks() as demo:
296
+ gr.Markdown("# Topological Index Calculator with Graph Visualization")
297
+
298
+ with gr.Row():
299
+ index_dropdown = gr.Dropdown(choices=[
300
+ "Wiener Index (W)",
301
+ "First Zagreb Index (M₁)",
302
+ "Second Zagreb Index (M₂)",
303
+ "Randić Index (R)",
304
+ "Balaban Index (J)",
305
+ "Harary Index (H)",
306
+ "Atom-Bond Connectivity Index (ABC)",
307
+ "Geometric Arithmetic Index (GA)",
308
+ "Harmonic Index (Harm)"
309
+ ], label="Select an Index")
310
+ formula_box = gr.Textbox(label="Formula", interactive=False)
311
+
312
+ input_box = gr.Textbox(label="Enter Values", placeholder="For example, for Wiener: 3,4,5; for Zagreb M2: 2-3,3-4")
313
+ custom_edges = gr.Textbox(label="Graph Edges (Optional)", placeholder="Format: 0-1,1-2,2-3")
314
+
315
+ solve_button = gr.Button("Solve & Visualize")
316
+ output_text = gr.Textbox(label="Step-by-Step Explanation", interactive=False, lines=15)
317
+ output_graph = gr.Image(label="Graph Visualization", interactive=False)
318
+
319
+ # When the dropdown changes, update the input placeholder and formula box.
320
+ index_dropdown.change(fn=lambda index: update_input_placeholder(index),
321
+ inputs=[index_dropdown],
322
+ outputs=[input_box, formula_box])
323
+ solve_button.click(fn=solve_index_and_visualize,
324
+ inputs=[index_dropdown, input_box, custom_edges],
325
+ outputs=[output_text, output_graph])
326
+
327
+ demo.launch()