# `Yog.IO.Matrix`
[🔗](https://github.com/code-shoily/yog_ex/blob/v0.97.1/lib/yog/io/matrix.ex#L1)

Adjacency matrix import/export for graph serialization.

This module provides functions to convert between `Yog.Graph` structures
and adjacency matrix representations. Adjacency matrices are commonly
used in graph databases (like House of Graphs), mathematical graph theory,
and dense graph representations.

## Format

An adjacency matrix is a square matrix where `matrix[i][j]` represents
the weight of the edge from node `i` to node `j`. A value of `0` or `nil`
indicates no edge.

## Example

    # Unweighted graph matrix (1 = edge exists, 0 = no edge)
    matrix = [
      [0, 1, 1, 0],
      [1, 0, 0, 1],
      [1, 0, 0, 1],
      [0, 1, 1, 0]
    ]

    graph = Yog.IO.Matrix.from_matrix(:undirected, matrix)

## Use Cases

- Importing graphs from House of Graphs database
- Working with mathematical graph definitions
- Dense graph representations
- Interoperability with matrix-based graph libraries

## Limitations

Adjacency matrices are primarily designed for numerical edge weights. While
`to_matrix/1` can return any Elixir term, `from_matrix/2` strictly expects
numerical weights (or `0`/`nil` for no edge).

## See Also

- `Yog.IO.JSON` - JSON graph format
- `Yog.IO.GraphML` - GraphML (XML) format
- `Yog.IO.GDF` - GUESS GDF format
- `Yog.IO.MatrixMarket` - Matrix Market format

# `from_matrix`

```elixir
@spec from_matrix(:directed | :undirected, [[number()]]) :: Yog.graph()
```

Creates a graph from an adjacency matrix.

The adjacency matrix is a square matrix where `matrix[i][j]` represents
the weight of the edge from node `i` to node `j`. A value of `0` or `nil`
indicates no edge.

## Parameters

- `type` - `:directed` or `:undirected`
- `matrix` - Square matrix (list of lists) representing edge weights

## Examples

    iex> # Unweighted adjacency matrix (1 = edge exists)
    ...> matrix = [
    ...>   [0, 1, 1, 0],
    ...>   [1, 0, 0, 1],
    ...>   [1, 0, 0, 1],
    ...>   [0, 1, 1, 0]
    ...> ]
    iex> graph = Yog.IO.Matrix.from_matrix(:undirected, matrix)
    iex> Yog.Model.order(graph)
    4
    iex> Yog.Model.edge_count(graph)
    4

    iex> # Weighted adjacency matrix
    ...> weighted = [
    ...>   [0, 5, 3, 0],
    ...>   [0, 0, 0, 2],
    ...>   [0, 0, 0, 7],
    ...>   [0, 0, 0, 0]
    ...> ]
    iex> digraph = Yog.IO.Matrix.from_matrix(:directed, weighted)
    iex> Yog.Model.edge_count(digraph)
    4

## Notes

- Node IDs are assigned as integers 0, 1, 2, ... based on matrix row indices
- For undirected graphs, only the upper triangle is processed (i < j)
  to avoid duplicate edges
- Zero values and `nil` are treated as "no edge"

## Raises

- `ArgumentError` if the matrix is not square

# `to_matrix`

```elixir
@spec to_matrix(Yog.graph()) :: {[Yog.node_id()], [[number()]]}
```

Exports a graph to an adjacency matrix representation.

Returns a tuple `{nodes, matrix}` where:
- `nodes` is a list of node IDs in the order they appear in the matrix
- `matrix` is the adjacency matrix (list of lists)

## Examples

    iex> graph = Yog.undirected()
    ...>   |> Yog.add_node(1, nil)
    ...>   |> Yog.add_node(2, nil)
    ...>   |> Yog.add_node(3, nil)
    ...>   |> Yog.add_edge_ensure(from: 1, to: 2, with: 5)
    ...>   |> Yog.add_edge_ensure(from: 2, to: 3, with: 7)
    iex> {nodes, matrix} = Yog.IO.Matrix.to_matrix(graph)
    iex> nodes
    [1, 2, 3]
    iex> matrix
    [[0, 5, 0], [5, 0, 7], [0, 7, 0]]

## Notes

- Unconnected node pairs have weight 0 in the matrix
- For undirected graphs, the matrix is symmetric
- Node order is deterministic (sorted by node ID)

# `to_string`

```elixir
@spec to_string(
  Yog.graph(),
  keyword()
) :: String.t()
```

Exports a graph to a string representation of an adjacency matrix.

## Options

- `weight_formatter` - Function to convert edge weights to strings (default: `&Yog.Utils.safe_string/1`)
- `delimiter` - String to separate values (default: " ")

## Examples

    iex> graph = Yog.undirected()
    ...>   |> Yog.add_edge_ensure(from: 1, to: 2, with: 5)
    ...>   |> Yog.add_edge_ensure(from: 2, to: 3, with: 7)
    iex> Yog.IO.Matrix.to_string(graph)
    "0 5 0\n5 0 7\n0 7 0"

    iex> # Using custom weight formatter for complex weights
    iex> graph = Yog.undirected()
    ...>   |> Yog.add_edge_with(1, 2, [weight: 10], & &1)
    iex> Yog.IO.Matrix.to_string(graph,
    ...>   weight_formatter: fn
    ...>     0 -> "0"
    ...>     [weight: w] -> "w#{w}"
    ...>   end
    ...> )
    "0 w10\nw10 0"

---

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