• Stars
    star
    454
  • Rank 92,709 (Top 2 %)
  • Language
    Elixir
  • License
    MIT License
  • Created almost 7 years ago
  • Updated 12 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A graph data structure library for Elixir projects

libgraph

Master Hex.pm Version Coverage Status

Documentation

About

This library provides:

  • An implementation of a graph datastructure, Graph, designed for both directed and undirected graphs. The API supports undirected graphs, but I'm still getting the tests updated to cover properties of undirected graphs.
  • A priority queue implementation PriorityQueue, oriented towards graphs (it prioritizes lower integer values over high), it is the fastest priority queue I know of which allows arbitrary priorities, and is more or less at parity with pqueue3 from the pqueue library, which supports priorities from 0 to 65535.
  • An idiomatic Elixir API for creating, modifying, and querying its graph structure. Creating and modifying a graph can be done in a single pipeline, and all queries take a Graph as their first parameter (one of my complaints with :digraph is that there is some inconsistency with the API between :digraph and :digraph_utils for no apparent reason).
  • Two "Reducer" implementations for mapping/reducing over a graph. I am trying to figure out the best way to make these extendable and part of the API, so that you can drop in your own shortest path algorithms, etc - but I have yet to come up with an approach that feels good on that front.
  • A Serializer behaviour, for defining custom serialization of graphs, with a Graphviz DOT format serializer provided out of the box.

It is backed by a large suite of tests, including several QuickCheck properties for the graph model. Its API shares some similarity with :digraph, but diverges in favor of a more idiomatic Elixir interface. In addition, over time I'm adding new functions to query the graph in ways not previously supported via :digraph, and introducing support for classifying a graph as undirected if so desired, so that queries over such graphs become easier.

If you are interested in reading more about how you can make use of libgraph, there is an excellent blog post written by Tony Hammond which is a very helpful walkthrough of the library and what can be built with it.

Installation

If available in Hex, the package can be installed by adding libgraph to your list of dependencies in mix.exs:

def deps do
  [{:libgraph, "~> 0.7"}]
end

Rationale

The original motiviation for me to start working on this library is the fact that :digraph requires a minimum of 3 ETS tables per graph, and up to 6 depending on the operations you are performing on the graph. If you are working with a lot of graphs concurrently, as I am, this means you can find yourself in a situation where you hit the system limit for the maximum number of ETS table, and bring your system down. Seeing as how it is ridiculous that trying to use a simple graph could potentially kill my system, and not wanting to hack around the problem, I decided to see if I could build an alternative which was competitive performance-wise, without requiring any ETS tables at all.

The result turned out better than I hoped - it is possible to build a graph datastructure without ETS that is both equally performant (and in many of my benchmarks, better performing), and supports all of the same functionality.

Additionally, I also had a few other things I wanted to address:

  • Inconsistency with argument order in the API between :digraph and :digraph_utils
  • The fact that there are two modules to work with the same datastructure to begin with, and trying to remember what lives where.
  • The lack of extensibility, for example, there is no API with which you can implement your own traversal algorithms. This means you are stuck with whatever way the Erlang maintainers decided was ideal, regardless of whether it suits your use case or not. A great example is single-source shortest path algorithms, where you may want a simple breadth-first search, or perhaps you want to use Dijkstra's algorithm - you are stuck with just one approach with :digraph, which as I understand it, is a breadth-first search.
  • :digraph as the name implies, only supports directed graphs
  • :digraph graphs are unweighted, with no way to supported weighted graphs
  • :digraph graphs are not "inspect-friendly", you get a tuple with the underlying ETS table ids, but that's it, not necessarily a big deal, but it's nice for playing around in the shell if you can see how your code affects the structure.

My speculation as to why :digraph is the way it is, is that when :digraph was originally written, there was no efficient key/value datastructure in Erlang that could support large numbers of keys. At that time, maps weren't even a speck in the eye of the language maintainers. Even after the initial introduction of maps in OTP 18, maps still weren't efficient enough to work with large numbers of keys. It wasn't until OTP 19 that the performance of maps with millions of keys became reasonable. So, it's not that :digraph sucks - it was the best possible implementation at the time; but now that the language has come so far, we can take advantage of some of the new hotness and reinvent it from the ground up :).

Benchmarks

Feel free to take a look under the bench folder in the project root. There a few benchmarks I threw together to keep an eye on a few key areas I wanted to ensure parity with :digraph on. You can run them yourself as well, but I would encourage you to use them as a template to construct a benchmark based on your own use case, and compare them that way, as it will give you a better basis to make your decision on. However, if you do find that libgraph is behind :digraph with a benchmark, please let me know so that I can improve the library!

NOTE: While this library is primarily focused on the Graph data structure it defines, it also contains an implementation of a priority queue (you can find it under the PriorityQueue module), designed for use with graphs specifically, as it considers lower integer values higher priority, which is perfect for the kinds of graph algorithms you need a priority queue for.

Contributing

To run the test suite you will need to run mix eqc.install --mini once you've cloned the repo and fetched dependencies.

If you have changes in mind that are significant or potentially time consuming, please open a RFC-style PR first, where we can discuss your plans first. I don't want you to spend all your time crafting a PR that I ultimately reject because I don't think it's a good fit or is too large for me to review. Not that I plan to reject PRs in general, but I have to be careful to balance features with maintenance burden, or I will quickly be unable to manage the project.

Please ensure that you adhere to a commit style where logically related changes are in a single commit, or broken up in a way that eases review if necessary. Keep commit subject lines informative, but short, and provide additional detail in the extended message text if needed. If you can, mention relevant issue numbers in either the subject or the extended message.

Roadmap

Please open an issue if you have a feature request!

License

MIT (See the LICENSE file)

More Repositories

1

distillery

Simplify deployments in Elixir with OTP releases!
Elixir
2,944
star
2

timex

A complete date/time library for Elixir projects.
Elixir
1,717
star
3

swarm

Easy clustering, registration, and distribution of worker processes for Erlang/Elixir
Elixir
1,179
star
4

exrm

Automatically generate a release for your Elixir project!
Elixir
923
star
5

exprotobuf

Protocol Buffers in Elixir made easy!
Elixir
481
star
6

conform

Easy, powerful, and extendable configuration tooling for releases.
Elixir
378
star
7

keys.js

Easy keybindings for browser applications!
JavaScript
360
star
8

alpine-elixir-phoenix

An Alpine Linux base image containing Elixir, Erlang, Node, Hex, and Rebar. Ready for Phoenix applications!
Makefile
349
star
9

alpine-elixir

A Dockerfile based on my alpine-erlang image for Elixir applications
Makefile
202
star
10

toml-elixir

An implementation of TOML for Elixir projects, compliant with the latest specification
Elixir
196
star
11

combine

A parser combinator library for Elixir projects
Elixir
194
star
12

libring

A fast consistent hash ring implementation in Elixir
Elixir
192
star
13

timex_ecto

An adapter for using Timex DateTimes with Ecto
Elixir
161
star
14

exirc

IRC client adapter for Elixir projects
Elixir
149
star
15

artificery

A toolkit for creating terminal user interfaces in Elixir
Elixir
121
star
16

alpine-erlang

An alpine image with Erlang installed, intended for releases
Dockerfile
82
star
17

strukt

Extends defstruct with schemas, changeset validation, and more
Elixir
71
star
18

ex_unit_clustered_case

An extension for ExUnit for simplifying tests against a clustered application
Elixir
57
star
19

uniq

Provides UUID generation, parsing, and formatting. Supports RFC 4122, and the v6 draft extension
Elixir
52
star
20

distillery-aws-example

An example application to go with the AWS guide in the Distillery documentation
Elixir
50
star
21

distillery-umbrella-test

An example Elixir application for working with umbella apps and Distillery.
Elixir
49
star
22

pluginhost

An example C# application which provides runtime extensibility via plugins
C#
31
star
23

picosat_elixir

Elixir + Erlang bindings for the PicoSAT solver
C
17
star
24

distillery-test

Elixir application which demonstrates a bare-minimum release-ready app using Distillery.
Elixir
16
star
25

docker-release-toolkit

My personal toolkit for building releases with Docker
Makefile
16
star
26

libswagger

A Swagger client library for Elixir projects
Elixir
15
star
27

stringex

A string extensions library for node.js
JavaScript
15
star
28

8bit-background

A sweet series of 8-bit backgrounds, which changes based on the time of day.
Shell
15
star
29

scaladiff

A diff library built in Scala, for Scala projects
Scala
13
star
30

dotfiles

My assorted dotfiles
Shell
10
star
31

resume

The TeX source for my current resume.
TeX
7
star
32

fogbugz-cli

FogBugz Command Line Client
Ruby
7
star
33

aria

An experiment in programming language design
7
star
34

s2i-alpine-base

An S2I base image using Alpine Linux
Python
5
star
35

RPNCalculator

Reverse Polish Notation calculator built in Scala for a impromptu code challenge at work
Scala
5
star
36

uniq_compat

A compatibility shim for ::elixir_uuid when used with :uniq
Elixir
4
star
37

centos7-elixir

A CentOS7 base image for use with the Distillery AWS guide
Dockerfile
4
star
38

aws-dist-test

Clone of distillery-aws-example to illustrate distribution
Elixir
4
star
39

firefly

TBD
Elixir
3
star
40

conform_exrm

Conform plugin for ExRM
Elixir
3
star
41

alpine-toolbox

A toolbox image based on Alpine Linux for when I want to troubleshoot things in my OpenShift cluster
Makefile
3
star
42

SharpTools

A collection of practical C# code to build upon
C#
2
star
43

functools

Functional programming tools for Go
Go
2
star
44

toolbox.js

A general purpose javascript utility library. Contains everything you need, and most anything worth having (other than DOM manipulation).
JavaScript
1
star
45

erl_tar2

A re-implementation of erl_tar to support common tar archive formats
Erlang
1
star
46

blog

My personal blog source
CSS
1
star
47

s2i-elixir

An S2I image which provides Elixir, Erlang, and Node.js
Shell
1
star
48

s2i-erlang

An S2I image which provides Erlang and Node.js
Shell
1
star