diff --git a/R/cliques.R b/R/cliques.R index 9005ce8f8cc..83ebe81f351 100644 --- a/R/cliques.R +++ b/R/cliques.R @@ -221,8 +221,11 @@ clique.number <- function(graph) { #' vectors of vertex IDs. Each list element is a clique, i.e. a vertex sequence #' of class [igraph.vs][V]. #' -#' `max_cliques()` returns `NULL`, invisibly, if its `file` -#' argument is not `NULL`. The output is written to the specified file in +#' `max_cliques()` returns: +#' - When `file` is `NULL` and `callback` is `NULL`, a list of vertex sequences. +#' - When `callback` is not `NULL`, the callback function is called for each click, +#' then `max_cliques()` returns `NULL` invisibly. +#' - When `file` is not `NULL`, the output is written to the specified file in #' this case. #' #' `clique_num()` and `count_max_cliques()` return an integer @@ -286,16 +289,18 @@ largest_cliques <- function(graph) { } #' @rdname cliques -#' @param subset If not `NULL`, then it must be a vector of vertex IDs, +#' @param subset A vector of vertex IDs, #' numeric or symbolic if the graph is named. The algorithm is run from these #' vertices only, so only a subset of all maximal cliques is returned. See the #' Eppstein paper for details. This argument makes it possible to easily -#' parallelize the finding of maximal cliques. -#' @param file If not `NULL`, then it must be a file name, i.e. a -#' character scalar. The output of the algorithm is written to this file. (If -#' it exists, then it will be overwritten.) Each clique will be a separate line -#' in the file, given with the numeric IDs of its vertices, separated by -#' whitespace. +#' parallelize the finding of maximal cliques. Default: `NULL`, the algorithm +#' is run from all vertices. +#' @param file A file name, i.e. a character scalar. +#' The output of the algorithm is written to this file. +#' (If it exists, then it will be overwritten.) +#' Each clique will be a separate line in the file, +#' given with the numeric IDs of its vertices, separated by whitespace. +#' Default: `NULL`, the function returns a list of vertex sequences. #' @export max_cliques <- function( graph, @@ -311,30 +316,21 @@ max_cliques <- function( # Handle file and subset modes (original functionality) if (!is.null(file)) { - if ( - !is.character(file) || - length(grep("://", file, fixed = TRUE)) > 0 || - length(grep("~", file, fixed = TRUE)) > 0 - ) { - tmpfile <- TRUE - origfile <- file - file <- tempfile() - } else { - tmpfile <- FALSE + if (!is.character(file)) { + cli::cli_abort("{.arg file} must be a file path, not a connection.") + } + if (grepl("://", file, fixed = TRUE)) { + cli::cli_abort("{.arg file} must be a file path, not a URL.") } on.exit(.Call(Rx_igraph_finalizer)) - res <- .Call( + .Call( Rx_igraph_maximal_cliques_file, graph, subset, - file, + path.expand(file), as.numeric(min %||% 0), as.numeric(max %||% 0) ) - if (tmpfile) { - buffer <- read.graph.toraw(file) - write.graph.fromraw(buffer, origfile) - } return(invisible(NULL)) } diff --git a/man/cliques.Rd b/man/cliques.Rd index a9d7497d016..da026562d45 100644 --- a/man/cliques.Rd +++ b/man/cliques.Rd @@ -61,17 +61,19 @@ so will cause R to crash due to reentrancy issues. Extract any needed graph information before calling the function with a callback, or use collector mode (the default) and process results afterward.} -\item{subset}{If not \code{NULL}, then it must be a vector of vertex IDs, +\item{subset}{A vector of vertex IDs, numeric or symbolic if the graph is named. The algorithm is run from these vertices only, so only a subset of all maximal cliques is returned. See the Eppstein paper for details. This argument makes it possible to easily -parallelize the finding of maximal cliques.} +parallelize the finding of maximal cliques. Default: \code{NULL}, the algorithm +is run from all vertices.} -\item{file}{If not \code{NULL}, then it must be a file name, i.e. a -character scalar. The output of the algorithm is written to this file. (If -it exists, then it will be overwritten.) Each clique will be a separate line -in the file, given with the numeric IDs of its vertices, separated by -whitespace.} +\item{file}{A file name, i.e. a character scalar. +The output of the algorithm is written to this file. +(If it exists, then it will be overwritten.) +Each clique will be a separate line in the file, +given with the numeric IDs of its vertices, separated by whitespace. +Default: \code{NULL}, the function returns a list of vertex sequences.} \item{vertex.weights}{Vertex weight vector. If the graph has a \code{weight} vertex attribute, then this is used by default. If the graph does not have a @@ -95,9 +97,14 @@ of class \link[=V]{igraph.vs}. If \code{callback} is provided, returns \code{NUL vectors of vertex IDs. Each list element is a clique, i.e. a vertex sequence of class \link[=V]{igraph.vs}. -\code{max_cliques()} returns \code{NULL}, invisibly, if its \code{file} -argument is not \code{NULL}. The output is written to the specified file in +\code{max_cliques()} returns: +\itemize{ +\item When \code{file} is \code{NULL} and \code{callback} is \code{NULL}, a list of vertex sequences. +\item When \code{callback} is not \code{NULL}, the callback function is called for each click, +then \code{max_cliques()} returns \code{NULL} invisibly. +\item When \code{file} is not \code{NULL}, the output is written to the specified file in this case. +} \code{clique_num()} and \code{count_max_cliques()} return an integer scalar. diff --git a/man/maximal.cliques.Rd b/man/maximal.cliques.Rd index 0570f2408ac..ae209406885 100644 --- a/man/maximal.cliques.Rd +++ b/man/maximal.cliques.Rd @@ -15,17 +15,19 @@ maximal.cliques(graph, min = NULL, max = NULL, subset = NULL, file = NULL) \item{max}{Numeric constant, upper limit on the size of the cliques to find. \code{NULL} means no limit.} -\item{subset}{If not \code{NULL}, then it must be a vector of vertex IDs, +\item{subset}{A vector of vertex IDs, numeric or symbolic if the graph is named. The algorithm is run from these vertices only, so only a subset of all maximal cliques is returned. See the Eppstein paper for details. This argument makes it possible to easily -parallelize the finding of maximal cliques.} +parallelize the finding of maximal cliques. Default: \code{NULL}, the algorithm +is run from all vertices.} -\item{file}{If not \code{NULL}, then it must be a file name, i.e. a -character scalar. The output of the algorithm is written to this file. (If -it exists, then it will be overwritten.) Each clique will be a separate line -in the file, given with the numeric IDs of its vertices, separated by -whitespace.} +\item{file}{A file name, i.e. a character scalar. +The output of the algorithm is written to this file. +(If it exists, then it will be overwritten.) +Each clique will be a separate line in the file, +given with the numeric IDs of its vertices, separated by whitespace. +Default: \code{NULL}, the function returns a list of vertex sequences.} } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} diff --git a/man/maximal.cliques.count.Rd b/man/maximal.cliques.count.Rd index 5dad067399f..954024b1908 100644 --- a/man/maximal.cliques.count.Rd +++ b/man/maximal.cliques.count.Rd @@ -15,11 +15,12 @@ maximal.cliques.count(graph, min = NULL, max = NULL, subset = NULL) \item{max}{Numeric constant, upper limit on the size of the cliques to find. \code{NULL} means no limit.} -\item{subset}{If not \code{NULL}, then it must be a vector of vertex IDs, +\item{subset}{A vector of vertex IDs, numeric or symbolic if the graph is named. The algorithm is run from these vertices only, so only a subset of all maximal cliques is returned. See the Eppstein paper for details. This argument makes it possible to easily -parallelize the finding of maximal cliques.} +parallelize the finding of maximal cliques. Default: \code{NULL}, the algorithm +is run from all vertices.} } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} diff --git a/src/rinterface_extra.c b/src/rinterface_extra.c index b9699995125..b64abbaa27a 100644 --- a/src/rinterface_extra.c +++ b/src/rinterface_extra.c @@ -5682,15 +5682,10 @@ SEXP Rx_igraph_maximal_cliques(SEXP graph, SEXP psubset, SEXP Rx_igraph_maximal_cliques_file(SEXP graph, SEXP psubset, SEXP file, SEXP pminsize, SEXP pmaxsize) { igraph_t g; - igraph_integer_t minsize=(igraph_integer_t) REAL(pminsize)[0]; - igraph_integer_t maxsize=(igraph_integer_t) REAL(pmaxsize)[0]; + igraph_integer_t minsize = (igraph_integer_t) REAL(pminsize)[0]; + igraph_integer_t maxsize = (igraph_integer_t) REAL(pmaxsize)[0]; igraph_vector_int_t subset; - SEXP result; FILE *stream; -#if HAVE_OPEN_MEMSTREAM == 1 - char *bp; - size_t size; -#endif Rz_SEXP_to_igraph(graph, &g); if (!Rf_isNull(psubset)) { @@ -5699,29 +5694,17 @@ SEXP Rx_igraph_maximal_cliques_file(SEXP graph, SEXP psubset, SEXP file, IGRAPH_R_CHECK(igraph_vector_int_init(&subset, 0)); } IGRAPH_FINALLY_PV(igraph_vector_int_destroy, &subset); -#if HAVE_OPEN_MEMSTREAM == 1 - stream=open_memstream(&bp, &size); -#else - stream=fopen(CHAR(STRING_ELT(file, 0)), "w"); -#endif - if (stream==0) { igraph_error("Cannot write cliques", __FILE__, - __LINE__, IGRAPH_EFILE); } + stream = fopen(CHAR(STRING_ELT(file, 0)), "w"); + if (stream == 0) { + igraph_error("Cannot write cliques", __FILE__, __LINE__, IGRAPH_EFILE); + } igraph_maximal_cliques_subset(&g, Rf_isNull(psubset) ? 0 : &subset, /*ptr=*/ 0, /*no=*/ 0, /*file=*/ stream, minsize, maxsize); fclose(stream); igraph_vector_int_destroy(&subset); IGRAPH_FINALLY_CLEAN(1); -#if HAVE_OPEN_MEMSTREAM == 1 - PROTECT(result=Rf_allocVector(RAWSXP, size)); - memcpy(RAW(result), bp, sizeof(char)*size); - free(bp); -#else - PROTECT(result=NEW_NUMERIC(0)); -#endif - - UNPROTECT(1); - return result; + return R_NilValue; } /* TOP-LEVEL: called from R via .Call; must use IGRAPH_R_CHECK */