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()