From 56660ef3592c53f70b139b6c6be1ae44046edfe9 Mon Sep 17 00:00:00 2001 From: melinavidoni Date: Mon, 4 Jun 2018 09:04:23 -0300 Subject: [PATCH] Adding tests and improving website. --- .travis.yml | 9 + DESCRIPTION | 6 +- NAMESPACE | 5 + NEWS.md | 14 +- R/hsu.R | 445 +++++++++--------- R/igraph.R | 4 +- R/parsers.R | 3 + R/villeneuve.R | 63 +-- README.Rmd | 2 + README.md | 1 + _pkgdown.yaml | 7 +- docs/LICENSE-text.html | 3 + docs/articles/benchmark.html | 3 + docs/articles/igraph.html | 33 +- .../figure-html/unnamed-chunk-4-1.png | Bin 23177 -> 26915 bytes docs/articles/index.html | 3 + docs/articles/references.html | 3 + docs/articles/rsppfp.html | 4 + docs/authors.html | 3 + docs/index.html | 4 + docs/news/index.html | 21 +- docs/reference/direct_graph.html | 3 + docs/reference/get_all_nodes.html | 3 + docs/reference/get_shortest_path.html | 9 +- docs/reference/index.html | 3 + docs/reference/modify_graph_hsu.html | 3 + docs/reference/modify_graph_vd.html | 4 + docs/reference/parse_vpath.html | 3 + docs/reference/rsppfp.html | 3 + tests/testthat/test-hsu.R | 36 +- tests/testthat/test-villeneuve.R | 44 +- vignettes/igraph.Rmd | 4 +- 32 files changed, 431 insertions(+), 320 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8d139ac..02074f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,12 @@ language: R sudo: false cache: packages + +r_packages: + - covr + +after_success: + - Rscript -e 'library(covr); codecov()' + +codecov: + token: b6939c0b-8d6c-4259-9974-3a2755ee8253 diff --git a/DESCRIPTION b/DESCRIPTION index 8b0c329..5c96d29 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -12,7 +12,8 @@ Depends: R (>= 3.4.0) Imports: dplyr, magrittr, foreach, - doParallel + doParallel, + igraph License: GPL-3 + file LICENSE Encoding: UTF-8 LazyData: true @@ -20,7 +21,8 @@ RoxygenNote: 6.0.1 Suggests: knitr, rmarkdown, testthat, - covr + covr, + ggplot2 VignetteBuilder: knitr URL: https://github.com/melvidoni/rsppfp BugReports: https://github.com/melvidoni/rsppfp/issues diff --git a/NAMESPACE b/NAMESPACE index 459de63..5859e9e 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,8 +6,13 @@ export(get_shortest_path) export(modify_graph_hsu) export(modify_graph_vd) export(parse_vpath) +importFrom(doParallel,registerDoParallel) +importFrom(foreach,"%do%") importFrom(foreach,"%dopar%") +importFrom(foreach,foreach) importFrom(igraph,E) importFrom(igraph,graph_from_data_frame) importFrom(igraph,shortest_paths) importFrom(magrittr,"%>%") +importFrom(parallel,makeCluster) +importFrom(parallel,stopCluster) diff --git a/NEWS.md b/NEWS.md index 37e8e67..baffe98 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,8 @@ -# rsppfp 2.0.0.0 - -* Added a `NEWS.md` file to track changes to the package. - - - +## RSPPFP 1.0.0.0 +First stable release of advanced implementation of R-SPPFP, released on June 2018. + +### Major Changes +First stable release of advanced implementation of R-SPPFP! Includes the following functions: + - Transformation algorithms (Villeneuve & Desaulniers, and Hsu's et al.). + - Basic parsing functions: graph-to-digraph, and vpath translation. + - Integration functions: equivalent nodes selection, and igraph's integration. \ No newline at end of file diff --git a/R/hsu.R b/R/hsu.R index 7889b14..38dcf59 100644 --- a/R/hsu.R +++ b/R/hsu.R @@ -30,6 +30,9 @@ #' #' @importFrom foreach %dopar% #' @importFrom magrittr %>% +#' @importFrom parallel makeCluster +#' @importFrom doParallel registerDoParallel +#' @importFrom parallel stopCluster #' #' #' @examples @@ -54,265 +57,271 @@ modify_graph_hsu <- function(g, f, cores = 1L) { g$from <- as.character(g$from) g$to <- as.character(g$to) f[f == ""] <- NA - + # Get the number of columns ncol <- ncol(g) - + # Set up the parallel cluster <- parallel::makeCluster(cores) doParallel::registerDoParallel(cluster) - + # Create the first output firstOutput <- foreach::foreach(startNode = unique(f[,1]), .combine = .comb, .multicombine = TRUE, - .export = ".get_arc_attributes") %dopar% { - # Create the new arcs - tempNewArcs <- g[0,] - tempDeletedArcs <- g[0,1:2] - tempFDelete <- f[0,] - - # Create a list of banned arcs and nodes - tempBannedArcs <- data.frame(from = as.character(), to = as.character(), stringsAsFactors = FALSE) - tempBannedNodes <- data.frame(node = as.character(), stringsAsFactors = FALSE) - - - # Subset all the paths that start with that node - subsetSN <- subset(f, f[,1] == startNode) - - # Get all the endings of paths starting with that node, transposed - endings <- t(sapply( - split(subsetSN, seq(nrow(subsetSN))), function(x) tail(x[!is.na(x)], 2) ) ) - - # For each ending - for(e in nrow(endings)) { - # Get all FP starting in "startNode" and ending in these pair - fps <- subsetSN[sapply(split(subsetSN, seq(nrow(subsetSN))), - function(x) tail(x[!is.na(x)], 2) == endings[e,])[1,] - ,] - - # SET 1, SUBSET 1 - # --------------------------------------------------------------------- - # If there are more than two paths here, it means the FPs on the subset - # are parte of set 1 subset 1 (same start, same end pair). - # Therefore, we remove it from the original list - if(nrow(fps) > 1) { - tempFDelete <- fps - - # For all paths - for(i in 1:nrow(fps)) { - - # Delete the first arc of the FP in the original graph - # g <- subset(g, from == fps[i,1] & to == fps[i,2]) - tempDeletedArcs[nrow(tempDeletedArcs) + 1,] <- c(fps[i,1], fps[i,2]) - - # Prepare a predecesor node - preNode <- fps[i,1] - - - # We are looping from the second to the penultimate item on the list. - # The last node is ignored because it is only temporal, - # So by ignoring it, we aim to reduce working time - # Create a flag to check if we continue adding - continue <- TRUE - j <- 2 - while(continue & j < (length(fps[i,]) - 1) ) { - # Create the new node name - nodeName <- paste0(as.character(fps[i,])[1:j], collapse = "|") - - # Add the link to the predecesor, with all the attributes - if(ncol > 2) { - tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName, .get_arc_attributes(g, fps[i, j-1], fps[i, j])) - } - # Or without the attributes - else tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName) - - - # Ban the next arc - tempBannedArcs[nrow(tempBannedArcs) + 1,] <- c(nodeName, fps[i, j + 1]) - - # And update the predecesor - preNode <- nodeName - - # SET 1 SUBGROUP 1 - # ---------------- - if(i != 1) { - # In this case, we also need to check if there is a node ending on the next node - newTo <- unique(tempNewArcs$to)[grepl(paste0("\\|", fps[i, j + 1], "$"), unique(tempNewArcs$to))] - - # If there is one - if(!identical(newTo, character(0))) { - # Write that arc as well, with its attributes - if(ncol > 2) { - tempNewArcs[nrow(tempNewArcs) + 1,] <- list(nodeName, newTo, - .get_arc_attributes(g, fps[i,j-1], fps[i,j])) - } - # Or without them - else tempNewArcs[nrow(tempNewArcs) + 1,] <- list(nodeName, newTo) - - # And mark that we stop looping here - continue <- FALSE - } - } - - j <- j + 1 - } - # Save the last node as banned - tempBannedNodes[nrow(tempBannedNodes) + 1,1] <- paste0(as.character(fps[i,][which(!is.na(fps[i,]))]), - collapse = "|") - - } # End loop of all paths - } # Endif - } - - # Return all the values - list(tempNewArcs, tempDeletedArcs, tempBannedArcs, tempBannedNodes, tempFDelete) - } - + .export = ".get_arc_attributes") %dopar% { + # Create the new arcs + tempNewArcs <- g[0,] + tempDeletedArcs <- g[0,1:2] + tempFDelete <- f[0,] + + # Create a list of banned arcs and nodes + tempBannedArcs <- data.frame(from = as.character(), to = as.character(), stringsAsFactors = FALSE) + tempBannedNodes <- data.frame(node = as.character(), stringsAsFactors = FALSE) + + + # Subset all the paths that start with that node + subsetSN <- subset(f, f[,1] == startNode) + + # Get all the endings of paths starting with that node, transposed + endings <- t(sapply( + split(subsetSN, seq(nrow(subsetSN))), function(x) tail(x[!is.na(x)], 2) ) ) + + # For each ending + for(e in nrow(endings)) { + # Get all FP starting in "startNode" and ending in these pair + fps <- subsetSN[sapply(split(subsetSN, seq(nrow(subsetSN))), + function(x) tail(x[!is.na(x)], 2) == endings[e,])[1,] + ,] + + # SET 1, SUBSET 1 + # --------------------------------------------------------------------- + # If there are more than two paths here, it means the FPs on the subset + # are parte of set 1 subset 1 (same start, same end pair). + # Therefore, we remove it from the original list + if(nrow(fps) > 1) { + tempFDelete <- fps + + # For all paths + for(i in 1:nrow(fps)) { + + # Delete the first arc of the FP in the original graph + # g <- subset(g, from == fps[i,1] & to == fps[i,2]) + tempDeletedArcs[nrow(tempDeletedArcs) + 1,] <- c(fps[i,1], fps[i,2]) + + # Prepare a predecesor node + preNode <- fps[i,1] + + + # We are looping from the second to the penultimate item on the list. + # The last node is ignored because it is only temporal, + # So by ignoring it, we aim to reduce working time + # Create a flag to check if we continue adding + continue <- TRUE + j <- 2 + while(continue & j < (length(fps[i,]) - 1) ) { + # Create the new node name + nodeName <- paste0(as.character(fps[i,])[1:j], collapse = "|") + + # Add the link to the predecesor, with all the attributes + if(ncol > 2) { + tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName, .get_arc_attributes(g, fps[i, j-1], fps[i, j])) + } + # Or without the attributes + else tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName) + + + # Ban the next arc + tempBannedArcs[nrow(tempBannedArcs) + 1,] <- c(nodeName, fps[i, j + 1]) + + # And update the predecesor + preNode <- nodeName + + # SET 1 SUBGROUP 1 + # ---------------- + if(i != 1) { + # In this case, we also need to check if there is a node ending on the next node + newTo <- unique(tempNewArcs$to)[grepl(paste0("\\|", fps[i, j + 1], "$"), unique(tempNewArcs$to))] + + # If there is one + if(!identical(newTo, character(0))) { + # Write that arc as well, with its attributes + if(ncol > 2) { + tempNewArcs[nrow(tempNewArcs) + 1,] <- list(nodeName, newTo, + .get_arc_attributes(g, fps[i,j-1], fps[i,j])) + } + # Or without them + else tempNewArcs[nrow(tempNewArcs) + 1,] <- list(nodeName, newTo) + + # And mark that we stop looping here + continue <- FALSE + } + } + + j <- j + 1 + } + # Save the last node as banned + tempBannedNodes[nrow(tempBannedNodes) + 1,1] <- paste0(as.character(fps[i,][which(!is.na(fps[i,]))]), + collapse = "|") + + } # End loop of all paths + } # Endif + } + + # Return all the values + list(tempNewArcs, tempDeletedArcs, tempBannedArcs, tempBannedNodes, tempFDelete) + } + # Delete forbidden paths f <- dplyr::setdiff(f, firstOutput[[5]]) - + # Leave unique only firstOutput[[1]] <- dplyr::distinct(firstOutput[[1]]) firstOutput[[2]] <- dplyr::distinct(firstOutput[[2]]) firstOutput[[3]] <- dplyr::distinct(firstOutput[[3]]) - - - + + + # STEP 2: CLASSIFY SET 2, AND SET 1 SUBSET 2 # ------------------------------------------------------------- # The remaining forbidden paths are those belonging to this set secondOutput <- foreach::foreach(k = 1:nrow(f), .combine = .comb, .multicombine = TRUE, .export = ".get_arc_attributes") %dopar% { - # Create the new arcs - tempNewArcs <- g[0,] - tempDeletedArcs <- g[0,1:2] - - # Create a list of banned arcs and nodes - tempBannedArcs <- data.frame(from = as.character(), to = as.character(), stringsAsFactors = FALSE) - tempBannedNodes <- data.frame(node = as.character(), stringsAsFactors = FALSE) - - - # Remove the first arc of the FP in the original graph - tempDeletedArcs[nrow(tempDeletedArcs) + 1,] <- list(f[k,1], f[k,2]) - - # Prepare a predecesor node - preNode <- f[k,1] - - # Get the cleaned path without NA - cleanfp <- f[k,][which(!is.na(f[k,]))] - - # From the second to the last one - # Here we need to go until the last node - continue <- TRUE - l <- 2 - while(continue & l <= length(cleanfp)) { - # Create the new node name - nodeName <- paste0(as.character(cleanfp)[1:l], collapse = "|") - - # If this is the second we use the node as-is - if(l == 2) { - # Add the link to the predecesor with its attributes - if(ncol > 2) { - tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName, - .get_arc_attributes(g, f[k,l-1], f[k,l])) - } - # Or without them - else tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName) - - # Ban the next arc - tempBannedArcs[nrow(tempBannedArcs) + 1,] <- c(nodeName, f[k, l + 1]) - - # And update the predecesor - preNode <- nodeName - } - else { - # Node name and quality of last - lastNode <- l == length(cleanfp[k,]) - lastPart <- ifelse(lastNode | l == 3, nodeName, - paste0(tail(strsplit(nodeName, "\\|")[[1]], 3), collapse = "|")) - - # If this is the last node, ban it - if(lastNode) - tempBannedNodes[nrow(tempBannedNodes) + 1,1] <- nodeName - - # If there is a banned element - if(lastPart %in% tempBannedNodes | lastPart %in% firstOutput[[4]]$node) { - # If the node has arcs going or comming to it, delete them - tempNewArcs <- setdiff(tempNewArcs, subset(tempNewArcs, tempNewArcs$to == lastPart)) - tempNewArcs <- setdiff(tempNewArcs, subset(tempNewArcs, tempNewArcs$from == lastPart)) - - # Do not continue - continue <- FALSE - } - # Otherwise, add it - else { - # Add the link to the predecesor, with its attributes - if(ncol > 2) { - tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName, - .get_arc_attributes(g, f[k,l-1], f[k,l])) - } - # Or without them - else tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName) - - - # Ban the next arc - tempBannedArcs[nrow(tempBannedArcs) + 1,] <- c(nodeName, f[k, l + 1]) - - # And update the predecesor - preNode <- nodeName - } - } - - # Increase the counter - l <- l + 1 - } - - # Return the results - list(tempNewArcs, tempDeletedArcs, tempBannedArcs, tempBannedNodes) - } - + # Create the new arcs + tempNewArcs <- g[0,] + tempDeletedArcs <- g[0,1:2] + + # Create a list of banned arcs and nodes + tempBannedArcs <- data.frame(from = as.character(), to = as.character(), stringsAsFactors = FALSE) + tempBannedNodes <- data.frame(node = as.character(), stringsAsFactors = FALSE) + + + # Remove the first arc of the FP in the original graph + tempDeletedArcs[nrow(tempDeletedArcs) + 1,] <- list(f[k,1], f[k,2]) + + # Prepare a predecesor node + preNode <- f[k,1] + + # Get the cleaned path without NA + cleanfp <- f[k,][which(!is.na(f[k,]))] + + # From the second to the last one + # Here we need to go until the last node + continue <- TRUE + l <- 2 + while(continue & l <= length(cleanfp)) { + # Create the new node name + nodeName <- paste0(as.character(cleanfp)[1:l], collapse = "|") + + # If this is the second we use the node as-is + if(l == 2) { + # Add the link to the predecesor with its attributes + if(ncol > 2) { + tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName, + .get_arc_attributes(g, f[k,l-1], f[k,l])) + } + # Or without them + else tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName) + + # Ban the next arc + tempBannedArcs[nrow(tempBannedArcs) + 1,] <- c(nodeName, f[k, l + 1]) + + # And update the predecesor + preNode <- nodeName + } + else { + # Node name and quality of last + lastNode <- l == length(cleanfp[k,]) + lastPart <- ifelse(lastNode | l == 3, nodeName, + paste0(tail(strsplit(nodeName, "\\|")[[1]], 3), collapse = "|")) + + # If this is the last node, ban it + if(lastNode) + tempBannedNodes[nrow(tempBannedNodes) + 1,1] <- nodeName + + # If there is a banned element + if(lastPart %in% tempBannedNodes | lastPart %in% firstOutput[[4]]$node) { + # If the node has arcs going or comming to it, delete them + tempNewArcs <- setdiff(tempNewArcs, subset(tempNewArcs, tempNewArcs$to == lastPart)) + tempNewArcs <- setdiff(tempNewArcs, subset(tempNewArcs, tempNewArcs$from == lastPart)) + + # Do not continue + continue <- FALSE + } + # Otherwise, add it + else { + # Add the link to the predecesor, with its attributes + if(ncol > 2) { + tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName, + .get_arc_attributes(g, f[k,l-1], f[k,l])) + } + # Or without them + else tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName) + + + # Ban the next arc + tempBannedArcs[nrow(tempBannedArcs) + 1,] <- c(nodeName, f[k, l + 1]) + + # And update the predecesor + preNode <- nodeName + } + } + + # Increase the counter + l <- l + 1 + } + + # Return the results + list(tempNewArcs, tempDeletedArcs, tempBannedArcs, tempBannedNodes) + } + # Merge banned nodes bannedNodes <- c(firstOutput[[4]]$node, secondOutput[[4]]$node) # For each of the banned nodes deletedBannedArcs <- foreach::foreach(bn = bannedNodes, .combine = rbind) %dopar% { temp <- g[0,] - + # Get the length of the banned node length <- nchar(bn) - + # Get the arcs where the from contains the banned node arcsEvaluation <- as.data.frame( apply(X = secondOutput[[1]], MARGIN = 1:2, FUN = function(x) substr(x, nchar(x) - length + 1, nchar(x)) == bn) ) - + # Now get the arcs subset(secondOutput[[1]], arcsEvaluation$from | arcsEvaluation$to) } - + # Now remove this secondOutput[[1]] <- dplyr::setdiff(secondOutput[[1]], deletedBannedArcs) rm(deletedBannedArcs) - - + + # Fill the banned arcs + secondOutput[[3]] <- unique(rbind(secondOutput[[3]], firstOutput[[3]])) + + # STEP 3: LINK BACK TO THE ORIGINAL GRAPH # --------------------------------------- # Now go through the new nodes - newNodes <- unique(secondOutput[[1]]$to) + newNodes <- c(unique(secondOutput[[1]]$to), unique(firstOutput[[1]]$to)) thirdOutput <- foreach::foreach(nn = newNodes, .combine = rbind, .export = ".get_arc_attributes") %dopar% { # Create the arcs newArcs <- g[0,] - + # Split by pipe and get the last element nsName <- gsub(".*\\|(.*)", "\\1", nn) - + # Get the outgoing arcs for the original node, that are not banned - toNodes <- setdiff(subset(g, from == nsName), secondOutput[[3]])$to - + # toNodes <- setdiff(subset(g, from == nsName), secondOutput[[3]])$to + toNodes <- setdiff(subset(g[,1:2], from == nsName)$to, + subset(secondOutput[[3]], from == nn | from == nsName)$to ) + + # For each element for(toNode in toNodes) { # Evaluate if there is a node newTo <- newNodes[grepl(paste0(nsName, "\\|", toNode, "$"), newNodes)] - + # If there is a value, and the transitive is not banned if(!identical(newTo, character(0)) & nrow(subset(secondOutput[[3]], from == nn & to == toNode)) == 0) { # Add it using the new node, if the original is not deleted @@ -328,28 +337,28 @@ modify_graph_hsu <- function(g, f, cores = 1L) { # ...and if the value with the original node is not banned else if(! any(apply( secondOutput[[3]], 1, function(x) paste(x, collapse="") == paste(c(nn,toNode), collapse="")) ) ) { - + # Add the new link, but with the original node and its attributes if(ncol > 2) { newArcs[nrow(newArcs) + 1, ] <- list(nn, toNode, .get_arc_attributes(g, nsName, toNode)) } # Or without them else newArcs[nrow(newArcs) + 1, ] <- list(nn, toNode) - + } } - + # Return the value newArcs } - + # Stop Cluster parallel::stopCluster(cluster) - - + + # Remove deleted arcs from here g <- g[!(g$from %in% firstOutput[[2]]$from & g$to %in% firstOutput[[2]]$to),] g <- g[!(g$from %in% secondOutput[[2]]$from & g$to %in% secondOutput[[2]]$to),] - + # Now add the new arcs and return return(unique(rbind(g, firstOutput[[1]]) %>% rbind(g, secondOutput[[1]]) %>% rbind(thirdOutput))) -} +} \ No newline at end of file diff --git a/R/igraph.R b/R/igraph.R index eb258d2..c0ffae6 100644 --- a/R/igraph.R +++ b/R/igraph.R @@ -26,7 +26,9 @@ #' #' @importFrom igraph graph_from_data_frame #' @importFrom igraph shortest_paths -#' @importFrom igraph E +#' @importFrom igraph E +#' @importFrom foreach %do% +#' @importFrom foreach foreach #' #' @examples #' # Given a specific gStar graph: diff --git a/R/parsers.R b/R/parsers.R index 1b1917a..57d562e 100644 --- a/R/parsers.R +++ b/R/parsers.R @@ -60,6 +60,9 @@ parse_vpath <- function(vpath) { #' #' @importFrom foreach %dopar% #' @importFrom magrittr %>% +#' @importFrom parallel makeCluster +#' @importFrom doParallel registerDoParallel +#' @importFrom parallel stopCluster #' #' @examples #' # Obtain the graph from any way diff --git a/R/villeneuve.R b/R/villeneuve.R index 120349d..ba413bc 100644 --- a/R/villeneuve.R +++ b/R/villeneuve.R @@ -31,6 +31,9 @@ #' #' @importFrom foreach %dopar% #' @importFrom magrittr %>% +#' @importFrom parallel makeCluster +#' @importFrom doParallel registerDoParallel +#' @importFrom parallel stopCluster #' #' @examples #' # Obtain a graph and its forbidden subpaths @@ -53,74 +56,74 @@ modify_graph_vd <- function(g, f, cores = 1L) { # Modify G g$from <- as.character(g$from) g$to <- as.character(g$to) - + # Number of columns ncol <- ncol(g) - + # Set up the parallel - cluster <- parallel::makeCluster(cores) - doParallel::registerDoParallel(cluster) - - firstOutput <- foreach::foreach(i = 1:nrow(f), .combine = .comb, .multicombine = TRUE, - .export = ".get_arc_attributes") %dopar% { + cluster <- makeCluster(cores) + registerDoParallel(cluster) + + firstOutput <- foreach(i = 1:nrow(f), .combine = .comb, .multicombine = TRUE, .export = ".get_arc_attributes") %dopar% { # Create a list of nodes and the new graph tempNewArcs <- g[0,] - + # Prepare a predecesor node preNode <- f[i,1] - + # Create a list of banned arcs tempBannedArcs <- g[0,1:2] - + # We are looping from the second to the penultimate item on the list. # The last node is ignored because it is only temporal, # So by ignoring it, we aim to reduce working time for(level in 2:(length(f[i,]) - 1) ) { # Create the new node name nodeName <- paste0(as.character(f[i,])[1:level], collapse = "|") - + # Add the link to the predecesor with the attributes if(ncol > 2) { - tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName, .get_arc_attributes(g, f[i, level-1], f[i, level])) + tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName, + .get_arc_attributes(g, f[i, level-1], f[i, level])) } # Otherwise just add the values else tempNewArcs[nrow(tempNewArcs) + 1,] <- list(preNode, nodeName) - + # And update the predecesor preNode <- nodeName } # Ban the next arc tempBannedArcs[nrow(tempBannedArcs) + 1,] <- c(nodeName, f[i, level + 1]) - + # Delete arcs tempDelete <- g[0,1:2] tempDelete[1,] <- c(f[i,1], f[i,2]) - + # Return the temporal frame to mix it with the others list(tempNewArcs, tempDelete, tempBannedArcs) } # Remove deleted arcs from here g <- g[!(g$from %in% firstOutput[[2]]$from & g$to %in% firstOutput[[2]]$to),] - - + + # Now through the new nodes newNodes <- firstOutput[[1]]$to - secondOutput <- foreach::foreach(nn = newNodes, .combine = rbind, .export = ".get_arc_attributes") %dopar% { + secondOutput <- foreach(nn = newNodes, .combine = rbind, .export = ".get_arc_attributes") %dopar% { # Split by pipe and get the last element nsName <- gsub(".*\\|(.*)", "\\1", nn) - + # Get the outgoing arcs for the original node, that are not banned - toNodes <- dplyr::setdiff(subset(g, from == nsName)[,1:2], firstOutput[[3]])$to - - + toNodes <- setdiff(subset(g[,1:2], from == nsName)$to, + subset(firstOutput[[3]], from == nsName | from == nn)$to) + # Prepare the temporal frame tempNewArcs <- g[0,] - + # For each element for(toNode in toNodes) { # Evaluate if there is a newTo <- newNodes[grepl(paste0(nsName, "\\|", toNode, "$"), newNodes)] - + # If there is a value... if(!identical(newTo, character(0))) { # ...add it using the new node, if it needs attributes @@ -134,7 +137,7 @@ modify_graph_vd <- function(g, f, cores = 1L) { # ...and if the value with the original node is not banned else if(! any(apply( firstOutput[[3]], 1, function(x) paste(x, collapse="") == paste(c(nn,toNode), collapse="")) ) ) { - + # Add the new link, but with the original node if(ncol > 2) { tempNewArcs[nrow(tempNewArcs) + 1,] <- list(nn, toNode, .get_arc_attributes(g, nsName, toNode)) @@ -143,14 +146,14 @@ modify_graph_vd <- function(g, f, cores = 1L) { else tempNewArcs[nrow(tempNewArcs) + 1,] <- list(nn, toNode) } } - + # Return the new value tempNewArcs } - + # Stop Cluster - parallel::stopCluster(cluster) - + stopCluster(cluster) + # Now return the - return( rbind(g, firstOutput[[1]]) %>% rbind(secondOutput) %>% unique()) + return( rbind(g, firstOutput[[1]]) %>% rbind(secondOutput) ) } diff --git a/README.Rmd b/README.Rmd index cafd8af..93ff50d 100644 --- a/README.Rmd +++ b/README.Rmd @@ -3,6 +3,8 @@ [![Travis-CI Build Status](https://travis-ci.org/melvidoni/rsppfp.svg?branch=master)](https://travis-ci.org/melvidoni/rsppfp) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/melvidoni/rsppfp?branch=master&svg=true)](https://ci.appveyor.com/project/melvidoni/rsppfp) [![Coverage Status](https://img.shields.io/codecov/c/github/melvidoni/rsppfp/master.svg)](https://codecov.io/github/melvidoni/rsppfp?branch=master) +[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) + The **R-SPPFP** package implements different algorithms for transforming graphs in the _Shortest Path Problem with Forbidden Paths_ (SPPFP). This problem is an important concept in the field of graph theories, and it is a variant of the traditional _shortest path problem_. In here, there is an additional constrait that takes the form of a finite set of forbidden paths (arc sequences of at least three nodes) that cannot be part of any solution path. diff --git a/README.md b/README.md index cafd8af..0cc91c6 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Travis-CI Build Status](https://travis-ci.org/melvidoni/rsppfp.svg?branch=master)](https://travis-ci.org/melvidoni/rsppfp) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/melvidoni/rsppfp?branch=master&svg=true)](https://ci.appveyor.com/project/melvidoni/rsppfp) [![Coverage Status](https://img.shields.io/codecov/c/github/melvidoni/rsppfp/master.svg)](https://codecov.io/github/melvidoni/rsppfp?branch=master) +[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) The **R-SPPFP** package implements different algorithms for transforming graphs in the _Shortest Path Problem with Forbidden Paths_ (SPPFP). This problem is an important concept in the field of graph theories, and it is a variant of the traditional _shortest path problem_. In here, there is an additional constrait that takes the form of a finite set of forbidden paths (arc sequences of at least three nodes) that cannot be part of any solution path. diff --git a/_pkgdown.yaml b/_pkgdown.yaml index 5820f06..71c2016 100644 --- a/_pkgdown.yaml +++ b/_pkgdown.yaml @@ -40,8 +40,11 @@ navbar: href: "articles/igraph.html" - text: "References" - href: "articles/references.html" - + href: articles/references.html + + - text: Changelog + href: news/index.html + - icon: fa-github text: "GitHub" href: https://github.com/melvidoni/rsppfp \ No newline at end of file diff --git a/docs/LICENSE-text.html b/docs/LICENSE-text.html index 9110de9..44e37b7 100644 --- a/docs/LICENSE-text.html +++ b/docs/LICENSE-text.html @@ -91,6 +91,9 @@
  • References
  • +
  • + Changelog +
  • diff --git a/docs/articles/benchmark.html b/docs/articles/benchmark.html index 1a1e209..1786938 100644 --- a/docs/articles/benchmark.html +++ b/docs/articles/benchmark.html @@ -65,6 +65,9 @@
  • References
  • +
  • + Changelog +
  • diff --git a/docs/articles/igraph.html b/docs/articles/igraph.html index 62a5d5b..46d3ca0 100644 --- a/docs/articles/igraph.html +++ b/docs/articles/igraph.html @@ -65,6 +65,9 @@
  • References
  • +
  • + Changelog +
  • @@ -119,7 +122,7 @@

    .Names = c("V1", "V2", "V3", "V4", "V5"), class = "data.frame", row.names = c(NA, -4L))

    After this, it is possible to use R-SPPFP’s functions to transform the original graph, into G*. In this case, some forbidden paths have sub-paths that are part of others; particular examples are f_3 and f_4. As a result, the example makes use of Hsu’s backward construction function.

    # Run the algorithm and transform the graph
    -gStar <- modify_graph_hsu(graph, fpaths, 3L)
    +gStar <- modify_graph_hsu(graph, fpaths)
     gStar
     #>     from    to cost
     #> 1      s     u    1
    @@ -140,7 +143,10 @@ 

    #> 33 x|w x|w|v 1 #> 17 w|v t 1 #> 24 x|w y 2 -#> 34 x|w|v t 1

    +#> 34 x|w|v t 1 +#> 41 u|v t 1 +#> 5 u|v|y t 3 +#> 6 u|w w|v 1

    @@ -165,23 +171,26 @@

    g.i <- graph_from_data_frame(g) # Get all the shortest paths to each node - sp <- shortest_paths(g.i, from = origin, to = get_all_nodes(gStar, dest), weights = E(g.i)$weight, output = "both") + sp <- shortest_paths(g.i, from = origin, to = get_all_nodes(g, dest), weights = E(g.i)$weight, output = "both") # Return the shortest path - sp$vpath[which.min( lapply(sp$epath, function(y) - ifelse(length(y) != 0, sum(E(g.i)$weight[ y[[1]] ]), 999999999)) )] [[1]] + sp$vpath[which.min( + foreach(i = 1:length(sp$epath), .combine = c) %do% { + ifelse( length(sp$epath[i]), sum(E(g.i)$weight[ sp$epath[[i]] ]), 999999999) + } + )] }

    And it can be used as in the following example:

    # Obtain the shortest path using the simplified function
    -shortestPath <- get_shortest_path(gStar, "u", "w")
    +shortestPath <- get_shortest_path(gStar, "u", "t")
     shortestPath
    -#> + 2/13 vertices, named, from 9e553ad:
    -#> [1] u   u|w
    -

    Also,

    -

    It is worth pointing out that a path can only be translated if it is presented as a list or vector of nodes, written sequentially. In iGraph, this is known as vpaths (vertexes paths).

    +#> [[1]] +#> + 3/13 vertices, named, from ce76b60: +#> [1] u u|v t +

    Also, it is worth pointing out that a path can only be translated if it is presented as a list or vector of nodes, written sequentially. In iGraph, this is known as vpaths (vertexes paths).

    # Translate the vpath
    -parse_vpath(V(gStar.igraph)$name[ shortestPath ])
    -#> [1] "u" "w"
    +parse_vpath(V(gStar.igraph)$name[ unlist(shortestPath) ]) +#> [1] "u" "v" "t" diff --git a/docs/articles/igraph_files/figure-html/unnamed-chunk-4-1.png b/docs/articles/igraph_files/figure-html/unnamed-chunk-4-1.png index 1f42293ec20092cbd5e6545224a05e0e36ca408c..d135de0b18f331e8d3199a3d301949abb5f215d1 100644 GIT binary patch literal 26915 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­{M^-$mM=RCt)a>=A7 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 TAOv{G_K$~J2KP(vJ`MQ~)hAKB diff --git a/docs/articles/index.html b/docs/articles/index.html index 93c9731..ef57fca 100644 --- a/docs/articles/index.html +++ b/docs/articles/index.html @@ -91,6 +91,9 @@
  • References
  • +
  • + Changelog +
  • diff --git a/docs/articles/references.html b/docs/articles/references.html index 087e77c..fc7040d 100644 --- a/docs/articles/references.html +++ b/docs/articles/references.html @@ -65,6 +65,9 @@
  • References
  • +
  • + Changelog +
  • diff --git a/docs/articles/rsppfp.html b/docs/articles/rsppfp.html index a9b0554..8b2d220 100644 --- a/docs/articles/rsppfp.html +++ b/docs/articles/rsppfp.html @@ -65,6 +65,9 @@
  • References
  • +
  • + Changelog +
  • @@ -150,6 +153,7 @@

    #> 41 u|v u|v|y 1 #> 14 s|u w 1 #> 22 s|u|v u|v|y 1 +#> 32 u|v u|v|y 1 #> 42 u|v t 1 #> 5 u|v|y t 7

    The second transformation algorithm is the one developed by Hsu et al. (2009), and it is named backward construction. This algorithms also supports multiple paths of different sizes. However any forbidden path -or part of it- can be a sub-path of another forbidden path. USing the same data of the previous section, this transformation can be called as follows:

    diff --git a/docs/authors.html b/docs/authors.html index a59f5a0..1e48145 100644 --- a/docs/authors.html +++ b/docs/authors.html @@ -91,6 +91,9 @@
  • References
  • +
  • + Changelog +
  • diff --git a/docs/index.html b/docs/index.html index b2a393f..0887a67 100644 --- a/docs/index.html +++ b/docs/index.html @@ -68,6 +68,9 @@
  • References
  • +
  • + Changelog +
  • @@ -168,6 +171,7 @@

    Dev status

  • Travis-CI Build Status
  • AppVeyor Build Status
  • Coverage Status
  • +
  • License: GPL v3
  • diff --git a/docs/news/index.html b/docs/news/index.html index 27f4768..4a597c2 100644 --- a/docs/news/index.html +++ b/docs/news/index.html @@ -91,6 +91,9 @@
  • References
  • +
  • + Changelog +
  • @@ -123,12 +126,20 @@

    Changelog

    Source:
    NEWS.md -
    -

    -rsppfp 2.0.0.0

    +
    +

    +RSPPFP 1.0.0.0

    +

    First stable release of advanced implementation of R-SPPFP, released on June 2018.

    +
    +

    +Major Changes

    +

    First stable release of advanced implementation of R-SPPFP! Includes the following functions:

      -
    • Added a NEWS.md file to track changes to the package.
    • +
    • Transformation algorithms (Villeneuve & Desaulniers, and Hsu’s et al.).
    • +
    • Basic parsing functions: graph-to-digraph, and vpath translation.
    • +
    • Integration functions: equivalent nodes selection, and igraph’s integration.
    +
    @@ -136,7 +147,7 @@

    Contents

    diff --git a/docs/reference/direct_graph.html b/docs/reference/direct_graph.html index c0c2294..628baa1 100644 --- a/docs/reference/direct_graph.html +++ b/docs/reference/direct_graph.html @@ -97,6 +97,9 @@
  • References
  • +
  • + Changelog +
  • diff --git a/docs/reference/get_all_nodes.html b/docs/reference/get_all_nodes.html index de62dcf..c9add86 100644 --- a/docs/reference/get_all_nodes.html +++ b/docs/reference/get_all_nodes.html @@ -98,6 +98,9 @@
  • References
  • +
  • + Changelog +
  • diff --git a/docs/reference/get_shortest_path.html b/docs/reference/get_shortest_path.html index 63a74b2..a9014c3 100644 --- a/docs/reference/get_shortest_path.html +++ b/docs/reference/get_shortest_path.html @@ -102,6 +102,9 @@
  • References
  • +
  • + Changelog +
  • @@ -205,10 +208,8 @@

    Examp #> 17 u u|v 4 #> 18 u|v u|v|y 3
    # Obtain the shortest path -get_shortest_path(gStar, "s", "v")
    #> Called from: eval(expr, p) -#> debug at /home/melina/RProjects/rsppfp/R/igraph.R#50: sp <- shortest_paths(g.i, from = origin, to = get_all_nodes(g, -#> dest), weights = E(g.i)$weight, output = "both")
    #> [[1]] -#> + 3/11 vertices, named, from a07cdb9: +get_shortest_path(gStar, "s", "v")
    #> [[1]] +#> + 3/11 vertices, named, from e6f75ba: #> [1] s s|u s|u|v #>
    diff --git a/docs/reference/parse_vpath.html b/docs/reference/parse_vpath.html index f440544..7254e82 100644 --- a/docs/reference/parse_vpath.html +++ b/docs/reference/parse_vpath.html @@ -95,6 +95,9 @@
  • References
  • +
  • + Changelog +
  • diff --git a/docs/reference/rsppfp.html b/docs/reference/rsppfp.html index 26bf2f9..74547c9 100644 --- a/docs/reference/rsppfp.html +++ b/docs/reference/rsppfp.html @@ -95,6 +95,9 @@
  • References
  • +
  • + Changelog +
  • diff --git a/tests/testthat/test-hsu.R b/tests/testthat/test-hsu.R index d2c0db2..60d7c29 100644 --- a/tests/testthat/test-hsu.R +++ b/tests/testthat/test-hsu.R @@ -10,14 +10,16 @@ test_that("Default example", { V4 = c("u", "u", "", "y"), V5 = c("", "", "", "t")), .Names = c("V1", "V2", "V3", "V4", "V5"), class = "data.frame", row.names = c(NA, -4L)) - expected.gStar <- structure(list(from = c("s", "s", "s", "w", "x", "v", "v", "y", "y", "u", "u|v", "u", "u|w", "w", - "x", "x|w", "w|v", "x|w", "x|w|v"), - to = c("u", "w", "x", "y", "y", "y", "t", "t", "u", "u|v", "u|v|y", "u|w", "u|v|y", - "w|v", "x|w", "x|w|v", "t", "y", "t"), - cost = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L)), - .Names = c("from", "to", "cost"), - row.names = c("1", "2", "3", "7", "9", "10", "11", "12", "13", "14", "21", "31", - "4", "16", "23", "33", "17", "24", "34"), class = "data.frame") + expected.gStar <- structure(list(from = c("s", "s", "s", "w", "x", "v", "v", "y", "y", "u", "u|v", "u", + "u|w", "w", "x", "x|w", "w|v", "x|w", "x|w|v", "u|v", "u|v|y", "u|w"), + to = c("u", "w", "x", "y", "y", "y", "t", "t", "u", "u|v", "u|v|y", "u|w", + "u|v|y", "w|v", "x|w", "x|w|v", "t", "y", "t", "t", "t", "w|v"), + cost = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L)), + .Names = c("from", "to", "cost"), + row.names = c("1", "2", "3", "7", "9", "10", "11", "12", "13", "14", "21", "31", + "4", "16", "23", "33", "17", "24", "34", "41", "5", "6"), + class = "data.frame") expect_equal(modify_graph_hsu(data.graph, data.fpaths), expected.gStar) }) @@ -32,15 +34,17 @@ test_that("Additional example", { r = c("a", "o", "n"), u_1 = c("r", "m", "o"), X5 = c("u", NA, NA)), .Names = c("u", "e", "r", "u_1", "X5"), row.names = c(NA, -3L), class = "data.frame") - expected.gStar <- structure(list(from = c("c", "c", "u", "t", "a", "r", "e", "e", "e", "i", "i", "n", "o", "u", "u|t", - "u|t|a", "p", "p|n", "a", "a|i", "u|t|a", "a|i"), - to = c("u", "p", "e", "a", "r", "u", "r", "i", "p", "n", "o", "o", "m", "u|t", - "u|t|a", "u|t|a|r", "p|n", "p|n|o", "a|i", "a|i|n", "a|i", "o")), - .Names = c("from", "to"), - row.names = c("1", "2", "3", "5", "6", "8", "9", "10", "11", "13", "14", "15", "16", "17", - "22", "32", "4", "52", "62", "7", "18", "23"), class = "data.frame") + expected.gStar <- structure(list(from = c("c", "c", "u", "t", "a", "r", "e", "e", + "e", "i", "i", "n", "o", "u", "u|t", "u|t|a", "p", "p|n", "a", + "a|i", "u|t|a", "a|i"), + to = c("u", "p", "e", "a", "r", "u", "r", "i", "p", "n", "o", "o", "m", "u|t", + "u|t|a", "u|t|a|r", "p|n", "p|n|o", "a|i", "a|i|n", "a|i", "o")), + .Names = c("from", "to"), + row.names = c("1", "2", "3", "5", "6", "8", "9", "10", "11", "13", "14", "15", + "16", "17", "22", "32", "4", "52", "62", "7", "18", "23"), + class = "data.frame") - expect_equal(modify_graph_hsu(data.graph, data.fpaths, 3L), expected.gStar) + expect_equal(modify_graph_hsu(data.graph, data.fpaths), expected.gStar) }) diff --git a/tests/testthat/test-villeneuve.R b/tests/testthat/test-villeneuve.R index 4068945..93bfb89 100644 --- a/tests/testthat/test-villeneuve.R +++ b/tests/testthat/test-villeneuve.R @@ -9,28 +9,28 @@ test_that("Default example", { data.fpaths <- structure(list(V1 = c("s", "u"), V2 = c("u", "v"), V3 = c("v", "y"), V4 = c("t", "u")), .Names = c("V1", "V2", "V3", "V4"), class = "data.frame", row.names = c(NA, -2L)) - expected.gStar <- structure(list(from = c("s", "s", "u", "w", "w", "x", "x", "v", "v", "y", "y", "s", "s|u", - "u", "u|v", "s|u", "s|u|v", "u|v", "u|v|y"), - to = c("w", "x", "w", "v", "y", "w", "y", "y", "t", "t", "u", "s|u", "s|u|v", - "u|v", "u|v|y", "w", "u|v|y", "t", "t"), - cost = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L)), - .Names = c("from", "to", "cost"), - row.names = c("2", "3", "4", "6", "7", "8", "9", "10", "11", "12", "13", "1", "21", "31", - "41", "14", "22", "42", "5"), - class = "data.frame") - - - expected.gStar2 <- structure(list(from = c("s", "s", "u", "w", "w", "x", "x", "v", "v", "y", "s", - "s|u", "u", "u|v", "s|u", "s|u|v", "u|v", "u|v"), - to = c("w", "x", "w", "v", "y", "w", "y", "y", "t", "t", "u", "s|u", - "u|v", "u|v|y", "w", "u|v|y", "u|v|y", "t"), - cost = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L)), - .Names = c("from", "to", "cost"), - row.names = c("2", "3", "4", "6", "7", "8", "9", "10", "11", "12", "13", "1", - "21", "31", "41", "14", "22", "32", "42"), - class = "data.frame") - - expect_equal(modify_graph_vd(data.graph, data.fpaths, 3L), expected = expected.gStar) + expected.gStar <- structure(list(from = c("s", "s", "u", "w", "w", "x", "x", "v", "v", "y", "y", "s", "s|u", + "u", "u|v", "s|u", "s|u|v", "u|v", "u|v", "u|v|y"), + to = c("w", "x", "w", "v", "y", "w", "y", "y", "t", "t", "u", "s|u", "s|u|v", + "u|v", "u|v|y", "w", "u|v|y", "u|v|y", "t", "t"), + cost = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L)), + .Names = c("from", "to", "cost"), + row.names = c("2", "3", "4", "6", "7", "8", "9", "10", "11", "12", "13", "1", + "21", "31", "41", "14", "22", "32", "42", "5"), class = "data.frame") + + + expected.gStar2 <- structure(list(from = c("s", "s", "u", "w", "w", "x", "v", "y", "y", "s", "s|u", + "u", "u|v", "s|u", "s|u|v", "u|v", "u|v", "u|v|y"), + to = c("w", "x", "w", "v", "y", "w", "y", "y", "t", "u", "s|u", "s|u|v", + "u|v", "u|v|y", "w", "u|v|y", "t", "t"), + cost = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L)), + .Names = c("from", "to", "cost"), + row.names = c("2", "3", "4", "6", "7", "8", "9", "10", "11", "12", "13", "1", + "21", "31", "41", "14", "22", "32", "42", "5"), class = "data.frame") + + expect_equal(modify_graph_vd(data.graph, data.fpaths, 1L), expected = expected.gStar) expect_false( isTRUE( all.equal(modify_graph_vd(data.graph, data.fpaths, 2L), expected.gStar2) diff --git a/vignettes/igraph.Rmd b/vignettes/igraph.Rmd index c13d384..b0e7801 100644 --- a/vignettes/igraph.Rmd +++ b/vignettes/igraph.Rmd @@ -49,7 +49,7 @@ After this, it is possible to use **R-SPPFP**’s functions to transform the ori ```{r} # Run the algorithm and transform the graph -gStar <- modify_graph_hsu(graph, fpaths, 3L) +gStar <- modify_graph_hsu(graph, fpaths) gStar ``` @@ -120,7 +120,7 @@ Also, it is worth pointing out that a path can only be translated if it is prese ```{r} # Translate the vpath -parse_vpath(V(gStar.igraph)$name[ shortestPath ]) +parse_vpath(V(gStar.igraph)$name[ unlist(shortestPath) ]) ```