yog/health

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/5Maximum distance (worst-case reachability)
Radiusradius/5Minimum eccentricity (best central point)
Eccentricityeccentricity/6Maximum distance from a node
Assortativityassortativity/1Degree correlation (homophily)
Average Path Lengthaverage_path_length/6Typical separation

Example

import gleam/int
import gleam/order
import yog/health

let graph = // ... build your graph

// Check graph compactness
let diam = health.diameter(
  in: graph,
  with_zero: 0,
  with_add: int.add,
  with_compare: int.compare,
  with: fn(w) { w }
)
let rad = health.radius(
  in: graph,
  with_zero: 0,
  with_add: int.add,
  with_compare: int.compare,
  with: fn(w) { w }
)

// Small diameter = well-connected
// High assortativity = nodes cluster with similar nodes
let assort = health.assortativity(graph)

Values

pub fn assortativity(graph: model.Graph(n, e)) -> 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

let assort = health.assortativity(graph)
case assort >. 0.0 {
  True -> io.println("Assortative mixing (homophily)")
  False -> io.println("Disassortative mixing")
}
pub fn average_path_length(
  in graph: model.Graph(n, e),
  with_zero zero: e,
  with_add add: fn(e, e) -> e,
  with_compare compare: fn(e, e) -> order.Order,
  with weight_fn: fn(e) -> e,
  with_to_float to_float: fn(e) -> Float,
) -> option.Option(Float)

Average shortest path length across all node pairs. Returns None 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)

Example

let avg_path = health.average_path_length(
  in: graph,
  with_zero: 0,
  with_add: int.add,
  with_compare: int.compare,
  with: fn(w) { w },
  with_to_float: int.to_float
)
case avg_path {
  Some(avg) -> io.println("Average path length: " <> float.to_string(avg))
  None -> io.println("Graph is disconnected")
}
pub fn diameter(
  in graph: model.Graph(n, e),
  with_zero zero: e,
  with_add add: fn(e, e) -> e,
  with_compare compare: fn(e, e) -> order.Order,
  with weight_fn: fn(e) -> e,
) -> option.Option(e)

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

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

Example

let diam = health.diameter(
  in: graph,
  with_zero: 0,
  with_add: int.add,
  with_compare: int.compare,
  with: fn(w) { w }
)
case diam {
  Some(d) -> io.println("Diameter: " <> int.to_string(d))
  None -> io.println("Graph is disconnected")
}
pub fn eccentricity(
  in graph: model.Graph(n, e),
  node node: Int,
  with_zero zero: e,
  with_add add: fn(e, e) -> e,
  with_compare compare: fn(e, e) -> order.Order,
  with weight_fn: fn(e) -> e,
) -> option.Option(e)

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

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

Example

let ecc = health.eccentricity(
  in: graph,
  node: node_id,
  with_zero: 0,
  with_add: int.add,
  with_compare: int.compare,
  with: fn(w) { w }
)
case ecc {
  Some(e) -> io.println("Eccentricity: " <> int.to_string(e))
  None -> io.println("Node cannot reach all others")
}
pub fn radius(
  in graph: model.Graph(n, e),
  with_zero zero: e,
  with_add add: fn(e, e) -> e,
  with_compare compare: fn(e, e) -> order.Order,
  with weight_fn: fn(e) -> e,
) -> option.Option(e)

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

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

Example

let rad = health.radius(
  in: graph,
  with_zero: 0,
  with_add: int.add,
  with_compare: int.compare,
  with: fn(w) { w }
)
case rad {
  Some(r) -> io.println("Radius: " <> int.to_string(r))
  None -> io.println("Graph is disconnected")
}
Search Document