Contributions to IGraph/M are most welcome!
IGraph/M serves both to expose the igraph C library to the Wolfram Language, as well as to provide independent graph theory and network analysis functionality.
igraph is a large graph library with diverse functionality. IGraph/M primarily focuses on exposing those pieces of igraph functionality which are not already available in Mathematica. Not all igraph functions are included. However, the main framework is there, and adding new functions is relatively quick and easy.
If you are interested in extending IGraph/M, send me an email to get technical guidance. IGraph/M uses the LTemplate package to simplify writing LibraryLink code, and acts as a driver for LTemplate development. I recommend starting by reading the LTemplate tutorial.
Help is also very welcome with writing or editing documentation, adding examples to the documentation, and writing unit tests.
The following tools are required for building the igraph C library: a C++ compiler with C++11 and C99 support, cmake
, flex
, bison
. See the installation instructions for the basic steps.
On Windows, we use MSVC.
In this guide, I will use $HOME/local
as the installation directory for the various needed libraries. You may want to use a different location for your system.
All igraph dependencies that are required for features that IGraph/M uses are already included with igraph.
On OS X, set the following environment variable before compiling the libraries:
export MACOSX_DEPLOYMENT_TARGET=10.9
Additional options to pass to CMake:
- It is recommended to use link-time optimization to achieve good performance, thus use
-DIGRAPH_ENABLE_LTO=ON
. - We do not need GraphML support and want to avoid a dependency on libxml2, so use
-DIGRAPH_GRAPHML_SUPPORT=OFF
. - On Linux and macOS, use
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
, as we will eventually be creating a shared library. - On Windows, use
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded
, as this uses the/MT
option, just like Mathematica'sCreateLibrary
.
When compiling for release, target the appropriate macOS version:
export MACOSX_DEPLOYMENT_TARGET=10.9
On Linux, make sure that _GLIBCXX_USE_CXX11_ABI=0
is defined for broader compatibility. All dependencies, such as LEMON, should also be compiled with the same setting.
For CMake, set -DIGRAPH_OPENMP_SUPPORT=OFF
to disable OpenMP support, as it only provides a modest improvement for PageRank.
Follow the installation guide of LEMON: http://lemon.cs.elte.hu/trac/lemon/wiki/InstallGuide
When running cmake
, use the option -DCMAKE_INSTALL_PREFIX:PATH=$HOME/local
to set the correct installation location.
An example command that also disables unneeded dependencies is
cmake -DCMAKE_INSTALL_PREFIX=$HOME/local -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=True -DLEMON_ENABLE_GLPK=False -DLEMON_ENABLE_COIN=False -DLEMON_ENABLE_ILOG=False -DLEMON_ENABLE_SOPLEX=False ..
When finished compiling, install it with make install
.
When compiling for release, edit CMakeLists.txt
and set: CMAKE_MINIMUM_REQUIRED(VERSION 3.15)
. This will allow DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded
to take effect on Windows. Optionally, set -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON
for better performance.
To compile IGraph/M, you will need:
- A C++ compiler with C++11 support. On Linux, use GCC 4.8 or later (GCC 4.8.5 is known to work). On macOS, use the command line tools of Xcode 6 or later.
- The LTemplate Mathematica package. Please download and install it.
- git, for cloning the repository.
Then follow these steps:
- Clone the IGraphM repository and check out the
master
branch (do not use any other branch). - Edit
BuildSettings.m
and set the path to your igraph and LEMON installation, where necessary. The available options are the same as for CreateLibrary. - Append the repository's root directory (i.e. the same directory where this
README.md
file is found) to Mathematica's$Path
. At the same time, ensure that you do not have the IGraph/M paclet installed. If you do, remove it withPacletUninstall["IGraphM"]
before proceeding. You can check which version of IGraph/M gets loaded on your system by evaluatingFindFile["IGraphM`"]
. - Load the package with
<<IGraphM`
. It should automatically be compiled. It can also be recompiled manually usingIGraphM`Developer`Recompile[]
.
If you decide to join IGraph/M development, please contact me for guidance. The basics are covered below.
IGraph/M uses the LTemplate package to interface with C++. First please go through the LTemplate tutorial.
To see the basic structure of a function, let us look at how a simple one such as IGConnectedQ
is implemented. On the C++ side we add a new member function to the IG
class:
bool connectedQ(bool strong) const {
igraph_bool_t res;
igCheck(igraph_is_connected(&graph, &res, strong ? IGRAPH_STRONG : IGRAPH_WEAK));
return res;
}
The igCheck
function must wrap any igraph function that returns error codes. It ensures that the error is passed on to Mathematica and reported appropriately.
On the Mathematica side we add the function to the library template in IGraphM.m
as
LFun["connectedQ", {True|False (* strongly connected *)}, True|False]
And finally we write a Mathematica function that calls this member function on an IG
object. It is found in Connectivity.m
.
IGConnectedQ[g_?igGraphQ] :=
catch@Block[{ig = igMake[g]},
check@ig@"connectedQ"[True]
]
The pattern g_?igGraphQ
will only match Graph
objects compatible with igraph. igMake
will convert a Mathematica graph to an IG
object. Calls to IG
member functions must be wrapped in check
, which will catch any library errors and Throw
them as exceptions. catch
is always used at the outermost level to catch and handle these exceptions.
igMake
is able to handle and convert to igraph format any kind of Mathematica graph for which igGraphQ
returns True
. It also transfers edge weights to igraph.
When weights are not required, use igMakeUnweighted
for better performance. igMakeUnweighted
could be used for IGConnectedQ
.
igMake
will automatically transfer edge weights to the IG
object. To pass these weights to an igraph function as an igraph_vector_t *
, use passWeights()
. See the betweenness
member function for a simple example. passWeights
returns NULL
for unweighted graphs.
Let us look at a simple function that creates graphs, such as IGGraphAtlas
, defined in DeterministicGenerators.m
. The C++ member function is
void graphAtlas(mint n) {
destroy();
igConstructorCheck(igraph_atlas(&graph, n));
}
On the C++ side, the graph creator function must first destroy()
the igraph graph that is currently stored in the IG
object. IG
objects always store some graph, and are initialized with an empty one.
Now we are ready to call the igraph function that creates the graph (igraph_atlas
).
Instead of igCheck
we must use igConstructorCheck
to handle igraph error codes. This function ensures that the graph is not left in an inconsistent state.
On the Mathematica side, we write
IGGraphAtlas[n_?Internal`NonNegativeMachineIntegerQ, opt : OptionsPattern[Graph]] :=
catch@Block[{ig = igMakeEmpty[]},
check@ig@"graphAtlas"[n];
applyGraphOpt[opt]@igToGraph[ig]
]
Since the IG
object is not created from an existing graph, we use igMakeEmpty[]
to make an empty one. igMakeEmpty[]
is equivalent to Make["IG"]
, a construct you will be familiar with from the LTemplate tutorial.
igToGraph
converts an IG
object back to a Mathematica graph.
Most IGraph/M functions that create graphs take all standard Graph
options such as GraphLayout
, VertexStyle
, etc. These can be applied using applyGraphOpt
, as in the example above.
Functions that would otherwise be free-standing (not member functions) should go in the IGlobal
class.
IGraph/M's tests rely on the MicroTest utility package. Please install this first.
To run the current tests, evaluate the cells in Tests/RunTests.nb
one by one. A summary is given at the end indicating the number of failures. Failures are also reported in red text.
To add more tests, edit Tests.m
. Unlike the rest of the IGraph/M sources, it is recommended to edit this file with the Mathematica Front End.
The basic format of a test is MT[expression, expected]
. For example,
MT[
1+1,
2
]
If evaluation generates messages, the test will fail. To specify expected messages, use
MT[
1/0,
ComplexInfinity,
{Power::infy}
]
An MT
expression is normally inert, and does not evaluate. To evaluate a test, wrap it in MTRun
. To evaluate all tests in Tests.m
, MTRun@Get["Tests.m"]
is used.
The Tests/MakeTests.nb
notebook provides an easy way to generate tests that can be pasted into Tests.m
.