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:
- Node section: Each line is
node_id node_label - Separator: A single
#character on its own line - 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")
trueParsing 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
serialize/1andserialize_with/2: $\mathcal{O}(V \log V + E)$ time complexity due to node ID sorting, and $\mathcal{O}(V + E)$ space complexity.parse/2andparse_with/4: $\mathcal{O}(V + E)$ time and space 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
Returns default TGF serialization options.
Default behavior:
- Node labels: Convert data to string using
to_string/1 - Edge labels: No labels (returns
:none) - Node formatter:
Yog.Utils.safe_string/1 - Edge formatter:
Yog.Utils.safe_string/1
Example
iex> {:tgf_options, _node_fn, _edge_fn, _node_fmt, _edge_fmt} = Yog.IO.TGF.default_options()
iex> :ok
:ok
Creates TGF options with custom node and edge label functions.
Parameters
node_label- Function to convert node data to string label(node_data) -> stringedge_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
ArgumentErrorifnode_labeloredge_labelare not arity-1 functions. - Raises
ArgumentErrorifoptsis 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
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 stringgtype-:directedor: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
- Raises
ArgumentErrorifgtypeis not:directedor:undirected. - Raises
ArgumentErrorifinputis not a binary.
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
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 stringgraph_type-:directedor:undirectednode_parser- Function transforming node label to node data(label) -> node_dataor(id, label) -> node_dataedge_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
- Raises
ArgumentErrorifgraph_typeis not:directedor:undirected. - Raises
ArgumentErrorifinputis not a binary. - Raises
ArgumentErrorifnode_parseroredge_parserare invalid functions.
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)
Reads a graph from a TGF file using String labels.
Parameters
path- File path to read fromgtype-:directedor:undirected
Returns
{:ok, {:tgf_result, graph, warnings}}on success{:error, reason}on file read or parse failure
Errors
- Raises
ArgumentErrorifpathis not a binary string.
Example
{:ok, {:tgf_result, graph, warnings}} =
Yog.IO.TGF.read("network.tgf", :directed)
if warnings != [] do
IO.puts("Warning: Some lines were malformed")
end
Reads a graph from a TGF file with custom parsers.
Errors
- Raises
ArgumentErrorifpathis not a binary string.
Serializes a graph to TGF format using default label conversion.
Node data is converted to strings, edge labels are omitted.
Errors
- Raises
ArgumentErrorifgraphis not a valid Yog graph struct.
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
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
options- TGF options tuple (seeoptions_with/2)graph- TheYog.GraphorYog.DAGto serialize
Returns
TGF format string
Errors
- Raises
ArgumentErrorifgraphis not a valid Yog graph struct. - Raises
ArgumentErrorifoptionsis not a valid options tuple.
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
Writes a graph to a TGF file using default label conversion.
Parameters
path- File path to write tograph- The graph to serialize
Returns
:okon success{:error, reason}on file write failure
Errors
- Raises
ArgumentErrorifpathis not a binary. - Raises
ArgumentErrorifgraphis not a valid Yog graph struct.
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
Writes a graph to a TGF file with custom label functions.
Parameters
path- File path to write tooptions- TGF options tuple (seeoptions_with/2)graph- The graph to serialize
Returns
:okon success{:error, reason}on file write failure
Errors
- Raises
ArgumentErrorifpathis not a binary. - Raises
ArgumentErrorifoptionsorgraphare invalid.
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)