# `Yog.Multi.Graph`
[🔗](https://github.com/code-shoily/yog_ex/blob/v0.97.0/lib/yog/multi/graph.ex#L1)

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

# `edge_data`

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

# `edge_id`

```elixir
@type edge_id() :: non_neg_integer()
```

# `edge_key`

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

# `t`

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

# `add_edge`

```elixir
@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`

```elixir
@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`

```elixir
@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`

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

Backward compatibility: convert from legacy map format.

# `get_edges`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

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

Convert to legacy map format.

# `to_simple_graph`

```elixir
@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`

```elixir
@spec to_simple_graph_with(t(), (number(), number() -&gt; 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`

```elixir
@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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
