Graph

Graph theory algorithms, parameterized- and random graphs.

Graph

Raku package for (discrete mathematics) graph data structures and algorithms.

For a quick introduction see the video "Graph demo in Raku (teaser)", [AAv1], (5 min.)

Remark: This package is not for drawing and rendering images. It is for the abstract data structure graph.

Installation

From Zef ecosystem:

zef install Graph

From GitHub:

zef install https://github.com/antononcube/Raku-Graph.git

Design and implementation details

Motivation

  • Needless to say, Graph theory is huge.

    • But certain algorithms like path and cycle finding are relatively easy to implement and are fundamental both mathematics-wise and computer-science-wise.

  • Having fast shortest path finding algorithms in graphs should be quite a booster for geography related projects.

Design

  • The central entity is the Graph class.

  • Graph is as generic as possible.

    • Meaning it is for directed graphs.

    • Undirected graphs are represented as directed graphs.

      • I.e. with twice as many edges than necessary.

    • The current graph representation is with hash-of-hashes, (adjacency-map), that keeps from-to-weight relationships.

      • For example, $g.adjacency-map<1><2> gives the weight of the edge connecting vertex "1" to vertex "2".

    • The vertexes are only strings.

      • Not a "hard" design decision.

      • More general vertexes can be imitated (in the future) with vertex tags.

        • This is related to having (in Raku) sparse matrices with named rows and columns.

  • Since I know Mathematica / Wolfram Language (WL) very well, many of the method names and signatures are strongly influenced by the corresponding functions in WL.

    • I do not follow them too strictly, though.

    • One reason is that with Raku it is much easier to have and use named arguments than with WL.

Implementation

  • I was considering re-programming Perl5’s "Graph", [JHp1], into Raku, but it turned out it was easier to write the algorithms directly in Raku.

    • (To me at least...)

  • The classes creating special graphs, like, grid-graph, star-graph, etc., are sub-classes of Graph with files in the sub-directory "Graph".

    • See usage of such classes here.

Visualization

  • Visualizing the graphs (the objects of the class Graph) is very important.

    • Has to be done from the very beginning of the development.

      • (Again, for me at least...)

  • The class Graph has the methods dot, graphml, mermaid, and wl for representing the graphs for GraphViz, GraphML, Mermaid-JS, and Wolfram Language (WL) respectively.

  • Mermaid's graph nodes and edges arrangement algorithm can produce "unexpected" images for the standard, parameterized graphs.

    • Like, "grid graph", "cycle graph", etc.

Performance

  • So far, I have tested the path finding algorithms on "Graph" on small graphs and moderate size graphs.

    • The largest random graph I used had 1000 vertexes and 1000 edges.

    • Mathematica (aka Wolfram Language) can be 500 ÷ 10,000 faster.

    • I hope good heuristic functions for the "A* search" method would make find-shortest-path fast enough, for say country / continent route systems.

      • With the larger, 1,000-vertex random graphs finding paths with the method "a-star" is ≈50 faster than with the method "dijkstra".

  • Setting up comprehensive performance profiling and correctness testing is somewhat involved.

    • One main impediment is that in Raku one cannot expect and specify same random numbers between different sessions.

Usage examples

Here we create a dataset of edges:

my @edges =
        { from => '1', to => '5', weight => 1 },
        { from => '1', to => '7', weight => 1 },
        { from => '2', to => '3', weight => 1 },
        { from => '2', to => '4', weight => 1 },
        { from => '2', to => '6', weight => 1 },
        { from => '2', to => '7', weight => 1 },
        { from => '2', to => '8', weight => 1 },
        { from => '2', to => '10', weight => 1 },
        { from => '2', to => '12', weight => 1 },
        { from => '3', to => '4', weight => 1 },
        { from => '3', to => '8', weight => 1 },
        { from => '4', to => '9', weight => 1 },
        { from => '5', to => '12', weight => 1 },
        { from => '6', to => '7', weight => 1 },
        { from => '9', to => '10', weight => 1 },
        { from => '11', to => '12', weight => 1 };

@edges.elems;
# 16

Remark: If there is no weight key in the edge records the weight of the edge is taken to be 1.

Here we create a graph object with the edges dataset:

use Graph;

my $graph = Graph.new;
$graph.add-edges(@edges);
# Graph(vertexes => 12, edges => 16, directed => False)

Here are basic properties of the graph:

say 'edge count   : ', $graph.edge-count;
say 'vertex count : ', $graph.vertex-count;
say 'vertex list  : ', $graph.vertex-list;
# edge count   : 16
# vertex count : 12
# vertex list  : (1 10 11 12 2 3 4 5 6 7 8 9)

Here we display the graph using Mermaid-JS, (see also, [AAp1]):

$graph.mermaid(d=>'TD')

Here we find the shortest path between nodes "1" and "4":

say 'find-shortest-path : ', $graph.find-shortest-path('1', '4');
# find-shortest-path : [1 7 2 4]

Here we find all paths between "1" and "4", (and sort them by length and vertex names):

say 'find-path : ' , $graph.find-path('1', '4', count => Inf).sort({ $_.elems ~ ' ' ~ $_.join(' ') });
# find-path : ([1 7 2 4] [1 5 12 2 4] [1 7 2 3 4] [1 7 6 2 4] [1 5 12 2 3 4] [1 7 2 10 9 4] [1 7 2 8 3 4] [1 7 6 2 3 4] [1 5 12 2 10 9 4] [1 5 12 2 8 3 4] [1 7 6 2 10 9 4] [1 7 6 2 8 3 4])

Here we find a Hamiltonian path in the graph:

say 'find-hamiltonian-path : ' , $graph.find-hamiltonian-path();
# find-hamiltonian-path : [11 12 5 1 7 6 2 10 9 4 3 8]

Here we find a cycle:

say 'find-cycle : ' , $graph.find-cycle().sort({ $_.elems ~ ' ' ~ $_.join(' ') });
# find-cycle : ([10 2 4 9 10])

Here we find all cycles in the graph:

say 'find-cycle (all): ' , $graph.find-cycle(count => Inf).sort({ $_.elems ~ ' ' ~ $_.join(' ') });
# find-cycle (all): ([2 3 4 2] [2 3 8 2] [2 6 7 2] [10 2 4 9 10] [2 4 3 8 2] [1 5 12 2 7 1] [10 2 3 4 9 10] [1 5 12 2 6 7 1] [10 2 8 3 4 9 10])

Graph plotting

Graph formats

The class Graph has the following methods of graph visualization:

Visualizing via DOT format

In Jupyter notebooks with a Raku kernel graph visualization can be done with the method dot and its adverb ":svg".

First, install Graphviz.

On macOS the installation can be done with:

brew install graphviz

Here a wheel graph is made and its DOT format is converted into SVG format (rendered automatically in Jupyter notebooks):

use Graph::Wheel;
Graph::Wheel.new(12).dot(:svg)

For more details see notebook "DOT-visualizations.ipynb".

Visualizing via D3.js

In Jupyter notebooks with a Raku kernel graph visualization can be done with the function js-d3-graph-plot of the package "JavaScript::D3".

The visualizations with "JavaScript::D3" are very capricious. Currently they:

  • Do not work with JupyterLab, but only with the "classical" Jupyter notebook.

  • Work nicely with the Jupyter notebook plugin(s) of Visual Studio Code, but often require re-loading of the notebooks.

The points above were the main reasons to develop the DOT format visualization. Most of the documentation notebooks show the graphs using both "JavaScript::D3" and DOT-SVG.

CLI

The package provides the Command Line Interface (CLI) script graph-creation that can be used to create graphs and export them to different formats. For example:

graph-creation Grid --rows=3 --columns=2 --format=dot
# graph {
# "1_0" [pos="0,1!"];
# "1_1" [pos="1,1!"];
# "2_0" [pos="0,2!"];
# "0_0" [pos="0,0!"];
# "2_1" [pos="1,2!"];
# "0_1" [pos="1,0!"]
# "1_0" -- "2_0" 
# "2_0" -- "2_1" 
# "1_1" -- "2_1" 
# "1_0" -- "1_1" 
# "0_1" -- "1_1" 
# "0_0" -- "1_0" 
# "0_0" -- "0_1" 
# }

TODO

Main, core features

  • DONE Rename the attribute .adjacency-list to .adjacency-map

    • Makes it consistent with Math::SparseMatrix.

  • TODO Object methods

    • DONE Str and gist methods

    • DONE Deep copy

    • DONE Creation from another graph.

    • TODO Ingest vertexes and edges of another Graph object

    • TODO Comparison: eqv and ne

  • DONE Disjoint graphs

    • The graphs can be disjoint as long as the components have edges.

    • Related, the class Graph does supports "lone vertices."

      • They have empty adjacency values.

  • TODO Vertexes

    • DONE Vertex list

    • DONE Vertex count

    • DONE Vertex degree

    • DONE in-degree, edges-at

    • DONE out-degree, edges-from

    • DONE Delete vertex(es)

    • DONE Add vertex

    • DONE Has vertex

    • TODO Vertex tags support

  • TODO Edges

    • DONE Edge list

    • DONE Edge dataset

    • DONE Edge count

    • DONE Add edge

    • DONE Delete edge(s)

    • DONE Has edge

    • TODO Edge tags support

  • TODO Matrix representation

    • Sparse matrices are needed before "seriously" considering this.

    • Sparse matrices should be easy to create using the (already implemented) edge dataset.

    • DONE Adjacency matrix (dense)

    • TODO Adjacency matrix (sparse)

    • DONE Incidence matrix (dense)

    • TODO Incidence matrix (sparse)

Graph programming

  • TODO Depth first scan / traversal

    • Scan a graph in a depth-first order.

    • This is already implemented, it has to be properly refactored.

      • See Depth-First Search (DFS) named (private) methods.

  • TODO Breadth first scan / traversal

    • Scan a graph in a breadth-first order.

Paths, cycles, flows

  • DONE Shortest paths

    • DONE Find shortest path

    • DONE Find Hamiltonian paths

      • For both the whole graph or for a given pair of vertexes.

      • Algorithms:

        • Backtracking, method => 'backtracking')

        • Application Warnsdorf's rule for backtracking, :warnsdorf-rule

        • Angluin-Valiant (probabilistic), method => 'random'

  • TODO Flows

    • TODO Find maximum flow

    • TODO Find minimum cost flow

  • TODO Distances

    • DONE Graph distance

      • See shortest path.

    • DONE Graph distance matrix

      • Again, requires choosing a matrix Raku class or package.

        • Just using a dense matrix for now.

  • DONE Longest shortest paths

    • DONE Vertex eccentricity

    • DONE Graph radius

    • DONE Graph diameter

    • DONE Graph center

    • DONE Graph periphery

  • DONE Weakly connected component

  • DONE Strongly connected component

  • DONE Topological sort

    • Using Tarjan's algorithm

  • TODO Cycles and tours

    • DONE Find cycle

      • Just one cycle

      • All cycles

    • TODO Find shortest tour

    • TODO Find postman tour

      • DONE Eulerian and semi-Eulerian graphs

      • TODO General graphs

    • TODO Find Eulerian cycle

    • TODO Find Hamiltonian cycle

    • TODO Find cycle matrix

  • TODO Independent paths

    • DONE Find paths

    • TODO Find edge independent paths

    • TODO Find edge vertex paths

Matching, coloring

Operations

  • TODO Unary graph operations

    • DONE Reversed graph

    • DONE Complement graph

    • DONE Subgraph

      • For given vertices and/or edges.

    • DONE Neighborhood graph

      • For given vertices and/or edges.

    • DONE Make undirected

      • Can be implemented as Graph.new($g, :!directed).

      • But maybe it is more efficient to directly manipulate adjacency-map.

    • DONE Make directed

      • It is not just a flag change of $!directed.

      • Implement the methods: Whatever, "Acyclic", "Random".

    • TODO Edge contraction

    • TODO Vertex contraction

    • TODO Line graph

    • TODO Dual graph

  • TODO Binary graph operations

    • DONE Union of graphs

    • DONE Intersection of graphs

    • DONE Difference of graphs

    • DONE Disjoint union of graphs

    • TODO Product of graphs (AKA "box product")

      • TODO Cartesian

      • TODO Co-normal

      • TODO Lexicographic

      • TODO Normal

      • TODO Rooted

      • TODO Strong

      • TODO Tensor

Construction

CLI

  • TODO Come up with a meaningful CLI script functionalities exposure

    • One script or separate creation and graph-operations scripts?

      • At first just one script, graph-creation.

      • Later on, one script can be used for, say, both creation and verification.

    • What is the set of commands?

  • TODO CLI functionalities

    • DONE Graph creation

    • TODO Graph tests over graphs given with JSON files

    • DONE Produce the Graphviz DOT, WL, Mermaid, JSON representations

      • DONE For specified with parameters graphs

      • TODO For JSON files

  • TODO Understanding commands like "hamiltonian path" or "grid graph"

    • Not just "hamiltonian-path" and "grid-graph".

Tests

  • TODO Unit tests

    • DONE Sanity

    • DONE Undirected graphs

    • DONE Vertex removal

    • DONE Edge removal

    • DONE Bipartite graph check

    • TODO Directed graphs cycles

  • TODO Cross-verification with Wolfram Language (aka Mathematica)

    • DONE General workflow programming/setup

    • TODO Path finding

    • TODO Cycle finding

Documentation

  • DONE Basic usage over undirected graphs

  • TODO Basic usage over directed graphs

  • DONE Regular graphs creation (Grid, Wheel, etc.)

  • DONE Random graphs creation

  • DONE DOT language visualizations

References

Articles

[Wk1] Wikipedia entry, "Graph (discrete mathematics)".

[Wk2] Wikipedia entry, "Graph theory".

[Wk3] Wikipedia entry, "Glossary of graph theory".

[Wk4] Wikipedia entry, "List of graphs" (aka "Gallery of named graphs.")

[Wk5] Wikipedia entry, "Hamiltonian path".

[AA1] Anton Antonov, "Graph Neat Examples in Raku – Set 1", (2024), RakuForPrediction at WordPress.

[AA2] Anton Antonov, "Graph Neat Examples in Raku – Set 2", (2024), RakuForPrediction at WordPress.

[AA3] Anton Antonov, "Graph Neat Examples in Raku – Set 3", (2024), RakuForPrediction at WordPress.

[AA4] Anton Antonov, "Day 12 – Graphs in Raku", (2024), Raku Advent Calendar 2024.

Packages

[AAp1] Anton Antonov, WWW::MermaidInk Raku package, (2023), GitHub/antononcube.

[AAp2] Anton Antonov, Proc::ZMQed Raku package, (2022), GitHub/antononcube.

[AAp3] Anton Antonov, JavaScript:3 Raku package, (2022-2024), GitHub/antononcube.

[JHp1] Jarkko Hietaniemi, Graph Perl package, (1998-2014), MetaCPAN.

Videos

[AAv1] Anton Antonov, "Graph demo in Raku (teaser)", (2024), YouTube/@AAA4Prediction.

[AAv2] Anton Antonov, "Graph neat examples in Raku (Set 1)", (2024), YouTube/@AAA4Prediction.

[AAv3] Anton Antonov, "Raku RAG demo", (2024), YouTube/@AAA4Prediction.

[AAv4] Anton Antonov, "Sparse matrix neat examples in Raku", (2024), YouTube/@AAA4Prediction.

[AAv5] Anton Antonov, "Graph neat examples in Raku (Set 2)", (2024), YouTube/@AAA4Prediction.

[AAv6] Anton Antonov, "Graph neat examples in Raku (Set 3)", (2024), YouTube/@AAA4Prediction.

[AAv7] Anton Antonov, "Chess positions and knight's tours via graphs (in Raku)", (2024), YouTube/@AAA4Prediction.

[AAv8] Anton Antonov, "Graph neat examples in Raku (Set 4)", (2025), YouTube/@AAA4Prediction.

Graph v0.1.0

Graph theory algorithms, parameterized- and random graphs.

Authors

  • Anton Antonov

License

Artistic-2.0

Dependencies

BinaryHeap:ver<0.0.7>:auth<zef:dumarchie>:api<1>Data::TypeSystem:ver<0.1.6+>:auth<zef:antononcube>:api<1>

Test Dependencies

Provides

  • Graph
  • Graph::Bipartitish
  • Graph::Butterfly
  • Graph::Circulant
  • Graph::Classes
  • Graph::Complete
  • Graph::CompleteKaryTree
  • Graph::Componentish
  • Graph::Cycle
  • Graph::DistanceFindish
  • Graph::Distribution
  • Graph::Formatish
  • Graph::Grid
  • Graph::Harary
  • Graph::HexagonalGrid
  • Graph::HighlightProcessing
  • Graph::Hypercube
  • Graph::Indexed
  • Graph::KnightTour
  • Graph::Leaper
  • Graph::MinCuttish
  • Graph::Neighborhoodish
  • Graph::Nested
  • Graph::Path
  • Graph::Petersen
  • Graph::Random
  • Graph::Relation
  • Graph::Star
  • Graph::Tourish
  • Graph::TriangularGrid
  • Graph::Utilities
  • Graph::Wheel

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.