Yog.Multi.Graph (YogEx v0.97.0)

Copy Markdown View Source

Multigraph with support for parallel edges.

A multigraph allows multiple edges between the same pair of nodes, which is useful for modeling real-world networks like transportation systems (multiple routes between cities) or social networks (multiple types of relationships).

Fields

  • kind - Either :directed or :undirected
  • nodes - Map from node ID to node data
  • edges - Map from {from, to} to list of edges (parallel edge support)
  • next_edge_id - Counter for generating unique edge IDs

Edge Storage

Each edge in the edges map is stored as a tuple: {edge_id, weight} where edge_id is a unique identifier.

Examples

iex> multi = Yog.Multi.Graph.new(:undirected)
iex> multi.kind
:undirected
iex> multi.next_edge_id
0

Summary

Functions

Adds an edge to the multigraph.

Adds a node to the multigraph.

Counts the number of parallel edges between two nodes.

Backward compatibility: convert from legacy map format.

Gets all parallel edges between two nodes.

Creates a new empty multigraph.

Returns the number of nodes in the multigraph.

Removes a specific edge by its edge ID.

Convert to legacy map format.

Converts the multigraph to a simple graph.

Converts to a simple graph, aggregating parallel edges using a combining function.

Returns the total number of edges (including parallel edges).

Types

edge_data()

@type edge_data() :: {edge_id(), number()}

edge_id()

@type edge_id() :: non_neg_integer()

edge_key()

@type edge_key() :: {Yog.Model.node_id(), Yog.Model.node_id()}

t()

@type t() :: %Yog.Multi.Graph{
  edges: %{required(edge_key()) => [edge_data()]},
  kind: :directed | :undirected,
  next_edge_id: non_neg_integer(),
  nodes: %{required(Yog.Model.node_id()) => term()}
}

Functions

add_edge(multi, from, to, weight)

@spec add_edge(t(), Yog.Model.node_id(), Yog.Model.node_id(), number()) ::
  {:ok, t(), edge_id()} | {:error, term()}

Adds an edge to the multigraph.

Returns {:ok, updated_multi, edge_id} on success. Returns {:error, reason} if nodes don't exist.

Parallel edges are allowed - multiple edges can exist between the same pair of nodes.

Examples

iex> multi = Yog.Multi.Graph.new(:undirected)
iex> multi = Yog.Multi.Graph.add_node(multi, 1, nil)
iex> multi = Yog.Multi.Graph.add_node(multi, 2, nil)
iex> {:ok, _multi, edge_id} = Yog.Multi.Graph.add_edge(multi, 1, 2, 10)
iex> is_integer(edge_id)
true

add_node(multi, node_id, data)

@spec add_node(t(), Yog.Model.node_id(), term()) :: t()

Adds a node to the multigraph.

If the node already exists, updates its data.

Examples

iex> multi = Yog.Multi.Graph.new(:undirected)
iex> multi = Yog.Multi.Graph.add_node(multi, 1, "node_data")
iex> Map.has_key?(multi.nodes, 1)
true

edge_count(multi, from, to)

@spec edge_count(t(), Yog.Model.node_id(), Yog.Model.node_id()) :: non_neg_integer()

Counts the number of parallel edges between two nodes.

Examples

iex> multi = Yog.Multi.Graph.new(:undirected)
iex> multi = Yog.Multi.Graph.add_node(multi, 1, nil)
iex> multi = Yog.Multi.Graph.add_node(multi, 2, nil)
iex> {:ok, multi, _} = Yog.Multi.Graph.add_edge(multi, 1, 2, 10)
iex> count = Yog.Multi.Graph.edge_count(multi, 1, 2)
iex> count >= 1
true

from_map(map)

@spec from_map(map()) :: t()

Backward compatibility: convert from legacy map format.

get_edges(graph, from, to)

@spec get_edges(t(), Yog.Model.node_id(), Yog.Model.node_id()) :: [edge_data()]

Gets all parallel edges between two nodes.

Returns a list of {edge_id, weight} tuples.

Examples

iex> multi = Yog.Multi.Graph.new(:undirected)
iex> multi = Yog.Multi.Graph.add_node(multi, 1, nil)
iex> multi = Yog.Multi.Graph.add_node(multi, 2, nil)
iex> {:ok, multi, _} = Yog.Multi.Graph.add_edge(multi, 1, 2, 10)
iex> edges = Yog.Multi.Graph.get_edges(multi, 1, 2)
iex> length(edges) >= 1
true

new(kind)

@spec new(:directed | :undirected) :: t()

Creates a new empty multigraph.

Examples

iex> multi = Yog.Multi.Graph.new(:directed)
iex> multi.kind
:directed
iex> Enum.count(multi.nodes)
0

iex> multi = Yog.Multi.Graph.new(:undirected)
iex> multi.kind
:undirected

node_count(graph)

@spec node_count(t()) :: non_neg_integer()

Returns the number of nodes in the multigraph.

Examples

iex> multi = Yog.Multi.Graph.new(:undirected)
iex> multi = Yog.Multi.Graph.add_node(multi, 1, nil)
iex> multi = Yog.Multi.Graph.add_node(multi, 2, nil)
iex> count = Yog.Multi.Graph.node_count(multi)
iex> count
2

remove_edge(multi, from, to, edge_id)

@spec remove_edge(t(), Yog.Model.node_id(), Yog.Model.node_id(), edge_id()) :: t()

Removes a specific edge by its edge ID.

Examples

iex> multi = Yog.Multi.Graph.new(:undirected)
iex> multi = Yog.Multi.Graph.add_node(multi, 1, nil)
iex> multi = Yog.Multi.Graph.add_node(multi, 2, nil)
iex> {:ok, multi, edge_id} = Yog.Multi.Graph.add_edge(multi, 1, 2, 10)
iex> multi = Yog.Multi.Graph.remove_edge(multi, 1, 2, edge_id)
iex> Yog.Multi.Graph.edge_count(multi, 1, 2)
0

to_map(graph)

@spec to_map(t()) :: map()

Convert to legacy map format.

to_simple_graph(graph)

@spec to_simple_graph(t()) :: Yog.graph()

Converts the multigraph to a simple graph.

When there are parallel edges, only the first edge is kept. This allows compatibility with algorithms that don't support parallel edges.

Examples

iex> multi = Yog.Multi.Graph.new(:undirected)
iex> multi = Yog.Multi.Graph.add_node(multi, 1, nil)
iex> multi = Yog.Multi.Graph.add_node(multi, 2, nil)
iex> {:ok, multi, _} = Yog.Multi.Graph.add_edge(multi, 1, 2, 10)
iex> {:ok, multi, _} = Yog.Multi.Graph.add_edge(multi, 1, 2, 20)
iex> _simple = Yog.Multi.Graph.to_simple_graph(multi)
iex> # simple graph will have only one edge between 1 and 2

to_simple_graph_with(graph, combine_fn)

@spec to_simple_graph_with(t(), (number(), number() -> number())) :: Yog.graph()

Converts to a simple graph, aggregating parallel edges using a combining function.

Examples

iex> multi = Yog.Multi.Graph.new(:undirected)
iex> multi = Yog.Multi.Graph.add_node(multi, 1, nil)
iex> multi = Yog.Multi.Graph.add_node(multi, 2, nil)
iex> {:ok, multi, _} = Yog.Multi.Graph.add_edge(multi, 1, 2, 10)
iex> {:ok, multi, _} = Yog.Multi.Graph.add_edge(multi, 1, 2, 20)
iex> simple = Yog.Multi.Graph.to_simple_graph_with(multi, &Kernel.+/2)
iex> Yog.Model.has_edge?(simple, 1, 2)
true

total_edge_count(graph)

@spec total_edge_count(t()) :: non_neg_integer()

Returns the total number of edges (including parallel edges).

Examples

iex> multi = Yog.Multi.Graph.new(:undirected)
iex> multi = Yog.Multi.Graph.add_node(multi, 1, nil)
iex> multi = Yog.Multi.Graph.add_node(multi, 2, nil)
iex> {:ok, multi, _} = Yog.Multi.Graph.add_edge(multi, 1, 2, 10)
iex> total = Yog.Multi.Graph.total_edge_count(multi)
iex> total >= 1
true