solverelite / app.py
summrs's picture
app.py
b39c815 verified
import subprocess
import math
import gradio as gr
# Ensure required packages are installed
def install(package):
subprocess.check_call(["pip", "install", package])
install("gradio")
# =========================
# Mathematical Formulas (from the paper and our implementation)
# =========================
formulas = {
"Wiener Index (W)":
"W = Σ d(u,v)\nwhere d(u,v) is the shortest-path distance between every unordered pair of vertices.",
"First Zagreb Index (M₁)":
"M₁ = Σ [deg(v)] for v ∈ V(G)\n(This sums the degrees of all vertices.)",
"Second Zagreb Index (M₂)":
"M₂ = Σ [deg(u) × deg(v)] for each edge uv ∈ E(G)\n(This sums the product of the degrees for each edge.)",
"Randić Index (R)":
"R = Σ [1 / √(deg(u) × deg(v))] for each edge uv ∈ E(G)\n(This measures connectivity by using the inverse square root of vertex degree products.)",
"Atom-Bond Connectivity Index (ABC)":
"ABC = Σ √((deg(u)+deg(v)-2)/(deg(u)×deg(v))) for each edge uv ∈ E(G)\n(This index relates to the molecular stability in chemical graphs.)",
"Augmented Zagreb Index (AZI)":
"AZI = Σ [(deg(u)×deg(v))/(deg(u)+deg(v)-2)]³ for each edge uv ∈ E(G)\n(This index amplifies the contribution of high-degree vertices.)",
"Geometric-Arithmetic Index (GA)":
"GA = Σ [2×√(deg(u)×deg(v))/(deg(u)+deg(v))] for each edge uv ∈ E(G)\n(This index blends the geometric and arithmetic means of vertex degrees.)",
"Sum-Connectivity Index (SCI)":
"SCI = Σ [1/√(deg(u)+deg(v))] for each edge uv ∈ E(G)\n(This index focuses on the sum of the degrees.)",
"Harmonic Index (Harm)":
"Harm = Σ [2/(deg(u)+deg(v))] for each edge uv ∈ E(G)\n(A measure emphasizing balanced degrees.)",
"Gutman Index (Gut)":
"Gut = Σ [deg(u)×deg(v)×d(u,v)] for every unordered pair u,v\n(This index combines connectivity with distance information.)",
"Eccentric Connectivity Index (ECI)":
"ECI = Σ [deg(v)×ecc(v)] for every vertex v\n(Here, ecc(v) is the maximum distance from v to any other vertex.)",
"Total Eccentricity (TE)":
"TE = Σ [ecc(v)] for every vertex v\n(This sums the eccentricity of each vertex.)",
"Harary Index (H)":
"H = Σ [1/d(u,v)] for every unordered pair u,v\n(This sums the reciprocals of distances for each vertex pair.)"
}
# ==========================
# Functions to update which input groups are visible based on the selected index
# ==========================
def update_inputs(selected_index):
# Indices that use vertex-based values only: M₁, TE, ECI.
vertex_vis = selected_index in ["First Zagreb Index (M₁)", "Total Eccentricity (TE)",
"Eccentric Connectivity Index (ECI)"]
# Indices that use edge-based inputs: M₂, R, ABC, AZI, GA, SCI, Harm.
edge_vis = selected_index in ["Second Zagreb Index (M₂)",
"Randić Index (R)",
"Atom-Bond Connectivity Index (ABC)",
"Augmented Zagreb Index (AZI)",
"Geometric-Arithmetic Index (GA)",
"Sum-Connectivity Index (SCI)",
"Harmonic Index (Harm)"]
# Gutman index uses its own group.
gutman_vis = (selected_index == "Gutman Index (Gut)")
# Wiener and Harary indices use distance inputs.
distance_vis = selected_index in ["Wiener Index (W)", "Harary Index (H)"]
return (
gr.update(visible=vertex_vis),
gr.update(visible=edge_vis),
gr.update(visible=gutman_vis),
gr.update(visible=distance_vis)
)
# ==========================
# Calculation Functions
# (For simplicity, we assume a triangle graph with 3 vertices, 3 edges, and 3 unordered pairs.)
# ==========================
def solve_index(selected_index, vertex_deg1, vertex_deg2, vertex_deg3,
vertex_ecc1, vertex_ecc2, vertex_ecc3,
edge1_a, edge1_b, edge2_a, edge2_b, edge3_a, edge3_b,
gutman_degs, gutman_dists,
distance1, distance2, distance3):
try:
# -----------------------
# Vertex-based Calculations
# -----------------------
if selected_index == "First Zagreb Index (M₁)":
degrees = [vertex_deg1, vertex_deg2, vertex_deg3]
result = sum(degrees)
steps = [
f"Step 1: Record the degree of each vertex: {degrees}.",
f"Step 2: Sum all vertex degrees since M₁ is defined as Σ deg(v): {degrees[0]} + {degrees[1]} + {degrees[2]} = {result}."
]
return f"### First Zagreb Index (M₁)\n{formulas[selected_index]}\n\nDetailed Steps:\n" + "\n".join(steps)
elif selected_index == "Total Eccentricity (TE)":
ecc = [vertex_ecc1, vertex_ecc2, vertex_ecc3]
result = sum(ecc)
steps = [
f"Step 1: Record the eccentricity (maximum distance) for each vertex: {ecc}.",
f"Step 2: Sum all eccentricities since TE is defined as Σ ecc(v): {ecc[0]} + {ecc[1]} + {ecc[2]} = {result}."
]
return f"### Total Eccentricity (TE)\n{formulas[selected_index]}\n\nDetailed Steps:\n" + "\n".join(steps)
elif selected_index == "Eccentric Connectivity Index (ECI)":
pairs = [(vertex_deg1, vertex_ecc1), (vertex_deg2, vertex_ecc2), (vertex_deg3, vertex_ecc3)]
contributions = [d * e for d, e in pairs]
result = sum(contributions)
steps = [
f"Step 1: For each vertex, record the pair (degree, eccentricity): {pairs}.",
f"Step 2: Multiply the degree and eccentricity for each vertex: {contributions} (because ECI = Σ [deg(v)×ecc(v)]).",
f"Step 3: Sum these products: {contributions[0]} + {contributions[1]} + {contributions[2]} = {result}."
]
return f"### Eccentric Connectivity Index (ECI)\n{formulas[selected_index]}\n\nDetailed Steps:\n" + "\n".join(steps)
# -----------------------
# Edge-based Calculations
# -----------------------
elif selected_index in ["Second Zagreb Index (M₂)", "Randić Index (R)",
"Atom-Bond Connectivity Index (ABC)", "Augmented Zagreb Index (AZI)",
"Geometric-Arithmetic Index (GA)", "Sum-Connectivity Index (SCI)",
"Harmonic Index (Harm)"]:
edges = [(edge1_a, edge1_b), (edge2_a, edge2_b), (edge3_a, edge3_b)]
if selected_index == "Second Zagreb Index (M₂)":
products = [a * b for a, b in edges]
result = sum(products)
steps = [
f"Step 1: Each edge connects two vertices with degrees as given: {edges}.",
f"Step 2: For each edge, multiply the degrees: {products} (since M₂ sums the product for each edge).",
f"Step 3: Sum these products: {products[0]} + {products[1]} + {products[2]} = {result}."
]
elif selected_index == "Randić Index (R)":
values = [1 / math.sqrt(a * b) for a, b in edges]
result = sum(values)
steps = [
f"Step 1: Record the degree pairs for each edge: {edges}.",
f"Step 2: For each edge, compute 1/√(deg(u)×deg(v)): {['{:.4f}'.format(v) for v in values]}.",
f"Step 3: Sum these values to obtain R: {result:.4f}."
]
elif selected_index == "Atom-Bond Connectivity Index (ABC)":
values = [math.sqrt((a + b - 2) / (a * b)) for a, b in edges]
result = sum(values)
steps = [
f"Step 1: For each edge, note the degree pair: {edges}.",
f"Step 2: Compute √((deg(u)+deg(v)-2)/(deg(u)×deg(v))) for each edge: {['{:.4f}'.format(v) for v in values]}.",
f"Step 3: Sum the results: {result:.4f}."
]
elif selected_index == "Augmented Zagreb Index (AZI)":
values = []
for a, b in edges:
denom = a + b - 2
val = (a * b / denom) ** 3 if denom != 0 else 0
values.append(val)
result = sum(values)
steps = [
f"Step 1: For each edge, record degrees: {edges}.",
f"Step 2: For each, compute ( (deg(u)×deg(v))/(deg(u)+deg(v)-2) )³: {['{:.4f}'.format(v) for v in values]}.",
f"Step 3: Sum these values: {result:.4f}."
]
elif selected_index == "Geometric-Arithmetic Index (GA)":
values = [(2 * math.sqrt(a * b))/(a + b) for a, b in edges]
result = sum(values)
steps = [
f"Step 1: Record edge degree pairs: {edges}.",
f"Step 2: For each edge, calculate 2×√(deg(u)×deg(v))/(deg(u)+deg(v)): {['{:.4f}'.format(v) for v in values]}.",
f"Step 3: Sum to obtain GA: {result:.4f}."
]
elif selected_index == "Sum-Connectivity Index (SCI)":
values = [1 / math.sqrt(a + b) for a, b in edges]
result = sum(values)
steps = [
f"Step 1: Note the degree pairs for each edge: {edges}.",
f"Step 2: For each edge, compute 1/√(deg(u)+deg(v)): {['{:.4f}'.format(v) for v in values]}.",
f"Step 3: Sum the computed values to get SCI: {result:.4f}."
]
elif selected_index == "Harmonic Index (Harm)":
values = [2 / (a + b) for a, b in edges]
result = sum(values)
steps = [
f"Step 1: Record the edge degree pairs: {edges}.",
f"Step 2: For each edge, compute 2/(deg(u)+deg(v)): {['{:.4f}'.format(v) for v in values]}.",
f"Step 3: Sum these values for the final Harmonic Index: {result:.4f}."
]
return f"### {selected_index}\n{formulas[selected_index]}\n\nDetailed Steps:\n" + "\n".join(steps)
# -----------------------
# Gutman Index: Requires vertex degrees and distances
# -----------------------
elif selected_index == "Gutman Index (Gut)":
degs = [float(x) for x in gutman_degs.split(',')]
dists = [float(x) for x in gutman_dists.split(',')]
# For a triangle, the unordered pairs are (1,2), (1,3), and (2,3)
total = degs[0]*degs[1]*dists[0] + degs[0]*degs[2]*dists[1] + degs[1]*degs[2]*dists[2]
steps = [
f"Step 1: The vertex degrees are recorded as: {degs}.",
f"Step 2: The shortest-path distances for unordered vertex pairs (1,2), (1,3), and (2,3) are: {dists}.",
f"Step 3: For each pair, multiply deg(u)×deg(v)×d(u,v):",
f" For (1,2): {degs[0]}×{degs[1]}×{dists[0]} = {degs[0]*degs[1]*dists[0]},",
f" For (1,3): {degs[0]}×{degs[2]}×{dists[1]} = {degs[0]*degs[2]*dists[1]},",
f" For (2,3): {degs[1]}×{degs[2]}×{dists[2]} = {degs[1]*degs[2]*dists[2]}.",
f"Step 4: Sum all products: {total}."
]
return f"### Gutman Index (Gut)\n{formulas[selected_index]}\n\nDetailed Steps:\n" + "\n".join(steps)
# -----------------------
# Distance-based: Wiener and Harary indices
# -----------------------
elif selected_index in ["Wiener Index (W)", "Harary Index (H)"]:
dists = [distance1, distance2, distance3]
if selected_index == "Wiener Index (W)":
result = sum(dists)
steps = [
f"Step 1: Record the shortest-path distances for each unordered pair: {dists}.",
f"Step 2: Sum all distances: {dists[0]} + {dists[1]} + {dists[2]} = {result}."
]
return f"### Wiener Index (W)\n{formulas[selected_index]}\n\nDetailed Steps:\n" + "\n".join(steps)
else: # Harary Index (H)
values = [1/d if d != 0 else 0 for d in dists]
result = sum(values)
steps = [
f"Step 1: Record the shortest-path distances: {dists}.",
f"Step 2: Compute the reciprocal for each distance (1/d): {['{:.4f}'.format(1/d) if d!=0 else 'undefined' for d in dists]}.",
f"Step 3: Sum these reciprocals: {result:.4f}."
]
return f"### Harary Index (H)\n{formulas[selected_index]}\n\nDetailed Steps:\n" + "\n".join(steps)
else:
return "Index not recognized."
except Exception as e:
return f"Error: {e}"
# ==========================
# Build the Gradio Interface with Dynamic Input Groups
# ==========================
with gr.Blocks() as demo:
gr.Markdown("## Topological Index Calculator\n\nSelect an index and enter the required values in the relevant fields. The fields below will change based on the chosen index. (For illustration, this example uses a fixed triangle graph with 3 vertices, 3 edges, and 3 unordered pairs.)")
index_dropdown = gr.Dropdown(
label="Select an Index",
choices=[
"Wiener Index (W)",
"First Zagreb Index (M₁)",
"Second Zagreb Index (M₂)",
"Randić Index (R)",
"Atom-Bond Connectivity Index (ABC)",
"Augmented Zagreb Index (AZI)",
"Geometric-Arithmetic Index (GA)",
"Sum-Connectivity Index (SCI)",
"Harmonic Index (Harm)",
"Gutman Index (Gut)",
"Eccentric Connectivity Index (ECI)",
"Total Eccentricity (TE)",
"Harary Index (H)"
]
)
# Group 1: Vertex-based inputs (for M₁, TE, ECI)
with gr.Group(visible=False) as vertex_group:
gr.Markdown("### Vertex Inputs\nEnter the degree and/or eccentricity for each vertex.")
vertex_deg1 = gr.Number(label="Vertex 1 Degree", value=2)
vertex_deg2 = gr.Number(label="Vertex 2 Degree", value=2)
vertex_deg3 = gr.Number(label="Vertex 3 Degree", value=2)
vertex_ecc1 = gr.Number(label="Vertex 1 Eccentricity", value=1)
vertex_ecc2 = gr.Number(label="Vertex 2 Eccentricity", value=1)
vertex_ecc3 = gr.Number(label="Vertex 3 Eccentricity", value=1)
# Group 2: Edge-based inputs (for M₂, R, ABC, AZI, GA, SCI, Harm)
with gr.Group(visible=False) as edge_group:
gr.Markdown("### Edge Inputs\nFor each edge, enter the degree of each end vertex.")
with gr.Row():
edge1_a = gr.Number(label="Edge 1: Vertex A Degree", value=2)
edge1_b = gr.Number(label="Edge 1: Vertex B Degree", value=2)
with gr.Row():
edge2_a = gr.Number(label="Edge 2: Vertex A Degree", value=2)
edge2_b = gr.Number(label="Edge 2: Vertex B Degree", value=2)
with gr.Row():
edge3_a = gr.Number(label="Edge 3: Vertex A Degree", value=2)
edge3_b = gr.Number(label="Edge 3: Vertex B Degree", value=2)
# Group 3: Gutman inputs (for Gutman Index)
with gr.Group(visible=False) as gutman_group:
gr.Markdown("### Gutman Index Inputs\nEnter vertex degrees and the distances for each unordered vertex pair.")
gutman_degs = gr.Textbox(label="Vertex Degrees (comma-separated)", value="2,2,2")
gutman_dists = gr.Textbox(label="Distances for pairs ((1,2), (1,3), (2,3)) (comma-separated)", value="1,1,1")
# Group 4: Distance-based inputs (for Wiener and Harary indices)
with gr.Group(visible=False) as distance_group:
gr.Markdown("### Distance Inputs\nEnter the shortest-path distance for each unordered vertex pair.")
distance1 = gr.Number(label="Distance for Pair 1", value=1)
distance2 = gr.Number(label="Distance for Pair 2", value=1)
distance3 = gr.Number(label="Distance for Pair 3", value=1)
output_box = gr.Textbox(label="Step-by-Step Detailed Solution", lines=20)
solve_btn = gr.Button("Solve")
# Update input group visibility when index is selected
index_dropdown.change(fn=update_inputs,
inputs=index_dropdown,
outputs=[vertex_group, edge_group, gutman_group, distance_group])
# Run calculations when Solve is clicked
solve_btn.click(fn=solve_index,
inputs=[index_dropdown,
vertex_deg1, vertex_deg2, vertex_deg3,
vertex_ecc1, vertex_ecc2, vertex_ecc3,
edge1_a, edge1_b, edge2_a, edge2_b, edge3_a, edge3_b,
gutman_degs, gutman_dists,
distance1, distance2, distance3],
outputs=output_box)
demo.launch()