bitwalker / libgraph

A graph data structure library for Elixir projects
MIT License
529 stars 75 forks source link

Multigraphs via an edge index and edge properties/metadata #81

Open zblanco opened 2 months ago

zblanco commented 2 months ago

Starting work to implement #18 as well as #54 for some of my own use cases. Looks like some planning work went into these in the past so not sure if there's a more preferred approach.

Ran into a performance use case for multigraphs where to minimize enumeration on traversals for I wanted an index of edge label sets to trade some space for time.

Current approach

A map edge_index: %{edge_index_key => MapSet.t()} to store the sets of of edge keys: edge_key :: {vertex_id, vertex_id}.

Since the trade-off of space for time shouldn't be assumed by default it's optional with Graph.new(multigraph: true) where an override-able edge_indexer: (Edge.t() -> edge_index_key) is invoked during edge addition but only when multigraph is true. The default indexer builds sets of edge_keys by the label. This lets the sets be built off of the weight or other edge properties.

For metadata / edge properties changing the edge value from %{label => edge_weight} to

@type edge_properties :: %{
          label: label,
          weight: edge_weight,
          properties: map
}

as well as adding the properties map to the %Edge{} struct.

Finally to use the index - reflection / traversal APIs can be extended for predicates such as Graph.out_edges(graph, :a, :foo) where multigraph: true it returns the subset of edges in the index by the key :foo. Not 100% on the API - something more explicit like Graph.out_edges(graph, :a, label: :foo) might be preferred so it can be used without multigraphs or where a function to filter the edges by can be passed in.

Todos: