yog/builder/labeled

A builder for creating graphs using arbitrary labels instead of integer IDs.

This module provides a convenient way to build graphs when your nodes are naturally identified by strings or other types, rather than integers. The builder maintains a mapping from labels to internal integer IDs and converts to a standard Graph when needed.

Example

import yog/builder/labeled
import yog/pathfinding
import gleam/int

pub fn main() {
  // Build a graph using string labels
  let builder =
    labeled.directed()
    |> labeled.add_edge("home", "work", 10)
    |> labeled.add_edge("work", "gym", 5)
    |> labeled.add_edge("home", "gym", 12)

  // Convert to a Graph to use with algorithms
  let graph = labeled.to_graph(builder)

  // Get the node IDs for the labels we care about
  let assert Ok(home_id) = labeled.get_id(builder, "home")
  let assert Ok(gym_id) = labeled.get_id(builder, "gym")

  // Now use standard graph algorithms
  case pathfinding.shortest_path(
    in: graph,
    from: home_id,
    to: gym_id,
    with_zero: 0,
    with_add: int.add,
    with_compare: int.compare,
  ) {
    Ok(path) -> // Path found!
    Error(_) -> // No path
  }
}

Types

A builder for graphs that use arbitrary labels instead of integer node IDs.

The builder maintains an internal mapping from labels to integer IDs and stores the label as the node’s data in the underlying graph.

pub type Builder(label, edge_data) {
  Builder(
    graph: model.Graph(label, edge_data),
    label_to_id: dict.Dict(label, Int),
    next_id: Int,
  )
}

Constructors

  • Builder(
      graph: model.Graph(label, edge_data),
      label_to_id: dict.Dict(label, Int),
      next_id: Int,
    )

    Arguments

    graph

    The underlying graph structure

    label_to_id

    Mapping from label to internal node ID

    next_id

    Next available node ID

Values

pub fn add_edge(
  builder: Builder(label, e),
  from src_label: label,
  to dst_label: label,
  with weight: e,
) -> Builder(label, e)

Adds an edge between two labeled nodes.

If either node doesn’t exist, it will be created automatically. For directed graphs, adds a single edge from from to to. For undirected graphs, adds edges in both directions.

Example

builder
|> labeled.add_edge(from: "A", to: "B", with: 10)
|> labeled.add_edge(from: "B", to: "C", with: 5)
pub fn add_node(
  builder: Builder(label, e),
  label: label,
) -> Builder(label, e)

Adds a node with the given label explicitly.

If a node with this label already exists, its data will be replaced. This is useful when you want to add nodes before adding edges.

Example

builder
|> labeled.add_node("Node A")
|> labeled.add_node("Node B")
|> labeled.add_edge("Node A", "Node B", 5)
pub fn add_simple_edge(
  builder: Builder(label, Int),
  from src_label: label,
  to dst_label: label,
) -> Builder(label, Int)

Adds a simple edge with weight 1 between two labeled nodes.

This is a convenience function for graphs with integer weights where a default weight of 1 is appropriate (e.g., unweighted graphs, hop counts). Nodes are created automatically if they don’t exist.

Example

import yog/builder/labeled

let builder = labeled.directed()
  |> labeled.add_simple_edge("home", "work")
  |> labeled.add_simple_edge("work", "gym")
// Both edges have weight 1
pub fn add_unweighted_edge(
  builder: Builder(label, Nil),
  from src_label: label,
  to dst_label: label,
) -> Builder(label, Nil)

Adds an unweighted edge between two labeled nodes.

This is a convenience function for graphs where edges have no meaningful weight. Uses Nil as the edge data type. Nodes are created automatically if they don’t exist.

Example

import yog/builder/labeled

let builder: labeled.Builder(String, Nil) = labeled.directed()
  |> labeled.add_unweighted_edge("A", "B")
  |> labeled.add_unweighted_edge("B", "C")
pub fn all_labels(builder: Builder(label, e)) -> List(label)

Returns all labels that have been added to the builder.

Example

let labels = labeled.all_labels(builder)
// ["Node A", "Node B", "Node C"]
pub fn directed() -> Builder(label, edge_data)

Creates a new empty labeled directed graph builder.

This is a convenience function equivalent to labeled.new(Directed).

Example

import yog/builder/labeled

let builder =
  labeled.directed()
  |> labeled.add_edge("home", "work", 10)
pub fn ensure_node(
  builder: Builder(label, e),
  label: label,
) -> #(Builder(label, e), Int)

Gets or creates a node for the given label, returning the builder and node ID.

If a node with this label already exists, returns its ID without modification. If it doesn’t exist, creates a new node with the label as its data.

Example

let #(builder, node_a) = labeled.ensure_node(builder, "Node A")
let #(builder, node_b) = labeled.ensure_node(builder, "Node B")
// Now you have the IDs and can use them with lower-level operations
pub fn from_list(
  graph_type: model.GraphType,
  edges: List(#(label, label, e)),
) -> Builder(label, e)

Creates a labeled graph builder from a list of edges #(src_label, dst_label, weight).

Example

let builder = labeled.from_list(model.Directed, [#("A", "B", 10), #("B", "C", 5)])
pub fn from_unweighted_list(
  graph_type: model.GraphType,
  edges: List(#(label, label)),
) -> Builder(label, Nil)

Creates a labeled graph builder from a list of unweighted edges #(src_label, dst_label).

Example

let builder = labeled.from_unweighted_list(model.Directed, [#("A", "B"), #("B", "C")])
pub fn get_id(
  builder: Builder(label, e),
  label: label,
) -> Result(Int, Nil)

Looks up the internal node ID for a given label.

Returns Ok(id) if the label exists, Error(Nil) if it doesn’t.

Example

case labeled.get_id(builder, "Node A") {
  Ok(id) -> // Use the ID
  Error(_) -> // Label doesn't exist
}
pub fn new(
  graph_type: model.GraphType,
) -> Builder(label, edge_data)

Creates a new empty labeled graph builder.

Example

import yog/builder/labeled
import yog/model.{Directed}

let builder = labeled.new(Directed)
pub fn predecessors(
  builder: Builder(label, e),
  label: label,
) -> Result(List(#(label, e)), Nil)

Gets the predecessors of a node by its label.

Returns a list of tuples containing the predecessor’s label and edge data.

Example

case labeled.predecessors(builder, "Node A") {
  Ok(predecessors) -> // List of #(label, edge_data)
  Error(_) -> // Node doesn't exist
}
pub fn successors(
  builder: Builder(label, e),
  label: label,
) -> Result(List(#(label, e)), Nil)

Gets the successors of a node by its label.

Returns a list of tuples containing the successor’s label and edge data.

Example

case labeled.successors(builder, "Node A") {
  Ok(successors) -> // List of #(label, edge_data)
  Error(_) -> // Node doesn't exist
}
pub fn to_graph(
  builder: Builder(label, e),
) -> model.Graph(label, e)

Converts the builder to a standard Graph.

The resulting graph uses integer IDs internally and stores the labels as node data. This graph can be used with all yog algorithms.

Example

let graph = labeled.to_graph(builder)
// Now use with pathfinding, traversal, etc.
pub fn undirected() -> Builder(label, edge_data)

Creates a new empty labeled undirected graph builder.

This is a convenience function equivalent to labeled.new(Undirected).

Example

import yog/builder/labeled

let builder =
  labeled.undirected()
  |> labeled.add_edge("A", "B", 5)
Search Document