Yog.IO.TGF (YogEx v1.0.0)

Copy Markdown View Source

Trivial Graph Format (TGF) serialization support.

Provides functions to serialize and deserialize graphs in TGF format, a simple text-based format suitable for quick graph exchange and debugging.

Format Overview

TGF consists of three parts:

  1. Node section: Each line is node_id node_label
  2. Separator: A single # character on its own line
  3. Edge section: Each line is source_id target_id [edge_label]

Example

iex> graph = Yog.directed()
...> |> Yog.add_node(1, "Alice")
...> |> Yog.add_node(2, "Bob")
...> |> Yog.add_edge_ensure(from: 1, to: 2, with: "follows")
iex>
iex> tgf_string = Yog.IO.TGF.serialize(graph)
iex> String.contains?(tgf_string, "1 Alice")
true
iex> String.contains?(tgf_string, "1 2")
true

Parsing Behavior

When parsing TGF files, the following behaviors apply:

  • Auto-node creation: If an edge references a node ID that was not declared in the node section, a node is automatically created with the ID as its label.
  • Empty labels: Nodes without labels default to using their ID as the label.
  • Malformed lines: Lines that cannot be parsed are skipped and collected as warnings in the TgfResult.

Complexity

Summary

Functions

Returns default TGF serialization options.

Creates TGF options with custom node and edge label functions.

Parses a TGF string into a graph with String labels.

Parses a TGF string into a graph with custom parsers.

Reads a graph from a TGF file using String labels.

Reads a graph from a TGF file with custom parsers.

Serializes a graph to TGF format using default label conversion.

Serializes a graph to TGF format with custom label functions.

Writes a graph to a TGF file using default label conversion.

Writes a graph to a TGF file with custom label functions.

Functions

default_options()

Returns default TGF serialization options.

Default behavior:

Example

iex> {:tgf_options, _node_fn, _edge_fn, _node_fmt, _edge_fmt} = Yog.IO.TGF.default_options()
iex> :ok
:ok

options_with(node_label, edge_label, opts \\ [])

Creates TGF options with custom node and edge label functions.

Parameters

  • node_label - Function to convert node data to string label (node_data) -> string
  • edge_label - Function to convert edge data to optional label (edge_data) -> :none | {:some, string}

  • opts - Keyword list options:
    • :node_formatter - Function converting node ID to string (default: &Yog.Utils.safe_string/1)
    • :edge_formatter - Function converting edge label to string (default: &Yog.Utils.safe_string/1)

Returns

TGF options tuple for use with serialize_with/2

Errors

  • Raises ArgumentError if node_label or edge_label are not arity-1 functions.
  • Raises ArgumentError if opts is not a keyword list or contains unknown options.

Example

iex> options = Yog.IO.TGF.options_with(
...>   fn data -> "Node: " <> to_string(data) end,
...>   fn weight -> {:some, "W:" <> to_string(weight)} end
...> )
iex> {:tgf_options, _, _, _, _} = options
iex> :ok
:ok

parse(input, gtype)

Parses a TGF string into a graph with String labels.

Node and edge labels are stored as strings. For custom data structures, use parse_with/4.

Parameters

  • input - TGF format string
  • gtype - :directed or :undirected

Returns

  • {:ok, {:tgf_result, graph, warnings}} on success
  • {:error, reason} on parsing failure

The warnings list contains any malformed lines that were skipped.

Errors

Example

iex> tgf_string = """
...> 1 Alice
...> 2 Bob
...> #
...> 1 2 follows
...> """
iex> {:ok, {:tgf_result, graph, []}} = Yog.IO.TGF.parse(tgf_string, :directed)
iex> Yog.Model.node_count(graph)
2

parse_with(input, graph_type, node_parser, edge_parser)

Parses a TGF string into a graph with custom parsers.

This function allows you to transform TGF labels into custom Elixir data structures as the graph is built.

Parameters

  • input - TGF format string
  • graph_type - :directed or :undirected
  • node_parser - Function transforming node label to node data (label) -> node_data or (id, label) -> node_data
  • edge_parser - Function transforming edge label to edge data (string | nil) -> edge_data

Returns

  • {:ok, {:tgf_result, graph, warnings}} on success
  • {:error, reason} on parsing failure

Errors

Example

tgf = "1 Alice\n2 Bob\n#\n1 2 5\n"

node_parser = fn label -> String.upcase(label) end
edge_parser = fn label ->
  case label do
    nil -> 1
    val -> String.to_integer(val)
  end
end

{:ok, {:tgf_result, graph, _warnings}} =
  Yog.IO.TGF.parse_with(tgf, :directed, node_parser, edge_parser)

read(path, gtype)

Reads a graph from a TGF file using String labels.

Parameters

  • path - File path to read from
  • gtype - :directed or :undirected

Returns

  • {:ok, {:tgf_result, graph, warnings}} on success
  • {:error, reason} on file read or parse failure

Errors

Example

{:ok, {:tgf_result, graph, warnings}} =
  Yog.IO.TGF.read("network.tgf", :directed)

if warnings != [] do
  IO.puts("Warning: Some lines were malformed")
end

read_with(path, gtype, node_parser, edge_parser)

Reads a graph from a TGF file with custom parsers.

Errors

serialize(graph)

Serializes a graph to TGF format using default label conversion.

Node data is converted to strings, edge labels are omitted.

Errors

Example

iex> graph = Yog.directed()
...> |> Yog.add_node(1, "Alice")
...> |> Yog.add_node(2, "Bob")
...> |> Yog.add_edge_ensure(from: 1, to: 2, with: "follows")
iex> tgf = Yog.IO.TGF.serialize(graph)
iex> String.contains?(tgf, "1 Alice") and String.contains?(tgf, "1 2")
true

serialize_with(options, graph)

Serializes a graph to TGF format with custom label functions.

Allows full control over how node and edge data are converted to TGF labels.

Parameters

Returns

TGF format string

Errors

Example

iex> graph = Yog.directed()
...> |> Yog.add_node(1, %{name: "Alice"})
...> |> Yog.add_node(2, %{name: "Bob"})
...> |> Yog.add_edge_ensure(from: 1, to: 2, with: 10)
iex> options = Yog.IO.TGF.options_with(
...>   fn data -> data.name end,
...>   fn weight -> {:some, Integer.to_string(weight)} end
...> )
iex> tgf_string = Yog.IO.TGF.serialize_with(options, graph)
iex> String.contains?(tgf_string, "1 Alice") and String.contains?(tgf_string, "1 2 10")
true

write(path, graph)

Writes a graph to a TGF file using default label conversion.

Parameters

  • path - File path to write to
  • graph - The graph to serialize

Returns

  • :ok on success
  • {:error, reason} on file write failure

Errors

Example

graph = Yog.directed()
|> Yog.add_node(1, "Alice")
|> Yog.add_node(2, "Bob")
|> Yog.add_edge_ensure(from: 1, to: 2, with: "follows")

Yog.IO.TGF.write("network.tgf", graph)
# => :ok

write_with(path, options, graph)

Writes a graph to a TGF file with custom label functions.

Parameters

  • path - File path to write to
  • options - TGF options tuple (see options_with/2)
  • graph - The graph to serialize

Returns

  • :ok on success
  • {:error, reason} on file write failure

Errors

Example

graph = Yog.directed() |> Yog.add_node(1, %{name: "Alice"})
options = Yog.IO.TGF.options_with(fn d -> d.name end, fn _ -> :none end)

Yog.IO.TGF.write_with("network.tgf", options, graph)