com.danielkeogh.graph
2024-10-12
A fast an reliable graph library.
com.danielkeogh.graph
This library contains graph datastructures and algorithms.
Getting started
Create and manipulate graphs like this:
(defpackage #:my-package (:use #:cl) (:local-nicknames (:com.danielkeogh.graph :g))) (in-package #:my-package) (let ((graph (g:make-bidirectional-graph))) (g:add-vertices graph 0 1) (g:add-edge graph 0 1))
The com.danielkeogh.graph system consists of multiple packages. These are as follows:
com.danielkeogh.graph
The main API for building and traversing graphs.
com.danielkeogh.graph.algorithms
A collection of useful algorithms that can be used against graphs that implement the API.
Structure specific packages
Each graph implementation also has its own package that you can use if you want to avoid the performance overhead of using generics in the main API. These are often compiled with (declare (optimize (speed 3) (safety 0))), and so not recommended by default.
com.danielkeogh.graph.adjacencycom.danielkeogh.graph.bidirectionalcom.danielkeogh.graph.bidirectional-matrixcom.danielkeogh.graph.undirectedcom.danielkeogh.graph.edge
What is a graph?
A graph is a datastructure that represents connections between nodes. In graph theory these nodes are called vertices and the links between them are called edges.
Defining terms
The names used in the API are derived from graph theory terminology. For those unfamiliar with graph theory, or for those needing a refresher, here are some definitions:
Vertex
A vertex is a node or point in a graph. Multiple are called vertices. ("Vertexes" is a valid alternative, but is less preferred by most surveyed programmers.)
Edge
An edge is a connection between two vertices. Edges may be directed or undirected, also known as arcs or lines.
Parallel Edges
If a graph allows more than one edge between the same pair of vertices, it is a multi-graph
Multi-graph
A graph that allows more than one edge between the same pair of vertices.
Directed Graph
A graph where each edge has a direction. That is an edge from A to B is not the same as an edge from B to A.
Directed Acyclic Graph
A directed acyclic graph (DAG) is a Directed Graph where no edges form any cycles or loops.
Strongly connected
Vertices are considered strongly connected when they are a part of a loop. That is, any given vertex in the set of strongly connected vertices can be used as a starting point to traverse to any other.
Clique
A clique is a complete sub-graph of another graph. It is a subset of vertices where all vertices are connected via edges to all other vertices in the set.
Maximum Clique
The clique with the maximum number of vertices in a graph.
Maximal Clique
A clique that is not sub-graph of any other clique.
Complete Graph
An undirected graph in which every vertex is connected to every other vertex by a single edge.
Graph Partitioning
Graph Partioning is the process of dividing a graph into smaller sub-graphs by grouping sets of related vertices into a single vertex. The edges connecting the grouped vertices ot other vertices are maintained. A well-known algorithm for graph partitioning is the Kernighan-Lin algorithm.
Graph API
Constructors
These are functions that instantiate the different graph and edge types supported by this library.
If you need help choosing, make-bidirectional-graph and make-undirected-graph are the most flexible graph implementations for directed and undirected graphs respectively.
make-adjacency-graph
(make-adjacency-graph &key (allow-parallel-edges t) (vertex-equality-fn #'eql))
Create directed graph optimized for out-edges access.
allow-parallel-edgescontrols whether multiple edges can exist between the same two verticesvertex-equality-fnshould be a hash-table compatible comparer function.
make-bidirectional-graph
(make-bidirectional-graph &key (allow-parallel-edges t) (vertex-equality-fn #'eql))
Create a directed graph optimized for out-edges and in-edges access.
allow-parallel-edgescontrols whether multiple edges can exist between the same two verticesvertex-equality-fnshould be a hash-table compatible comparer function.
make-bidirectional-matrix-graph
(make-bidirectional-matrix-graph vertex-count)
Create a directed graph optimized for finding an edge between any two vertices quickly. This graph type does not support add-vertex or remove-vertex.
vertex-countdetermines the number of vertices in a graph, which are represented asfixnumstarting from0.
make-undirected-graph
(make-undirected-graph &key (allow-parallel-edges t) (vertex-equality-fn #'eql))
Create an undirected graph optimized for finding edges of a given vertex quickly.
allow-parallel-edgescontrols whether multiple edges can exist between the same two verticesvertex-equality-fnshould be a hash-table compatible comparer function.
make-edge
(make-edge source target)
Create an edge that can be added to graphs between two vertices.
Builders
These are functions and methods for modifying the edges and vertices in a graph.
add-vertex
(add-vertex graph vertex)
Add a vertex to a graph that supports the adding of new vertices. (I'm not sure what you expected.)
add-vertices
(add-vertices graph &rest vertices)
Add a set of vertices to a graph.
add-edge
(add-edge graph edge)
Add an edge to a graph.
add-edges
(add-edges graph &rest edges)
Add a set of edges to a graph.
add-edges-and-vertices
(add-edges-and-vertices graph &rest edges)
Add a set of edges to a graph, also adding any vertices in those edges that aren't in a graph yet.
add-edge-between
(add-edge-between graph vertex1 vertex2)
Create and add an edge to a graph between two vertices.
add-edges-and-vertices-between
(add-edges-and-vertices-between graph &rest pairs)
Create and add edges and any vertices not already a part of a graph in a plist.
e.g.
(add-edges-and-vertices-between graph (1 2) (1 3) (2 3))
remove-vertex
(remove-vertex graph vertex)
Remove a vertex and all connecting edges from a graph.
remove-edge
(remove-edge graph vertex)
Remove an edge between two vertices.
remove-edge-between
(remove-edge-between graph vertex1 vertex2)
Remove any edges in a graph between two vertices.
Tests and Predicates
is-directed
(is-directed graph)
Return t if it's a directed graph.
has-vertex
(has-vertex graph vertex)
Return t if the vertex is in a graph.
has-edge
(has-edge graph edge)
Return t if the edge is in a graph.
has-edge-between
(has-edge-between graph vertex1 vertex2)
Return t if there is at least one edge between vertex1 and vertex2.
vertex-equals
(vertex-equals graph vertex1 vertex2)
Use a graph's equality function to check if any pair of vertices are equal.
Graph Accessors
These are functions and methods that access the edges and vertices of a graph.
for-in-out-edges
(for-in-out-edges graph vertex fn)
Apply the function fn to all edges connected to a given vertex.
fnshould be like(lambda (edge) ...)
for-vertices
(for-vertices graph fn)
Apply the function fn to all vertices in a graph.
fnshould be like(lambda (vertex) ...)
for-roots
(for-roots graph fn)
Apply the function fn to all vertices without in-edges a directed graph.
fnshould be like(lambda (vertex) ...)
for-edges
(for-edges graph fn)
Apply the function fn to all edges in a graph.
fnshould be like(lambda (edge) ...)
adjacent-edges
(adjacent-edges graph vertex)
Return a list of the edges connected to a vertex in an undirected graph.
out-edges
(out-edges graph vertex)
Return a list of the outbound edges of a vertex in a directed graph.
in-edges
(in-edges graph vertex)
Return a list of the inbound edges of a vertex in a directed graph.
vertices
(vertices graph)
Return a list of all vertices in a graph.
roots
(roots graph)
Return a list of all vertices with no inbound edges in a directed graph.
edges
(edges graph)
Return a list of all edges in a graph.
vertex-count
(vertex-count graph)
Return the number of vertices in a graph.
edge-count
(edge-count graph)
Return the number of edge in a graph.
graph-vertex-equality-fn
(graph-vertex-equality-fn graph)
Return the function that is used to check if two vertices are the same for a graph.
- The function will take the form of
(lambda (vertex1 vertex2) ...)and returntif the vertexes are equal. - For most graphs types, the default value type is
eql.
Edge Readers
Functions to access slots in edge objects.
edge-source
(edge-source edge)
Returns the source vertex of an edge.
- Edges in an undirected graph still have a source and target value.
edge-target
(edge-target edge)
Returns the target vertex of an edge.
- Edges in an undirected graph still have a source and target value.
Dynamic Builders
These functions and macros are for building a graph without using the graph as a parameter.
with-graph*
(with-graph* (graph) &body body)
A macro to set the dynamic variable *graph*, which is used by the dynamic graph building functions.
add-vertex*
(add-vertex* vertex)
Add a vertex to *graph*.
add-edge*
(add-edge* edge)
Add an edge to *graph*.
add-edges-and-vertices*
(add-edges-and-vertices* &rest edges)
Add a set of edges to *graph*. Also ensure all new vertices are added.
add-edges-and-vertices-between*
(add-edges-and-vertices-between* &rest pairs)
Make a set of edges from a plist and add them to a graph. Also ensure all new vertices are added.
e.g.
(add-edges-and-vertices-between* (1 2) (1 3) (2 3))
Utilities
Helpful utilities.
pretty-print
(pretty-print graph &optional (stream t))
Prints the list of all vertices and then edges in the graph to stream.
e.g.
VERTICES: 1, 2, 3
EDGES: 1->2, 2->3, 1->3
graph-equals
(graph-equals graph1 graph2)
Check if two graphs have the same set of edges and vertices.
Conditions
Conditions that can be thrown by this library.
unsupported-generic
(unsupported-generic :supported-by)
Will be thrown when a method cannot be implemented by a given graph type. This includes, but is not limited to:
- Trying to access
out-edgesorin-edgesof an undirected graph. - Trying to access
adjacentedges of a directed graph. - Trying to
add-vertexorremove-vertexfrom abidirectional-matrix-graph
The supported-by slot describes the types that are supported by the method as a helpful hint.
Algorithms API
This library provides a collection of popular graph-traversing algorithms for your convenience.
Search Algorithms
Bidirectional Breadth First Search Algorithm
Visit vertices in a directed graph by visiting all of a vertices edges first, then all edges connected to those vertices, and then all vertices connected to those vertices, and so-on.
As this is bidirectional it traverses both in and out edges of each vertex.
(function (directed-graph &key (:root-vertex vertex) (:queue-size fixnum) (:on-discover-vertex-fn (function (vertex))) (:on-examine-vertex-fn (function (vertex))) (:on-vertex-finished-fn (function (vertex))) (:on-gray-target-fn (function (edge:edge))) (:on-black-target-fn (function (edge:edge)))))
:root-vertexThe starting vertex. If root vertex is nil, all vertices will be searched, otherwise, vertices not connected to the root vertex will not be visited.:queue-sizeThe maximum size of the queue of unvisited vertices.:on-discover-vertex-fnIs called the first time a vertex is found.:on-examine-vertex-fnIs called before a vertexes edges are visited.:on-vertex-finished-fnIs called after all edges of a vertex have been visisted.:on-gray-target-fnIs called for each edge that is visited where the next vertex has no yet been visited.:on-black-target-fnIs called for each edge that is visited where the next vertex has already finished being visited.
Breadth First Search Algorithm
Visit vertices in a directed graph by visiting all of a vertices out-edges, then all out-edges connected to those vertices, and so on.
(function (directed-graph &key (:root-vertex vertex) (:queue-size fixnum) (:on-discover-vertex-fn (function (vertex))) (:on-examine-vertex-fn (function (vertex))) (:on-vertex-finished-fn (function (vertex))) (:on-gray-target-fn (function (edge:edge))) (:on-black-target-fn (function (edge:edge)))))
:root-vertexThe starting vertex. If root vertex is nil, all vertices will be searched, otherwise, vertices that are not connected via the out edges of the root vertex will not be visited.:queue-sizeThe maximum size of the queue of unvisited vertices.:on-discover-vertex-fnIs called the first time a vertex is found.:on-examine-vertex-fnIs called before a vertexes edges are visited.:on-vertex-finished-fnIs called after all edges of a vertex have been visisted.:on-gray-target-fnIs called for each edge that is visited where the next vertex has no yet been visited.:on-black-target-fnIs called for each edge that is visited where the next vertex has already finished being visited.
Depth First Search Algorithm
Visit vertices in a directed graph recursively traversing each out edge of a given vertex.
(function (directed-graph &key (:root-vertex vertex) (:process-all-vertices boolean) (:max-depth most-positive-fixnum) (:on-start-vertex-fn (function (vertex))) (:on-tree-edge-fn (function (edge:edge))) (:on-discover-vertex-fn (function (vertex))) (:on-back-edge-fn (function (edge:edge))) (:on-forward-or-cross-edge-fn (function (edge:edge))) (:on-vertex-finished-fn (function (vertex)))))
:root-vertexThe starting vertex. If root vertex is nil, all vertices will be searched, otherwise, vertices that are not connected via the out edges of the root vertex will not be visited.:process-all-verticesIf:root-vertexis nil, this option does not apply. Otherwise, setting this to truu will guarantee all vertices are visited bydepth-first-searcheven if they are not connected to the:root-vertex.:max-deptha fixnum indicating how many times the search will recur before it stops searching.:on-start-vertex-fnThis function is applied to each root vertex chosen as the starting vertex for the search.:on-tree-edge-fna function applied to each edge, the first time it is visited.:on-discover-vertex-fna function applied to a vertex, each time it is visited.:on-back-edge-fna function applied to an edge if it is visited whilst the search is looking at out-edges of this edge already. This can be used to detect loops.:on-forward-or-cross-edge-fna function applied to an edge when it is visited, but the algorithm has already searched it and all of it'sout-edges.:on-vertex-finisheda function applied to an edge when all of its out-edges have been searched.
Connected Components Algorithms
Strongly Connected Components Algorithm
Find the sets of strongly connected vertices in a directed graph.
(strongly-connected-components directed-graph)
Result is (values hash-table count), where:
hash-tablekeys are vertices and the values are positive fixnums that indicate which strongly-connected group each vertex is in. Each unique fixnum value is a set of one or more strongly connected vertices.countis the number of of groups.
Also see utilities for convience functions related to this algorithm.
Weakly Connected Components Algorithm
Find the sets of weakly connected vertices in a directed graph.
(weakly-connected-components directed-graph)
Result is (values hash-table count), where:
hash-tablekeys are vertices and the values are positive fixnums that indicate which strongly-connected group each vertex is in. Each unique fixnum value is a set of one or more weakly connected vertices.countis the number of of groups.
Also see utilities for convience functions related to this algorithm.
Connected Components Algorithm Utilities
conneced-components->graphs
Create a vector containing sub-graphs for given of connected components.
source-graph: The original graph a connected components algorithm was applied tocomponents: A hash-table where each key is a vertex and each value is a fixnum representing which group the vertex is in.component-count: The number of unique groups in components.
condensate-vertices
(condensate-vertices graph components-fn)
graphshould be a directed graphcomponents-fnshould be a(function (graph) (values hash-table fixnum &optional))such asstrongly-connected-componentsorweakly-connected-components.
This algorithm condensates either a graphs strongly or weakly connected sets of vertices into a new graph, where each vertex is a graph containing the condensed vertices and their connected edges.
Graph Partition Algorithms
Kerninghan-Lin Algorithm
The Kernighan-Lin Algorithm will split a graph into two partitions. It aims to minimize the cost of the edges between the two partitions in order to find two partitions that are mostly unconnected.
(kernighan-lin-partition undirected-graph iterations edge-cost-fn)
graph: an undirected graphiterations: A fixnum that indicates the number of times to run the alforithm.edge-cost-fn: A(function (edge) (values fixnum &optional))that calculates the cost of traversing a given edge.
History and inspiration
This library has largely been inspired by the library QuikGraph, with many ideas and algorithms ripped from it wholesale.
I wrote this library because I could not find any graph libraries that I loved in the Common Lisp ecosytem and when I have time, I enjoy solving Project Euler problems, which are often sanely modelled in graphs.