# Transformations Benchmark

## Random Graphs Generation

To evaluate rsppfp package, several examples were benchmarked. This is run using 27 randomly generated graphs, which result from the combination of the following values:

- A set of nodes of sizes: `size(N) = {100, 300, 500}`
- The number of forbidden paths varying according to `size(F) = {50, 250, 500}`
- A varying densite of `size(d) = {0.1, 0.5, 0.9}`

A total of 27 graphs were generated using igraph’s `erdos.renyi.game(...)`, and the forbidden paths were constructed with their `random_walk(…)` function; results were limited to prevent walks of less than three nodes.

    # Checks if the path is part of any row of the forbidden subpaths.
    # Returns the logical comparison of the evaluation.
    already_exists <- function(fp, path) {
      return( any(
        apply(fp, 1, function(x) str_detect(paste(x, collapse=","), paste(path, collapse=",")))
      ))
    }

    # Checks if a forbidden paths contains a subpath of another forbidden path.
    # Returns the logical result of the comparison.
    has_subpath <- function(fp, path) {
      # If the length if 3, evaluate as-is
      if(length(path) == 3) {
        return(already_exists(fp, path))
      }
      # Otherwise
      else {
        # We need to generate subpaths of length three
        for(i in 1:(length(path) - 2)) {
          # Check if the value is a subpath. If it is, return immediately
          if( already_exists(fp, path[i:(i+2)]) ) return(TRUE)
        }

        # If we got here, return false
        return(FALSE)
      }
    }

    # Creates random graphs using a specific seed.
    # seed <- 1111
    get_random_graphs <- function(seed, directory) {
      # Set a random seed
      set.seed(seed)

      # Prepare values
      nodesNumbers <- c(100, 300, 500)
      densities <- c(0.1, 0.5, 0.9)
      fpathsNumber <- c(50, 250, 500)

      # Create the dataframe
      graphsInfo <- data.frame(
        nCount = numeric(),
        nCountReal = numeric(),
        aCount = numeric(),
        density = numeric(),
        fCountTheoric = numeric(),
        fCountReal = numeric(),
        gfile = character(),
        fFile = character(),
        stringsAsFactors = FALSE
      )

      # Loop through the node count
      for(nodeCount in nodesNumbers) {

        # Loop through the density
        for(d in densities) {
          # Get the arc count
          arcsNumber <- (nodeCount ^ 2) * d

          # Create a graph
          graphI <- erdos.renyi.game(nodeCount, arcsNumber, directed = TRUE, type = "gnm")

          # Remove nodes without edges
          graphI <- delete_vertices(graphI, V(graphI)[ degree(graphI, v = V(graphI), mode = "total") == 0])
          giDF <- as.data.frame(get.edgelist(graphI)) %>% `colnames<-`(c("from", "to"))

          # Loop on the number of forbidden paths
          for(fpCount in fpathsNumber) {

            # Get the starting nodes for forbidden paths
            randomStartNodes <- sample(x = V(graphI), size = fpCount, replace = TRUE)
            fpDF <- data.frame(matrix(ncol = 6, nrow = 0))

            # For each element there
            for(rsn in randomStartNodes) {

              # Get a random number of steps
              rSteps <- sample(3:6,1)

              # Create a random walk through the graph
              rWalk <- random_walk(graph = graphI, start = rsn, steps = rSteps,
                                   mode = "out", stuck = "return") %>% as.vector()

              # If it is at least 3 nodes long and has no subpaths
              if(length(rWalk) >= 3 & !has_subpath(fpDF, rWalk) ) {
                # Then add it to the list
                fpDF[nrow(fpDF) + 1, ] <- structure("length<-"(rWalk, 6), class = class(rWalk))
              }
            }

            # Record the files
            gFileName <- paste0(directory, "/", nodeCount, "Graph_", (d * 100), "pDense_", fpCount, "fp_Graph.csv",
                                collapse = "")
            fFileName <- paste0(directory, "/", nodeCount, "Graph_", (d * 100), "pDense_", fpCount, "fp_Paths.csv",
                                collapse = "")
            write.csv(file = gFileName, x = giDF, row.names = FALSE)
            write.table(fpDF, fFileName, na = "", row.names = FALSE, col.names = FALSE, append = TRUE, sep = ",")

            # Append to the dataframe
            graphsInfo[nrow(graphsInfo) + 1, ] <-
              list(nodeCount, length(V(graphI)), arcsNumber, d, fpCount, nrow(fpDF), gFileName, fFileName)
          }
        } # End densities loop
      } # End arc count loop

      # Return the values
      return(graphsInfo)
    }

## Benchmark Results

Figure 1 shows the results for digraphs of each density. This was executed on a computer with an Intel I7-4790 CPU at 3.6Ghz, with 4GB of RAM, using Linux Ubuntu 16.04 as the operating system. Three cores out of four were used in all of the transformations.

As can be seen, in all situations the more general algorithm (Hsu’s) takes longer to complete, though this difference increases with the number of forbidden paths and density. It is worth mentioning that this is expected behavior, as the transformation logic iterates on the forbidden paths `F` –each one needs to be individually evaluated to add new nodes and arcs to the graph. Because of its logic, Hsu’s backward construction always produces a smaller `gStar` with less nodes and arcs, while Villeneuve’s algorithm is slightly faster; this difference is noticeable for larger values of `F`.