Yog.Health (YogEx v0.97.0)

Copy Markdown View Source

Network health and structural quality metrics.

These metrics measure the overall "health" and structural properties of your graph, including size, compactness, and connectivity patterns.

Overview

MetricFunctionMeasures
Diameterdiameter/2Maximum distance (worst-case reachability)
Radiusradius/2Minimum eccentricity (best central point)
Eccentricityeccentricity/3Maximum distance from a node
Assortativityassortativity/1Degree correlation (homophily)
Average Path Lengthaverage_path_length/2Typical separation

Example

# Check graph compactness
diam = Yog.Health.diameter(graph, opts)
rad = Yog.Health.radius(graph, opts)

# Small diameter = well-connected
# High assortativity = nodes cluster with similar nodes
assort = Yog.Health.assortativity(graph)

Summary

Types

Options for health metrics that operate on weighted graphs.

A metric value returned by health functions. May be nil for disconnected or empty graphs.

Functions

Assortativity coefficient measures degree correlation.

Average local efficiency over all nodes.

Average shortest path length across all node pairs. Returns nil if the graph is disconnected or empty. Requires a function to convert edge weights to Float for averaging.

The diameter is the maximum eccentricity (longest shortest path). Returns nil if the graph is disconnected or empty.

Eccentricity is the maximum distance from a node to all other nodes. Returns nil if the node cannot reach all other nodes.

Efficiency between two nodes.

Global efficiency of the graph.

Local efficiency of a single node.

The radius is the minimum eccentricity. Returns nil if the graph is disconnected or empty.

Types

metric_opts()

@type metric_opts() :: keyword()

Options for health metrics that operate on weighted graphs.

metric_value()

@type metric_value() :: term() | nil

A metric value returned by health functions. May be nil for disconnected or empty graphs.

Functions

assortativity(graph)

@spec assortativity(Yog.graph()) :: float()

Assortativity coefficient measures degree correlation.

Time Complexity: O(V+E)

Interpreting Assortativity

ValueMeaning
PositiveHigh-degree nodes preferentially connect to other high-degree nodes (assortative)
NegativeHigh-degree nodes connect to low-degree nodes (disassortative)
ZeroRandom mixing, or all nodes have the same degree (regular graph)

Common real-world patterns:

  • Social networks tend to be assortative (people with many friends know each other)
  • Biological and technological networks tend to be disassortative (hubs serve many leaves)

Example

iex> # Star graph is disassortative (center with high degree connects to leaves with low degree)
iex> graph =
...>   Yog.undirected()
...>   |> Yog.add_node(1, "center")
...>   |> Yog.add_node(2, "A")
...>   |> Yog.add_node(3, "B")
...>   |> Yog.add_node(4, "C")
...>   |> Yog.add_edges!([{1, 2, 1}, {1, 3, 1}, {1, 4, 1}])
iex> assort = Yog.Health.assortativity(graph)
iex> assort < 0.0
true

iex> # Empty graph returns 0.0
iex> Yog.Health.assortativity(Yog.undirected())
0.0

average_local_efficiency(graph, opts \\ [])

@spec average_local_efficiency(Yog.graph(), metric_opts()) :: float()

Average local efficiency over all nodes.

This is the mean of local_efficiency/3 for every node in the graph.

Time Complexity: O(V × d² × (d+E') log d)

Example

iex> graph = Yog.undirected() |> Yog.add_node(1, nil) |> Yog.add_node(2, nil) |> Yog.add_node(3, nil) |> Yog.add_edges!([{1, 2, 1}, {1, 3, 1}, {2, 3, 1}])
iex> opts = [with_zero: 0, with_add: &Kernel.+/2, with_compare: &Yog.Utils.compare/2, with: &Function.identity/1, with_to_float: fn x -> x * 1.0 end]
iex> Yog.Health.average_local_efficiency(graph, opts)
1.0

average_path_length(graph, opts \\ [])

@spec average_path_length(Yog.graph(), metric_opts()) :: float() | nil

Average shortest path length across all node pairs. Returns nil if the graph is disconnected or empty. Requires a function to convert edge weights to Float for averaging.

Time Complexity: O(V × (V+E) log V)

Options

  • :with_zero - The zero value for the weight type
  • :with_add - Function to add two weights
  • :with_compare - Function comparing two weights returning :lt, :eq, or :gt
  • :with - Function to extract/transform edge weight
  • :with_to_float - Function to convert weight to float

Interpreting Average Path Length

ValueMeaning
≈ 1.0Dense or highly connected graph (e.g., complete graph)
≈ 2.0Star-like or small-world structure
≈ V/3Chain-like or path-like topology
nilDisconnected or empty graph

A low APL relative to the number of nodes indicates a small-world structure: the graph achieves global connectivity through a small number of hops.

Example

iex> graph =
...>   Yog.undirected()
...>   |> Yog.add_node(1, "A")
...>   |> Yog.add_node(2, "B")
...>   |> Yog.add_node(3, "C")
...>   |> Yog.add_edges!([{1, 2, 1}, {2, 3, 1}, {3, 1, 1}])
iex> opts = [
...>   with_zero: 0,
...>   with_add: &Kernel.+/2,
...>   with_compare: fn a, b ->
...>     cond do a < b -> :lt; a > b -> :gt; true -> :eq end
...>   end,
...>   with: &Function.identity/1,
...>   with_to_float: fn x -> x * 1.0 end
...> ]
iex> avg = Yog.Health.average_path_length(graph, opts)
iex> # In triangle, average path length is 1.0
iex> abs(avg - 1.0) < 0.001
true

diameter(graph, opts \\ [])

@spec diameter(Yog.graph(), metric_opts()) :: metric_value()

The diameter is the maximum eccentricity (longest shortest path). Returns nil if the graph is disconnected or empty.

Time Complexity: O(V × (V+E) log V)

Options

  • :with_zero - The zero value for the weight type (e.g., 0 for integers)
  • :with_add - Function to add two weights (e.g., &Kernel.+/2)
  • :with_compare - Function comparing two weights returning :lt, :eq, or :gt
  • :with - Function to extract/transform edge weight

Interpreting Diameter

ValueMeaning
1Complete graph — everyone is directly connected
2Small world — at most one hop between any pair
> log(V)Relatively sparse or stretched topology
nilDisconnected or empty graph

Example

iex> graph =
...>   Yog.undirected()
...>   |> Yog.add_node(1, "A")
...>   |> Yog.add_node(2, "B")
...>   |> Yog.add_node(3, "C")
...>   |> Yog.add_node(4, "D")
...>   |> Yog.add_edges!([{1, 2, 1}, {2, 3, 1}, {3, 4, 1}])
iex> opts = [
...>   with_zero: 0,
...>   with_add: &Kernel.+/2,
...>   with_compare: fn a, b ->
...>     cond do a < b -> :lt; a > b -> :gt; true -> :eq end
...>   end,
...>   with: &Function.identity/1
...> ]
iex> Yog.Health.diameter(graph, opts)
3

eccentricity(graph, node, opts \\ [])

@spec eccentricity(Yog.graph(), Yog.node_id(), metric_opts()) :: metric_value()

Eccentricity is the maximum distance from a node to all other nodes. Returns nil if the node cannot reach all other nodes.

Time Complexity: O((V+E) log V)

Options

  • :with_zero - The zero value for the weight type
  • :with_add - Function to add two weights
  • :with_compare - Function comparing two weights returning :lt, :eq, or :gt
  • :with - Function to extract/transform edge weight

Interpreting Eccentricity

ValueMeaning
= radiusThe node is in the graph center
= diameterThe node is on the periphery (worst-case reachability)
1The node is adjacent to every other node
0Single-node graph
nilThe node cannot reach all others (disconnected component)

Example

iex> graph =
...>   Yog.undirected()
...>   |> Yog.add_node(1, "A")
...>   |> Yog.add_node(2, "B")
...>   |> Yog.add_node(3, "C")
...>   |> Yog.add_node(4, "D")
...>   |> Yog.add_edges!([{1, 2, 1}, {2, 3, 1}, {3, 4, 1}])
iex> opts = [
...>   with_zero: 0,
...>   with_add: &Kernel.+/2,
...>   with_compare: fn a, b ->
...>     cond do a < b -> :lt; a > b -> :gt; true -> :eq end
...>   end,
...>   with: &Function.identity/1
...> ]
iex> # End nodes have eccentricity 3
iex> Yog.Health.eccentricity(graph, 1, opts)
3
iex> # Middle nodes have eccentricity 2
iex> Yog.Health.eccentricity(graph, 2, opts)
2

efficiency(graph, u, v, opts \\ [])

@spec efficiency(Yog.graph(), Yog.node_id(), Yog.node_id(), metric_opts()) :: float()

Efficiency between two nodes.

The efficiency is the inverse of the shortest path distance. If no path exists, returns 0.0. A node's efficiency to itself is 0.0.

Time Complexity: O((V+E) log V)

Example

iex> graph = Yog.undirected() |> Yog.add_node(1, nil) |> Yog.add_node(2, nil) |> Yog.add_node(3, nil) |> Yog.add_edges!([{1, 2, 1}, {2, 3, 1}])
iex> opts = [with_zero: 0, with_add: &Kernel.+/2, with_compare: &Yog.Utils.compare/2, with: &Function.identity/1, with_to_float: fn x -> x * 1.0 end]
iex> Yog.Health.efficiency(graph, 1, 3, opts)
0.5
iex> Yog.Health.efficiency(graph, 1, 1, opts)
0.0

global_efficiency(graph, opts \\ [])

@spec global_efficiency(Yog.graph(), metric_opts()) :: float()

Global efficiency of the graph.

The average efficiency over all ordered pairs of distinct nodes. Unlike average path length, this is well-defined for disconnected graphs: unreachable pairs simply contribute 0.0.

Time Complexity: O(V × (V+E) log V)

Example

iex> graph = Yog.undirected() |> Yog.add_node(1, nil) |> Yog.add_node(2, nil) |> Yog.add_node(3, nil) |> Yog.add_edges!([{1, 2, 1}, {2, 3, 1}, {3, 1, 1}])
iex> opts = [with_zero: 0, with_add: &Kernel.+/2, with_compare: &Yog.Utils.compare/2, with: &Function.identity/1, with_to_float: fn x -> x * 1.0 end]
iex> Yog.Health.global_efficiency(graph, opts)
1.0

local_efficiency(graph, node, opts \\ [])

@spec local_efficiency(Yog.graph(), Yog.node_id(), metric_opts()) :: float()

Local efficiency of a single node.

Computes the global efficiency of the subgraph induced by the node's neighbors. For directed graphs, the neighborhood includes both successors and predecessors.

Returns 0.0 if the node has fewer than 2 neighbors.

Time Complexity: O(d² × (d+E') log d) where d is the node degree and E' is the number of edges in the neighborhood subgraph.

Example

iex> graph = Yog.undirected() |> Yog.add_node(1, nil) |> Yog.add_node(2, nil) |> Yog.add_node(3, nil) |> Yog.add_edges!([{1, 2, 1}, {1, 3, 1}, {2, 3, 1}])
iex> opts = [with_zero: 0, with_add: &Kernel.+/2, with_compare: &Yog.Utils.compare/2, with: &Function.identity/1, with_to_float: fn x -> x * 1.0 end]
iex> Yog.Health.local_efficiency(graph, 1, opts)
1.0

radius(graph, opts \\ [])

@spec radius(Yog.graph(), metric_opts()) :: metric_value()

The radius is the minimum eccentricity. Returns nil if the graph is disconnected or empty.

Time Complexity: O(V × (V+E) log V)

Options

  • :with_zero - The zero value for the weight type
  • :with_add - Function to add two weights
  • :with_compare - Function comparing two weights returning :lt, :eq, or :gt
  • :with - Function to extract/transform edge weight

Interpreting Radius

ValueMeaning
= diameterHighly symmetric structure (e.g., cycle, complete graph)
<< diameterCentralized topology with a clear hub (e.g., star)
1There exists a central node one hop from everyone else
nilDisconnected or empty graph

Example

iex> graph =
...>   Yog.undirected()
...>   |> Yog.add_node(1, "center")
...>   |> Yog.add_node(2, "A")
...>   |> Yog.add_node(3, "B")
...>   |> Yog.add_edges!([{1, 2, 1}, {1, 3, 1}])
iex> opts = [
...>   with_zero: 0,
...>   with_add: &Kernel.+/2,
...>   with_compare: fn a, b ->
...>     cond do a < b -> :lt; a > b -> :gt; true -> :eq end
...>   end,
...>   with: &Function.identity/1
...> ]
iex> Yog.Health.radius(graph, opts)
1