What is the best way to edit igraph C functions and call them into R?

We are trying to modify certain igraph functions in R software and have a few brief questions regarding that.

  1. How is igraph calling in underlying C functions?
  2. If we were able to modify the C files and save them to our local disc, what would be the best way to call them into R?

Specifically, we are attempting the following modification of the cluster_fast_greedy() algorithm:

  • To input a user-specified matrix \mathbf{P} alongside the already existing input adjacency matrix \mathbf{A}.
  • Instead of maintaining the vector \mathbf{a} and updating it at every iteration, we need to maintain a matrix \mathbf{W} (depending on \mathbf{P} ) and update this matrix at every iteration.
  • For updating the array that stores the iterative changes in modularity values, we want to replace a_{i}a_{j} by W_{ij} in line 728 of the code in this link

We will tremendously appreciate any help possible in this regard.

Let’s link your earlier question for context and completeness:

(Probably someone else should answer this in detail.)

1 Like

Let’s start from the very beginning. igraph is primarily a library written in C; the R version of igraph is mostly a wrapper around the C functions, extended with convenience features that make it integrate better with the R environment (e.g., converting from 0-based indices common in C to 1-based indices that are used in R; adding attribute handling to graphs etc). So, if you look for the source code of the cluster_fast_greedy() function in R using trace(), you will only find a small wrapper function that takes the arguments and forwards them to a mysterious C_R_igraph_community_fastgreedy() function using .Call() from R.

You will not find such a function in the source tarball of the R interface because the C_ prefix is added dynamically by R; however, you can find a table of all R_* functions in init.c in the source code of the R interface, here:

Then you need to look for the source of R_igraph_community_fastgreedy() to see where the rabbit hole leads. It leads to rinterface.c, which is mostly a generated file that contains functions that convert R expressions (of type SEXP in C) to the corresponding C data types, and then call a function in the C core of igraph. In the case of cluster_fast_greedy, this looks like this:

You can see that the function eventually leads to igraph_community_fastgreedy(), at which you leave R-land and enter C-land. From this point you are in the C core of igraph, which knows nothing about R itself. The source code of the C core is vendored within the R package; in case of igraph_community_fastgreedy(), it is in fast_community.c, and pretty much the entire file is the C implementation of the algorithm:

If you want to modify this function, it is going to be a tedious process as you need to be familiar with not only R but also the internals of the C core of igraph; basically, you are essentially editing the C library (and maybe also updating the wrapper code between R and C in the process if you add new arguments to the function in the C core). The easiest is probably to check out the master (not the dev) branch of the R-igraph repo on Github:

git clone https://github.com/igraph/rigraph.git
git checkout master

Note that you need to check out the master branch explicitly because that one contains the code that is identical to the submitted CRAN package; the default branch is dev, which contains a makefile that generates rinterface.c and many other files in the CRAN package using a set of scripts written in Python. So, use the master branch unless you want to go one more level deeper :slight_smile:

In the master branch, you can then dive into the source code of src/fast_modularity.c, and modify what you need to modify. If you tweak the function signature (add more arguments etc), you also need to update the declaration in src/interface/igraph_community.h, and then update the R-to-C wrapper in src/rinterface.c and the pure R counterpart of the function in R/community.R to expose these parameters to R. Then, you need to do the usual R CMD install --build dance to compile and install the modified package locally.

Let me know if you need more details about the process.

2 Likes

Thank you very much for the detailed answer. Let me take the time to carefully go through it and reply.

Hello, I read through your reply. I knew that I had to edit in the fast_community.c file but was completely unaware of the rinterface.c file. I did check the trace() function first and my understanding was whatever C functionalities were being invoked were in the two .Call() functions. From Hadley Wickham’s adv-r.had.co.nz/C-interface.html I knew that everything had to be SEXP. This now means to add an extra input I have to add SEXP arguments into init.c#L85..
A question for now would be that in the fast_community.c the vector \mathbf{a} is declared as a VECTOR which presumable is custom defined in igraph_vector_pmt.h. If I want to replace \mathbf{a} by a matrix \mathbf{W} do I work with matrix types declared in src/include/igraph_matrix_pmt.h or is that to be custom defined too? Thank you, I surely will be having many more questions. (I can write C codes, atleast simpler ones, but here I face the difficulty of understanding what each pointer is doing.)
P.S. It only lets me post two links at most, so a couple links are posted as plain text.

You can work with the matrix types declared in igraph_matrix_pmt.h; the C core of igraph has support for dense matrices consisting of doubles, integers and booleans so I’m pretty sure it covers most corners if you need dense matrices.

As you surely know by now, R_igraph_community_fastgreedy() will receive parameters of type SEXP, each representing one R expression that was passed to the function call. You need to convert the SEXP into an igraph_matrix_t to let igraph’s C core work with it. This is done with the function named R_SEXP_to_matrix, which creates an igraph matrix that is a view into the data inside the R matrix. Make sure you do not modify the igraph matrix created this way in your own function.

There are also other conversion functions; the names for all of them start with R_SEXP_to_..., so if you need to convert a SEXP into some core C igraph data type, these are the first candidates to look at before rolling your own solution.

1 Like