---
title: "Introducing Graphettes"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Introducing Graphettes}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
bibliography: bibliography.bib  
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

```{r setup}
library(igraph)
library(graphonmix)
library(ggplot2)
```


Graphettes are a graph generation methodology proposed in [@wijesinghe2026flowette]. A graphette has 3 components: a graphon $W$, a sequence $\{\rho_n\}_n$ and a graph edit function $f$. We can generate dense or sparse graphs from a graphette. On the sparse graph front, we can generate diverse sparse graphs, such as ring-added graphs that have bounded degree or graphs with large hubs like this social networks. We can generate trees as well. Let's look at some examples.

# Dense graphs from graphons and graphettes
Let's start with a stochastic block model graphon. 

```{r}
mat <- matrix(c(0.9, 0.01, 0.02,
                0.01, 0.8, 0.03,
                0.02, 0.03, 0.7), nrow = 3, byrow = TRUE)
W <- sbm_graphon(mat, 100)
plot_graphon(W) +  coord_fixed(ratio = 1) 
```

## Using the graphon

Without using graphettes we can sample from this graphon as follows:

```{r}
gr <- sample_graphon(W, 100)
plot(gr, vertex.size = 3, vertex.label = NA, main = "Graphon sampling")
```

## Using the graphette

To generate dense graphs from the graphette we set $\rho_n = 1$ for all $n$ and the graph edit function $f$ to the identity function. In the function call *sample_graphette* we set 
$\rho_n$ = NULL and graph_edit_function = NULL. That way, we get are actually sampling from the graphon $W$ without sparsifying it.  


```{r}
gr1 <- sample_graphette(W, n= 100)
plot(gr1, vertex.size = 3, vertex.label = NA, main = "Graphette sampling")


```

As you can see, in this instance sampling from the graphon or the graphette gives similar graphs. 

# Sparse graphs using $\rho_n$
Consider the same SBM graphon. We can sparsify it by considering $W_n = \rho_n W$ where $\rho_n \to 0$. And then we sample a graph $G_n$ with $n$ nodes from $W_n$. As $n \to \infty$ the edge density goes to zero making this graph sequence sparse. Let's see how this works. 

## Using the standard graphon and $\rho_n$ separately

```{r}

rho_n <- function(n) exp( -((n-100)/50 ))


W250 <- rho_n(250)*W
W300 <- rho_n(300)*W

gr1 <- sample_graphon(W, 100)
gr2 <- sample_graphon(W250, 250)
gr3 <- sample_graphon(W300, 300)


g1 <- plot_graphon(W) +  coord_fixed(ratio = 1) 
g2 <- plot_graphon(W250) +  coord_fixed(ratio = 1) 
g3 <- plot_graphon(W300) + coord_fixed(ratio = 1) 

gridExtra::grid.arrange(g1, g2, g3, nrow = 1)
```

The three graphons are shown above and we see the colours get greyed out as $\rho_n$ goes to zero. Let's plot the graphs sampled from the two graphons. 

```{r}
par(mfrow = c(1,3))
plot(gr1, vertex.size = 3, vertex.label = NA, main = "From W")
plot(gr2, vertex.size = 3, vertex.label = NA, main = "From W250")
plot(gr3, vertex.size = 3, vertex.label = NA, main = "From W300")
par(mfrow = c(1,1)) 

```

We see the graphs getting sparser. 

## Using the function *sample_sparse_graphon*

The function *sample_sparse_graphon* has a function parameter that can be used for $\rho_n$. 


```{r}
gr4 <- sample_sparse_graphon(W, rho_n, n = 250)
gr5 <- sample_sparse_graphon(W, rho_n, n = 300)

par(mfrow = c(1,2))
plot(gr4, vertex.size = 3, vertex.label = NA, main = "n = 250")
plot(gr5, vertex.size = 3, vertex.label = NA, main = "n = 300")
par(mfrow = c(1,1))
```
## Using the graphette

We can use the *sample_graphette* to get the same functionality by setting *graph_edit_f = $NULL. 

```{r}
gr6 <- sample_graphette(W, rho_n, n = 250)
gr7 <- sample_graphette(W, rho_n, n = 300)


par(mfrow = c(1,2))
plot(gr6, vertex.size = 3, vertex.label = NA, main = "n = 250")
plot(gr7, vertex.size = 3, vertex.label = NA, main = "n = 300")
par(mfrow = c(1,1))

```

# Sparse graphs using $\rho_n$ and the graph edit function
We have the functionality to add stars, add rings and remove cycles using the graph edit function. 

## Adding stars
The functions *star_f1*, *star_f2*, *star_f3*, *star_f4* and *star_f5* can be used to add stars. While there are slight differences, all of these functions use a Poisson process to add additional connections to each node. The parameter $t_or_p$ controls the intensity of the Poisson process. 

```{r}
gr8 <- sample_graphette(W, rho_n, "star_f1", n = 250, t_or_p = 2)
gr9 <- sample_graphette(W, rho_n, "star_f1", n = 300, t_or_p = 2)

par(mfrow = c(1,2))
plot(gr8, vertex.size = 3, vertex.label = NA, main = "n = 250")
plot(gr9, vertex.size = 3, vertex.label = NA, main = "n = 300")
par(mfrow = c(1,1))
```
In the above example $\rho_n$ goes to 0 very fast. Let us use something a bit different

```{r}
rho2 <- function(n)10/n
gr10 <- sample_graphette(W, rho2, "star_f1", n = 250, t_or_p = 2)
gr11 <- sample_graphette(W, rho2, "star_f1", n = 300, t_or_p = 2)

par(mfrow = c(1,2))
plot(gr10, vertex.size = 3, vertex.label = NA, main = "n = 250")
plot(gr11, vertex.size = 3, vertex.label = NA, main = "n = 300")
par(mfrow = c(1,1))
```
## Adding rings

To add rings to the graph, use *add_rings* as the graph edit function. The parameter *t_or_p* gives the proportion of nodes to which rings are added. The default ring size is 5 or 6, but this can be changed. 

```{r}
gr <- sample_graphette(W, rho_n, "add_rings", n = 100, t_or_p = 0.5, ring_sizes = c(10:15))

plot(gr, vertex.size = 3, vertex.label = NA)

```

## Removing cycles

To remove cycles from a graph, use *remove_cycles* as the graph edit function. 

```{r}
gr <- sample_graphette(W, rho_n, "remove_cycles", n = 200)

plot(gr, vertex.size = 3, vertex.label = NA)

```

## References
