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

Core multigraph type and basic operations.

A multigraph allows multiple (parallel) edges between the same pair of nodes.
Both directed and undirected variants are supported.

The internal representation keeps three indices:
- `edges`: EdgeId → {from, to, data} — canonical edge store
- `out_edge_ids`: NodeId → [EdgeId] — outgoing edges per node
- `in_edge_ids`: NodeId → [EdgeId] — incoming edges per node

# `edge_id`

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

# `t`

```elixir
@type t() :: Yog.Multi.Model.Graph.t()
```

# `add_edge`

```elixir
@spec add_edge(t(), Yog.node_id(), Yog.node_id(), any()) :: {t(), edge_id()}
```

Adds an edge from `from` to `to` with the given data.

Returns `{updated_graph, new_edge_id}`.

For undirected graphs, a single `EdgeId` is issued and the reverse
direction is indexed automatically.

# `add_node`

```elixir
@spec add_node(t(), Yog.node_id(), any()) :: t()
```

Adds a node with the given ID and data.
If the node already exists, its data is replaced (edges are unaffected).

# `all_edge_ids`

```elixir
@spec all_edge_ids(t()) :: [edge_id()]
```

Returns all edge IDs in the graph.

# `all_nodes`

```elixir
@spec all_nodes(t()) :: [Yog.node_id()]
```

Returns all node IDs in the multigraph.

# `directed`

```elixir
@spec directed() :: t()
```

Creates a new, empty directed multigraph.

# `edges_between`

```elixir
@spec edges_between(t(), Yog.node_id(), Yog.node_id()) :: [{edge_id(), any()}]
```

Returns all parallel edges between `from` and `to` as
`[{edge_id, edge_data}]`.

# `has_edge`

```elixir
@spec has_edge(t(), edge_id()) :: boolean()
```

Returns `true` if an edge with this ID exists in the graph.

# `in_degree`

```elixir
@spec in_degree(t(), Yog.node_id()) :: integer()
```

Returns the in-degree of a node (number of incoming edges).

# `new`

```elixir
@spec new(Yog.graph_type()) :: t()
```

Creates a new, empty multigraph of the given type.

# `order`

```elixir
@spec order(t()) :: integer()
```

Returns the number of nodes (graph order).

# `out_degree`

```elixir
@spec out_degree(t(), Yog.node_id()) :: integer()
```

Returns the out-degree of a node (number of outgoing edges).
For undirected graphs, this equals the total degree.

# `predecessors`

```elixir
@spec predecessors(t(), Yog.node_id()) :: [{Yog.node_id(), edge_id(), any()}]
```

Returns all incoming edges to `id` as `[{from_node, edge_id, edge_data}]`.

# `remove_edge`

```elixir
@spec remove_edge(t(), edge_id()) :: t()
```

Removes a single edge by its `EdgeId`.
For undirected graphs, both direction-index entries are removed.

# `remove_node`

```elixir
@spec remove_node(t(), Yog.node_id()) :: t()
```

Removes a node and all edges connected to it.

# `size`

```elixir
@spec size(t()) :: integer()
```

Returns the total number of edges (graph size).
For undirected graphs, each physical edge is counted once.

# `successors`

```elixir
@spec successors(t(), Yog.node_id()) :: [{Yog.node_id(), edge_id(), any()}]
```

Returns all outgoing edges from `id` as `[{to_node, edge_id, edge_data}]`.

# `to_simple_graph`

```elixir
@spec to_simple_graph(t(), (any(), any() -&gt; any())) :: Yog.graph()
```

Collapses the multigraph into a simple `Yog.graph()` by combining
parallel edges with `combine_fn(existing, new)`.

## Example

Keep minimum weight among parallel edges:

    multi.to_simple_graph(mg, fn a, b -> min(a, b) end)

# `to_simple_graph_min_edges`

```elixir
@spec to_simple_graph_min_edges(t()) :: Yog.graph()
```

Collapses parallel edges, keeping the minimum weight.

# `to_simple_graph_sum_edges`

```elixir
@spec to_simple_graph_sum_edges(t(), (any(), any() -&gt; any())) :: Yog.graph()
```

Collapses parallel edges, summing weights.

# `undirected`

```elixir
@spec undirected() :: t()
```

Creates a new, empty undirected multigraph.

---

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