Source code for graphcalc.graphs.invariants.graph_indices

# graphcalc/graphs/invariants/graph_indices.py

import math
import networkx as nx
import graphcalc.graphs as gc
from ..core import SimpleGraph
from graphcalc.utils import enforce_type, GraphLike
from graphcalc.metadata import invariant_metadata

__all__ = [
    "randic_index",
    "zagreb_1",
    "zagreb_2",
    "reciprocal_zagreb_1",
    "reciprocal_zagreb_2",
    "abc_index",
    "ga_index",
    "reciprocal_ga_index",
    "sum_connectivity_index",
    "sombor_index",
    "reciprocal_sombor_index",
    "hyper_zagreb_index",
    "reciprocal_hyper_zagreb_index",
    "augmented_zagreb_index",
    "reciprocal_augmented_zagreb_index",
    "harmonic_index",
]

[docs] @invariant_metadata( display_name="Randić index", notation=r"R_{\alpha}(G)", category="graph indices", aliases=("generalized Randić index", "generalized connectivity index"), definition=( "The generalized Randić index R_alpha(G) of a graph G is the sum over all " "edges uv of (deg(u) deg(v))^alpha." ), ) def randic_index(G, alpha=-0.5): r""" Compute the generalized Randić index :math:`R_\alpha(G)` of a graph :math:`G`. For a real parameter :math:`\alpha`, the **generalized Randić index** (also called the generalized connectivity index) is .. math:: R_\alpha(G) \;=\; \sum_{\{u,v\}\in E(G)} \bigl(d(u)\,d(v)\bigr)^{\alpha}, where :math:`d(u)` denotes the degree of :math:`u`. The **classical Randić index** is the special case :math:`\alpha=-\tfrac12`: .. math:: R(G) \;=\; \sum_{\{u,v\}\in E(G)} \frac{1}{\sqrt{d(u)\,d(v)}}. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. alpha : float, default=-0.5 The exponent :math:`\alpha`. Notes ----- - Only edges contribute, so isolated vertices do not affect the value. - In a simple graph, every edge has endpoints of degree at least 1, so :math:`\alpha<0` causes no division-by-zero issues. - For multigraphs, degrees count multiplicity and parallel edges are summed repeatedly; this matches the literal formula over the multiset of edges, which may differ from some chemistry conventions. Returns ------- float The value :math:`R_\alpha(G)`. Examples -------- A path on 4 vertices has degrees :math:`(1,2,2,1)`, so .. math:: R(G)=\tfrac{1}{\sqrt{2}}+\tfrac{1}{2}+\tfrac{1}{\sqrt{2}}. >>> import networkx as nx >>> import graphcalc.graphs as gc >>> G = nx.path_graph(4) >>> round(gc.randic_index(G), 6) 1.914214 A complete graph :math:`K_n` has all degrees :math:`n-1`, so :math:`R_{-1/2}(K_n) = |E|/(n-1) = n/2`. >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.randic_index(nx.complete_graph(6)) 3.0 Changing :math:`\alpha` changes the weighting: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> G = nx.path_graph(4) >>> gc.randic_index(G, alpha=1) # sum of degree products over edges 8 """ deg = dict(G.degree()) return sum((deg[u] * deg[v]) ** alpha for u, v in G.edges())
[docs] @invariant_metadata( display_name="First Zagreb index", notation=r"M_1(G)", category="graph indices", aliases=("Zagreb 1 index",), definition=( "The first Zagreb index M_1(G) of a graph G is the sum over all vertices v " "of deg(v)^2." ), ) def zagreb_1(G): r""" Compute the first Zagreb index :math:`M_1(G)`. The **first Zagreb index** is .. math:: M_1(G) = \sum_{v \in V(G)} d(v)^2, where :math:`d(v)` is the degree of :math:`v`. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. Returns ------- int The first Zagreb index :math:`M_1(G)`. Examples -------- >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.zagreb_1(nx.path_graph(4)) # degrees 1,2,2,1 10 >>> gc.zagreb_1(nx.complete_graph(4)) # degrees all 3 36 """ return sum(d * d for _, d in G.degree())
[docs] @invariant_metadata( display_name="Second Zagreb index", notation=r"M_2(G)", category="graph indices", aliases=("Zagreb 2 index",), definition=( "The second Zagreb index M_2(G) of a graph G is the sum over all edges uv " "of deg(u) deg(v)." ), ) def zagreb_2(G): r""" Compute the second Zagreb index :math:`M_2(G)`. The **second Zagreb index** is .. math:: M_2(G) = \sum_{\{u,v\} \in E(G)} d(u)\,d(v), where :math:`d(\cdot)` denotes vertex degree. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. Returns ------- int The second Zagreb index :math:`M_2(G)`. Examples -------- >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.zagreb_2(nx.path_graph(4)) # edge degree-products: 1*2 + 2*2 + 2*1 8 >>> gc.zagreb_2(nx.complete_graph(4)) # 6 edges, each contributes 3*3 54 """ deg = dict(G.degree()) return sum(deg[u] * deg[v] for u, v in G.edges())
[docs] @invariant_metadata( display_name="Reciprocal first Zagreb index", notation=r"RM_1(G)", category="graph indices", aliases=("reciprocal Zagreb 1 index",), definition=( "The reciprocal first Zagreb index RM_1(G) of a graph G is the sum over " "all vertices v of 1/deg(v)^2, with isolated vertices contributing 0." ), ) def reciprocal_zagreb_1(G): r""" Compute the reciprocal first Zagreb index :math:`RM_1(G)`. A common “reciprocal” variant replaces :math:`d(v)^2` by its reciprocal: .. math:: RM_1(G) = \sum_{v \in V(G)} \frac{1}{d(v)^2}. Isolated vertices (degree 0) contribute nothing in this implementation. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. Returns ------- float The reciprocal first Zagreb index :math:`RM_1(G)`. Examples -------- >>> import networkx as nx >>> import graphcalc.graphs as gc >>> G = nx.path_graph(4) # degrees 1,2,2,1 >>> gc.reciprocal_zagreb_1(G) 2.5 >>> H = nx.empty_graph(3) # all isolated -> contributes 0 by convention here >>> gc.reciprocal_zagreb_1(H) 0.0 """ s = 0.0 for _, d in G.degree(): if d > 0: s += 1.0 / (d * d) return s
[docs] @invariant_metadata( display_name="Reciprocal second Zagreb index", notation=r"RM_2(G)", category="graph indices", aliases=("reciprocal Zagreb 2 index",), definition=( "The reciprocal second Zagreb index RM_2(G) of a graph G is the sum over " "all edges uv of 1/(deg(u)deg(v))." ), ) def reciprocal_zagreb_2(G): r""" Compute the reciprocal second Zagreb index :math:`RM_2(G)`. A common “reciprocal” variant replaces :math:`d(u)d(v)` by its reciprocal: .. math:: RM_2(G) = \sum_{\{u,v\} \in E(G)} \frac{1}{d(u)\,d(v)}. For simple graphs, every edge has endpoints of degree at least 1, so no division-by-zero occurs. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. Returns ------- float The reciprocal second Zagreb index :math:`RM_2(G)`. Examples -------- >>> import networkx as nx >>> import graphcalc.graphs as gc >>> G = nx.path_graph(4) # edge degree-products: 2,4,2 -> reciprocals: 1/2+1/4+1/2 >>> gc.reciprocal_zagreb_2(G) 1.25 >>> gc.reciprocal_zagreb_2(nx.complete_graph(4)) # 6 edges, each 1/(3*3) 0.6666666666666666 """ deg = dict(G.degree()) return sum(1.0 / (deg[u] * deg[v]) for u, v in G.edges())
[docs] @invariant_metadata( display_name="Atom-Bond Connectivity index", notation=r"\operatorname{ABC}(G)", category="graph indices", aliases=("ABC index",), definition=( "The Atom-Bond Connectivity index ABC(G) of a graph G is the sum over all " "edges uv of sqrt((deg(u)+deg(v)-2)/(deg(u)deg(v)))." ), ) def abc_index(G): r""" Compute the Atom–Bond Connectivity (ABC) index of a graph :math:`G`. The **ABC index** is .. math:: \mathrm{ABC}(G) \;=\; \sum_{\{u,v\}\in E(G)} \sqrt{\frac{d(u)+d(v)-2}{d(u)\,d(v)}}\,, where :math:`d(\cdot)` denotes vertex degree. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. Notes ----- - For a pendant edge with :math:`d(u)=d(v)=1` (which can only occur in :math:`K_2`), the summand is :math:`\sqrt{0/1}=0`. - In a simple graph, every edge has endpoints of degree at least 1, so the denominator is positive. Returns ------- float The Atom–Bond Connectivity index :math:`\mathrm{ABC}(G)`. Examples -------- >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.abc_index(nx.complete_graph(2)) # single edge with degrees 1,1 0.0 For :math:`P_3` (degrees 1-2-1), each edge contributes :math:`\sqrt{1/2}`, so the total is :math:`\sqrt{2}`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> round(gc.abc_index(nx.path_graph(3)), 6) 1.414214 A 4-cycle has 4 edges with degree-pair (2,2), so each contributes :math:`\sqrt{(2)/(4)}=\sqrt{1/2}`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> round(gc.abc_index(nx.cycle_graph(4)), 6) 2.828427 """ deg = dict(G.degree()) s = 0.0 for u, v in G.edges(): du, dv = deg[u], deg[v] s += math.sqrt((du + dv - 2) / (du * dv)) return s
[docs] @invariant_metadata( display_name="Geometric-Arithmetic index", notation=r"\operatorname{GA}(G)", category="graph indices", aliases=("GA index",), definition=( "The Geometric-Arithmetic index GA(G) of a graph G is the sum over all " "edges uv of 2 sqrt(deg(u)deg(v)) divided by deg(u)+deg(v)." ), ) def ga_index(G): r""" Compute the Geometric–Arithmetic (GA) index of a graph :math:`G`. The **GA index** is the degree-based topological index .. math:: \mathrm{GA}(G) \;=\; \sum_{\{u,v\}\in E(G)} \frac{2\sqrt{d(u)\,d(v)}}{d(u)+d(v)}, where :math:`d(\cdot)` denotes vertex degree. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. Notes ----- - For a simple graph, :math:`d(u),d(v)\ge 1` on every edge, and :math:`d(u)+d(v)\ge 2`, so the expression is well-defined. - Each summand lies in :math:`(0,1]` by AM–GM, with equality 1 iff :math:`d(u)=d(v)`. Returns ------- float The GA index :math:`\mathrm{GA}(G)`. Examples -------- For :math:`K_2`, the single edge has degrees (1,1), so GA = 1: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.ga_index(nx.complete_graph(2)) 1.0 For :math:`P_3`, each edge has degrees (1,2), contributing :math:`2\sqrt{2}/3`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> round(gc.ga_index(nx.path_graph(3)), 6) 1.885618 For :math:`C_4`, all edges have degrees (2,2), so each term is 1 and GA = 4: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.ga_index(nx.cycle_graph(4)) 4.0 """ deg = dict(G.degree()) s = 0.0 for u, v in G.edges(): du, dv = deg[u], deg[v] s += (2.0 * math.sqrt(du * dv)) / (du + dv) return s
[docs] @invariant_metadata( display_name="Reciprocal Geometric-Arithmetic index", notation=r"\operatorname{RGA}(G)", category="graph indices", aliases=("reciprocal GA index",), definition=( "The reciprocal Geometric-Arithmetic index RGA(G) of a graph G is the sum " "over all edges uv of (deg(u)+deg(v)) divided by 2 sqrt(deg(u)deg(v))." ), ) def reciprocal_ga_index(G): r""" Compute the reciprocal Geometric–Arithmetic index of a graph :math:`G`. This “reciprocal” variant sums the reciprocals of the GA edge terms: .. math:: \mathrm{RGA}(G) \;=\; \sum_{\{u,v\}\in E(G)} \frac{d(u)+d(v)}{2\sqrt{d(u)\,d(v)}}. For simple graphs, degrees on an edge are at least 1, so this is well-defined. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. Returns ------- float The reciprocal GA index :math:`\mathrm{RGA}(G)`. Examples -------- For :math:`K_2`, RGA = 1: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.reciprocal_ga_index(nx.complete_graph(2)) 1.0 For :math:`C_4`, every edge has degrees (2,2), so each term is 1 and RGA = 4: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.reciprocal_ga_index(nx.cycle_graph(4)) 4.0 """ deg = dict(G.degree()) s = 0.0 for u, v in G.edges(): du, dv = deg[u], deg[v] s += (du + dv) / (2.0 * math.sqrt(du * dv)) return s
[docs] @invariant_metadata( display_name="Sum-connectivity index", notation=r"SC_{\alpha}(G)", category="graph indices", aliases=("generalized sum-connectivity index",), definition=( "The generalized sum-connectivity index SC_alpha(G) of a graph G is the " "sum over all edges uv of (deg(u)+deg(v))^alpha." ), ) def sum_connectivity_index(G, alpha=-0.5): r""" Compute the generalized sum-connectivity index :math:`SC_\alpha(G)` of a graph :math:`G`. For a real parameter :math:`\alpha`, the **generalized sum-connectivity index** is .. math:: SC_\alpha(G) \;=\; \sum_{\{u,v\}\in E(G)} \bigl(d(u)+d(v)\bigr)^{\alpha}, where :math:`d(\cdot)` denotes vertex degree. The classical sum-connectivity index is the special case :math:`\alpha=-\tfrac12`. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. alpha : float, default=-0.5 The exponent :math:`\alpha`. Notes ----- - Only edges contribute, so isolated vertices do not affect the value. - In a simple graph, every edge satisfies :math:`d(u)+d(v)\ge 2`, so negative :math:`\alpha` causes no division-by-zero issues. Returns ------- float The value :math:`SC_\alpha(G)`. Examples -------- For :math:`K_2`, the single edge has degrees (1,1), so :math:`SC_{-1/2} = 2^{-1/2}`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> round(gc.sum_connectivity_index(nx.complete_graph(2)), 6) 0.707107 For :math:`P_4`, edge degree-sums are 3, 4, 3, so :math:`SC_{-1/2} = 3^{-1/2} + 4^{-1/2} + 3^{-1/2}`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> round(gc.sum_connectivity_index(nx.path_graph(4)), 6) 1.654701 With :math:`\alpha=1`, this is the sum of degree-sums over edges: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.sum_connectivity_index(nx.path_graph(4), alpha=1) 10 """ deg = dict(G.degree()) return sum((deg[u] + deg[v]) ** alpha for u, v in G.edges())
[docs] @invariant_metadata( display_name="Sombor index", notation=r"\operatorname{SO}(G)", category="graph indices", aliases=("SO index",), definition=( "The Sombor index SO(G) of a graph G is the sum over all edges uv of " "sqrt(deg(u)^2 + deg(v)^2)." ), ) def sombor_index(G): r""" Compute the Sombor index :math:`SO(G)` of a graph :math:`G`. The **Sombor index** is the degree-based topological index .. math:: SO(G) \;=\; \sum_{\{u,v\}\in E(G)} \sqrt{d(u)^2 + d(v)^2}, where :math:`d(\cdot)` denotes vertex degree. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. Returns ------- float The Sombor index :math:`SO(G)`. Examples -------- For :math:`K_2`, degrees are (1,1), so :math:`SO(K_2)=\sqrt{2}`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> round(gc.sombor_index(nx.complete_graph(2)), 6) 1.414214 For :math:`P_3`, degrees are 1-2-1, so each edge contributes :math:`\sqrt{1^2+2^2}=\sqrt{5}`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> round(gc.sombor_index(nx.path_graph(3)), 6) 4.472136 For :math:`C_4`, all degrees are 2, so each edge contributes :math:`\sqrt{8}`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> round(gc.sombor_index(nx.cycle_graph(4)), 6) 11.313708 """ deg = dict(G.degree()) s = 0.0 for u, v in G.edges(): du, dv = deg[u], deg[v] s += math.sqrt(du * du + dv * dv) return s
[docs] @invariant_metadata( display_name="Reciprocal Sombor index", notation=r"\operatorname{RSO}(G)", category="graph indices", aliases=("RSO index",), definition=( "The reciprocal Sombor index RSO(G) of a graph G is the sum over all edges " "uv of 1 divided by sqrt(deg(u)^2 + deg(v)^2)." ), ) def reciprocal_sombor_index(G): r""" Compute the reciprocal Sombor index :math:`RSO(G)` of a graph :math:`G`. This “reciprocal” variant sums the reciprocals of the per-edge Sombor terms: .. math:: RSO(G) \;=\; \sum_{\{u,v\}\in E(G)} \frac{1}{\sqrt{d(u)^2 + d(v)^2}}. For simple graphs, every edge has endpoints of degree at least 1, so each denominator is at least :math:`\sqrt{2}` and the expression is well-defined. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. Returns ------- float The reciprocal Sombor index :math:`RSO(G)`. Examples -------- >>> import networkx as nx >>> import graphcalc.graphs as gc >>> round(gc.reciprocal_sombor_index(nx.complete_graph(2)), 6) 0.707107 >>> import networkx as nx >>> import graphcalc.graphs as gc >>> round(gc.reciprocal_sombor_index(nx.path_graph(3)), 6) # 2 edges, each 1/sqrt(5) 0.894427 """ deg = dict(G.degree()) s = 0.0 for u, v in G.edges(): du, dv = deg[u], deg[v] s += 1.0 / math.sqrt(du * du + dv * dv) return s
[docs] @invariant_metadata( display_name="Hyper Zagreb index", notation=r"\operatorname{HM}(G)", category="graph indices", aliases=("HM index",), definition=( "The Hyper Zagreb index HM(G) of a graph G is the sum over all edges uv of " "(deg(u)+deg(v))^2." ), ) def hyper_zagreb_index(G): r""" Compute the Hyper Zagreb index :math:`HM(G)` of a graph :math:`G`. The **Hyper Zagreb index** is .. math:: HM(G) \;=\; \sum_{\{u,v\}\in E(G)} \bigl(d(u)+d(v)\bigr)^2, where :math:`d(\cdot)` denotes vertex degree. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. Returns ------- int The Hyper Zagreb index :math:`HM(G)` (integer-valued for simple graphs). Examples -------- For :math:`P_4`, edge degree-sums are 3, 4, 3, so :math:`HM(P_4) = 3^2 + 4^2 + 3^2 = 34`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.hyper_zagreb_index(nx.path_graph(4)) 34 For :math:`K_4`, all degrees are 3 and each of the 6 edges has sum 6, so :math:`HM(K_4) = 6 \cdot 6^2 = 216`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.hyper_zagreb_index(nx.complete_graph(4)) 216 A graph with no edges has Hyper Zagreb index 0: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.hyper_zagreb_index(nx.empty_graph(5)) 0 """ deg = dict(G.degree()) return sum((deg[u] + deg[v]) ** 2 for u, v in G.edges())
[docs] @invariant_metadata( display_name="Reciprocal Hyper Zagreb index", notation=r"\operatorname{RHM}(G)", category="graph indices", aliases=("reciprocal HM index",), definition=( "The reciprocal Hyper Zagreb index RHM(G) of a graph G is the sum over all " "edges uv of 1/(deg(u)+deg(v))^2." ), ) def reciprocal_hyper_zagreb_index(G): r""" Compute the reciprocal Hyper Zagreb index :math:`RHM(G)` of a graph :math:`G`. This “reciprocal” variant sums the reciprocals of the Hyper Zagreb edge terms: .. math:: RHM(G) \;=\; \sum_{\{u,v\}\in E(G)} \frac{1}{\bigl(d(u)+d(v)\bigr)^2}. For simple graphs, every edge satisfies :math:`d(u)+d(v)\ge 2`, so the expression is well-defined. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. Returns ------- float The reciprocal Hyper Zagreb index :math:`RHM(G)`. Examples -------- For :math:`K_2`, the single edge has degree-sum 2, so :math:`RHM=1/4`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.reciprocal_hyper_zagreb_index(nx.complete_graph(2)) 0.25 For :math:`P_4`, edge degree-sums are 3, 4, 3, so :math:`RHM(P_4)=1/3^2 + 1/4^2 + 1/3^2`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> round(gc.reciprocal_hyper_zagreb_index(nx.path_graph(4)), 6) 0.284722 """ deg = dict(G.degree()) return sum(1.0 / ((deg[u] + deg[v]) ** 2) for u, v in G.edges())
[docs] @invariant_metadata( display_name="Augmented Zagreb index", notation=r"\operatorname{AZI}(G)", category="graph indices", aliases=("AZI index",), definition=( "The Augmented Zagreb index AZI(G) of a graph G is the sum over all edges " "uv of ((deg(u)deg(v))/(deg(u)+deg(v)-2))^3, with edges having " "deg(u)+deg(v)-2 <= 0 contributing 0." ), ) def augmented_zagreb_index(G): r""" Compute the Augmented Zagreb index :math:`AZI(G)` of a graph :math:`G` (with a total-function convention). The usual definition is .. math:: AZI(G) \;=\; \sum_{\{u,v\}\in E(G)} \left(\frac{d(u)\,d(v)}{d(u)+d(v)-2}\right)^3, where :math:`d(\cdot)` denotes vertex degree. Convention / edge cases ----------------------- The denominator :math:`d(u)+d(v)-2` is zero exactly when :math:`d(u)=d(v)=1`, which in a simple graph occurs only for :math:`G=K_2`. Different sources either treat :math:`AZI(K_2)` as undefined or define a special value. This implementation uses a **total-function** convention: any edge with :math:`d(u)+d(v)-2 \le 0` contributes 0 to the sum. In particular, this yields :math:`AZI(K_2)=0`. Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. Notes ----- For all other edges in a simple graph, :math:`d(u)+d(v)-2 \ge 1`, so the summand is well-defined. Returns ------- float The Augmented Zagreb index :math:`AZI(G)` under the convention above. Examples -------- By convention, :math:`AZI(K_2)=0`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.augmented_zagreb_index(nx.complete_graph(2)) 0.0 For :math:`P_3`, degrees are 1-2-1, and each edge contributes :math:`((2)/(1))^3 = 8`, so :math:`AZI(P_3)=16`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.augmented_zagreb_index(nx.path_graph(3)) 16.0 For :math:`C_4`, every edge has degrees (2,2), so each contributes :math:`((4)/(2))^3 = 8`, hence :math:`AZI(C_4)=32`: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.augmented_zagreb_index(nx.cycle_graph(4)) 32.0 """ deg = dict(G.degree()) s = 0.0 for u, v in G.edges(): du, dv = deg[u], deg[v] denom = du + dv - 2 if denom <= 0: continue s += ((du * dv) / denom) ** 3 return s
[docs] @invariant_metadata( display_name="Reciprocal Augmented Zagreb index", notation=r"\operatorname{RAZI}(G)", category="graph indices", aliases=("reciprocal AZI index",), definition=( "The reciprocal Augmented Zagreb index RAZI(G) of a graph G is the sum over " "all edges uv of ((deg(u)+deg(v)-2)/(deg(u)deg(v)))^3." ), ) def reciprocal_augmented_zagreb_index(G): r""" Compute a reciprocal variant of the Augmented Zagreb index. This per-edge reciprocal variant is .. math:: RAZI(G) \;=\; \sum_{\{u,v\}\in E(G)} \left(\frac{d(u)+d(v)-2}{d(u)\,d(v)}\right)^3, which is the reciprocal of the usual AZI edge fraction (inside the cube). Parameters ---------- G : networkx.Graph The input graph. Intended for finite simple undirected graphs. Notes ----- - For :math:`K_2`, the single edge has :math:`d(u)=d(v)=1`, so the summand is 0 and :math:`RAZI(K_2)=0`. - For simple graphs, :math:`d(u),d(v)\ge 1` on every edge, so the expression is well-defined. Returns ------- float The reciprocal Augmented Zagreb index :math:`RAZI(G)`. Examples -------- >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.reciprocal_augmented_zagreb_index(nx.complete_graph(2)) 0.0 For :math:`P_3`, each edge has degrees (1,2), so each contributes :math:`((1)/(2))^3 = 1/8` and the total is 1/4: >>> import networkx as nx >>> import graphcalc.graphs as gc >>> gc.reciprocal_augmented_zagreb_index(nx.path_graph(3)) 0.25 """ deg = dict(G.degree()) s = 0.0 for u, v in G.edges(): du, dv = deg[u], deg[v] # denom (of the reciprocal fraction) is du*dv >= 1 on any simple-graph edge s += ((du + dv - 2) / (du * dv)) ** 3 return s
[docs] @enforce_type(0, (nx.Graph, SimpleGraph)) @invariant_metadata( display_name="Harmonic index", notation=r"H(G)", category="graph indices", aliases=("graph harmonic index",), definition=( "The harmonic index H(G) of a graph G is the sum over all edges uv of " "2 divided by deg(u)+deg(v)." ), ) def harmonic_index(G: GraphLike) -> float: r""" Returns the harmonic index of a graph. The harmonic index of a graph is defined as: .. math:: H(G) = \sum_{uv \in E(G)} \frac{2}{d(u) + d(v)} where: - :math:`E(G)` is the edge set of the graph :math:`G`. - :math:`d(u)` is the degree of vertex :math:`u`. The harmonic index is commonly used in mathematical chemistry and network science to measure structural properties of molecular and network graphs. Parameters ---------- G : nx.Graph The graph. Returns ------- float The harmonic index of the graph. Examples -------- >>> import graphcalc.graphs as gc >>> from graphcalc.graphs.generators import path_graph, complete_graph >>> G = path_graph(4) # Path graph with 4 vertices >>> gc.harmonic_index(G) 1.8333333333333333 >>> H = complete_graph(3) # Complete graph with 3 vertices >>> gc.harmonic_index(H) 1.5 Notes ----- - The harmonic index assumes all edge weights are equal to 1. If you want to consider weighted graphs, modify the function to account for edge weights. - The harmonic index is symmetric with respect to the graph's structure, making it invariant under graph isomorphism. References ---------- S. Klavžar and I. Gutman, A comparison of the Schultz molecular topological index and the Wiener index. *Journal of Chemical Information and Computer Sciences*, 33(6), 1006-1009 (1993). """ return 2 * sum(1 / (G.degree(v) + G.degree(u)) for u, v in G.edges())