This library contains graph datastructures and algorithms.
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:
The main API for building and traversing graphs.
A collection of useful algorithms that can be used against graphs that implement the API.
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
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.
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:
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.)
An edge is a connection between two vertices. Edges may be directed or undirected, also known as arcs or lines.
If a graph allows more than one edge between the same pair of vertices, it is a multi-graph
A graph that allows more than one edge between the same pair of vertices.
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.
A directed acyclic graph (DAG) is a Directed Graph where no edges form any cycles or loops.
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.
Vertices are weakly connected if there is any path between them, ignoring the direction of edges.
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.
The clique with the maximum number of vertices in a graph.
A clique that is not sub-graph of any other clique.
An undirected graph in which every vertex is connected to every other vertex by a single edge.
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.
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 &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 &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 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 &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 source target)
Create an edge that can be added to graphs between two vertices.
These are functions and methods for modifying the edges and vertices in a graph.
(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 graph &rest vertices)
Add a set of vertices to a graph.
(add-edge graph edge)
Add an edge to a graph.
(add-edges graph &rest edges)
Add a set of edges to a graph.
(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 graph vertex1 vertex2)
Create and add an edge to a graph between two vertices.
(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 graph vertex)
Remove a vertex and all connecting edges from a graph.
(remove-edge graph vertex)
Remove an edge between two vertices.
(remove-edge-between graph vertex1 vertex2)
Remove any edges in a graph between two vertices.
(is-directed graph)
Return t if it's a directed graph.
(has-vertex graph vertex)
Return t if the vertex is in a graph.
(has-edge graph edge)
Return t if the edge is in a graph.
(has-edge-between graph vertex1 vertex2)
Return t if there is at least one edge between vertex1 and vertex2.
(vertex-equals graph vertex1 vertex2)
Use a graph's equality function to check if any pair of vertices are equal.
These are functions and methods that access the edges and vertices of a graph.
(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 graph fn)
Apply the function fn to all vertices in a graph.
fnshould be like(lambda (vertex) ...)
(for-roots graph fn)
Apply the function fn to all vertices without in-edges a directed graph.
fnshould be like(lambda (vertex) ...)
(for-edges graph fn)
Apply the function fn to all edges in a graph.
fnshould be like(lambda (edge) ...)
(adjacent-edges graph vertex)
Return a list of the edges connected to a vertex in an undirected graph.
(out-edges graph vertex)
Return a list of the outbound edges of a vertex in a directed graph.
(in-edges graph vertex)
Return a list of the inbound edges of a vertex in a directed graph.
(vertices graph)
Return a list of all vertices in a graph.
(roots graph)
Return a list of all vertices with no inbound edges in a directed graph.
(edges graph)
Return a list of all edges in a graph.
(vertex-count graph)
Return the number of vertices in a graph.
(edge-count graph)
Return the number of edge in a graph.
(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.
Functions to access slots in edge objects.
(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)
Returns the target vertex of an edge.
- Edges in an undirected graph still have a source and target value.
These functions and macros are for building a graph without using the graph as a parameter.
(with-graph* (graph) &body body)
A macro to set the dynamic variable *graph*, which is used by the dynamic graph building functions.
(add-vertex* vertex)
Add a vertex to *graph*.
(add-edge* edge)
Add an edge to *graph*.
(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* &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))Helpful utilities.
(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 graph1 graph2)
Check if two graphs have the same set of edges and vertices.
Conditions that can be thrown by this library.
(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.
This library provides a collection of popular graph-traversing algorithms for your convenience.
This is a combinatorial optimization method to solve the assignment problem: efficiently matching agents to tasks to achieve the minimum total cost and ensuring each pair is assigned.
(function (hungarian-assignment
(costs (array fixnum 2))))costsis a two-dimensional array offixnumwhere rows and columns represent agents and tasks, and each cell is the cost for the particular combination of agent and task.
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.
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.
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.
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.
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.
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 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.
Kernighan-Lin is an algorithm that divides a graph into roughly two equal parts (bipartitioning) to minimize the number of edges between them.
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.
A topological sort is an ordering of vertices of a Directed Acyclic Graph that guarantees that prerequisites always appear first.
(topological-sort graph)error will be thrown if any cycles are found.
Otherwise result is an ordered array consisting of all vertices in the graph.
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.