Yog.Health (YogEx v0.70.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)

Migration Note: This module was ported from Gleam to pure Elixir in v0.53.0. The API remains unchanged.

Summary

Functions

Assortativity coefficient measures degree correlation.

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.

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

Functions

assortativity(graph)

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

Assortativity coefficient measures degree correlation.

  • Positive: high-degree nodes connect to high-degree nodes (assortative)
  • Negative: high-degree nodes connect to low-degree nodes (disassortative)
  • Zero: random mixing

Time Complexity: O(V+E)

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_path_length(graph, opts \\ [])

@spec average_path_length(
  Yog.graph(),
  keyword()
) :: 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

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(),
  keyword()
) :: term() | nil

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

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(), keyword()) :: term() | nil

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

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

radius(graph, opts \\ [])

@spec radius(
  Yog.graph(),
  keyword()
) :: term() | nil

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

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