1#   IGraph R package
2#   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
3#   334 Harvard street, Cambridge, MA 02139 USA
4#
5#   This program is free software; you can redistribute it and/or modify
6#   it under the terms of the GNU General Public License as published by
7#   the Free Software Foundation; either version 2 of the License, or
8#   (at your option) any later version.
9#
10#   This program is distributed in the hope that it will be useful,
11#   but WITHOUT ANY WARRANTY; without even the implied warranty of
12#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13#   GNU General Public License for more details.
14#
15#   You should have received a copy of the GNU General Public License
16#   along with this program; if not, write to the Free Software
17#   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
18#   02110-1301 USA
19#
20###################################################################
21
22###################################################################
23# Structure building
24###################################################################
25
26#' Add edges to a graph
27#'
28#' The new edges are given as a vertex sequence, e.g. internal
29#' numeric vertex ids, or vertex names. The first edge points from
30#' \code{edges[1]} to \code{edges[2]}, the second from \code{edges[3]}
31#' to \code{edges[4]}, etc.
32#'
33#' If attributes are supplied, and they are not present in the graph,
34#' their values for the original edges of the graph are set to \code{NA}.
35#'
36#' @param graph The input graph
37#' @param edges The edges to add, a vertex sequence with even number
38#'   of vertices.
39#' @param ... Additional arguments, they must be named,
40#'   and they will be added as edge attributes, for the newly added
41#'   edges. See also details below.
42#' @param attr A named list, its elements will be added
43#'   as edge attributes, for the newly added edges. See also details
44#'   below.
45#' @return The graph, with the edges (and attributes) added.
46#'
47#' @export
48#'
49#' @aliases add.edges
50#' @family functions for manipulating graph structure
51#'
52#' @examples
53#' g <- make_empty_graph(n = 5) %>%
54#'   add_edges(c(1,2, 2,3, 3,4, 4,5)) %>%
55#'   set_edge_attr("color", value = "red") %>%
56#'   add_edges(c(5,1), color = "green")
57#' E(g)[[]]
58#' plot(g)
59
60add_edges <- function(graph, edges, ..., attr = list()) {
61  if (!is_igraph(graph)) {
62    stop("Not a graph object")
63  }
64
65  attrs <- list(...)
66  attrs <- append(attrs, attr)
67  nam <- names(attrs)
68  if (length(attrs) != 0 && (is.null(nam) || any(nam==""))) {
69    stop("please supply names for attributes")
70  }
71
72  edges.orig <- ecount(graph)
73  on.exit( .Call(C_R_igraph_finalizer) )
74  graph <- .Call(C_R_igraph_add_edges, graph, as.igraph.vs(graph, edges)-1)
75  edges.new <- ecount(graph)
76
77  if (edges.new-edges.orig != 0) {
78    idx <- seq(edges.orig+1, edges.new)
79  } else {
80    idx <- numeric()
81  }
82
83  eattrs <- .Call(C_R_igraph_mybracket2, graph, 9L, 4L)
84  for (i in seq(attrs)) { eattrs[[nam[i]]][idx] <- attrs[[nam[i]]] }
85
86  .Call(C_R_igraph_mybracket2_set, graph, 9L, 4L, eattrs)
87}
88
89#' Add vertices to a graph
90#'
91#' If attributes are supplied, and they are not present in the graph,
92#' their values for the original vertices of the graph are set to
93#' \code{NA}.
94#'
95#' @param graph The input graph.
96#' @param nv The number of vertices to add.
97#' @param ... Additional arguments, they must be named,
98#'   and they will be added as vertex attributes, for the newly added
99#'   vertices. See also details below.
100#' @param attr A named list, its elements will be added
101#'   as vertex attributes, for the newly added vertices. See also details
102#'   below.
103#' @return The graph, with the vertices (and attributes) added.
104#'
105#' @aliases add.vertices
106#' @family functions for manipulating graph structure
107#'
108#' @export
109#' @examples
110#' g <- make_empty_graph() %>%
111#'   add_vertices(3, color = "red") %>%
112#'   add_vertices(2, color = "green") %>%
113#'   add_edges(c(1,2, 2,3, 3,4, 4,5))
114#' g
115#' V(g)[[]]
116#' plot(g)
117
118add_vertices <- function(graph, nv, ..., attr=list()) {
119  if (!is_igraph(graph)) {
120    stop("Not a graph object")
121  }
122
123  attrs <- list(...)
124  attrs <- append(attrs, attr)
125  nam <- names(attrs)
126  if (length(attrs) != 0 && (is.null(nam) || any(nam==""))) {
127    stop("please supply names for attributes")
128  }
129
130  vertices.orig <- vcount(graph)
131  on.exit( .Call(C_R_igraph_finalizer) )
132  graph <- .Call(C_R_igraph_add_vertices, graph, as.numeric(nv))
133  vertices.new <- vcount(graph)
134
135  if (vertices.new-vertices.orig != 0) {
136    idx <- seq(vertices.orig+1, vertices.new)
137  } else {
138    idx <- numeric()
139  }
140
141  vattrs <- .Call(C_R_igraph_mybracket2, graph, 9L, 3L)
142  for (i in seq(attrs)) { vattrs[[nam[i]]][idx] <- attrs[[nam[i]]] }
143
144  .Call(C_R_igraph_mybracket2_set, graph, 9L, 3L, vattrs)
145}
146
147#' Delete edges from a graph
148#'
149#' @param graph The input graph.
150#' @param edges The edges to remove, specified as an edge sequence.
151#' @return The graph, with the edges removed.
152#'
153#' @aliases delete.edges
154#' @family functions for manipulating graph structure
155#'
156#' @export
157#' @examples
158#' g <- make_ring(10) %>%
159#'   delete_edges(seq(1, 9, by = 2))
160#' g
161#'
162#' g <- make_ring(10) %>%
163#'   delete_edges("10|1")
164#' g
165
166delete_edges <- function(graph, edges) {
167  if (!is_igraph(graph)) {
168    stop("Not a graph object")
169  }
170  on.exit( .Call(C_R_igraph_finalizer) )
171  .Call(C_R_igraph_delete_edges, graph, as.igraph.es(graph, edges)-1)
172}
173
174#' Delete vertices from a graph
175#'
176#' @param graph The input graph.
177#' @param v The vertices to remove, a vertex sequence.
178#' @return The graph, with the vertices removed.
179#'
180#' @aliases delete.vertices
181#' @family functions for manipulating graph structure
182#'
183#' @export
184#' @examples
185#' g <- make_ring(10) %>%
186#'   set_vertex_attr("name", value = LETTERS[1:10])
187#' g
188#' V(g)
189#'
190#' g2 <- delete_vertices(g, c(1,5)) %>%
191#'   delete_vertices("B")
192#' g2
193#' V(g2)
194
195delete_vertices <- function(graph, v) {
196  if (!is_igraph(graph)) {
197    stop("Not a graph object")
198  }
199  on.exit( .Call(C_R_igraph_finalizer) )
200  .Call(C_R_igraph_delete_vertices, graph, as.igraph.vs(graph, v)-1)
201}
202
203###################################################################
204# Structure query
205###################################################################
206
207#' The size of the graph (number of edges)
208#'
209#' \code{ecount} of an alias of this function.
210#'
211#' @param graph The graph.
212#' @return Numeric scalar, the number of edges.
213#'
214#' @aliases ecount
215#' @family structural queries
216#'
217#' @export
218#' @examples
219#' g <- sample_gnp(100, 2/100)
220#' gsize(g)
221#'
222#' # Number of edges in a G(n,p) graph
223#' replicate(100, sample_gnp(10, 1/2), simplify = FALSE) %>%
224#'   vapply(gsize, 0) %>%
225#'   hist()
226
227gsize <- function(graph) {
228  if (!is_igraph(graph)) {
229    stop("Not a graph object")
230  }
231  on.exit( .Call(C_R_igraph_finalizer) )
232  .Call(C_R_igraph_ecount, graph)
233}
234
235#' Neighboring (adjacent) vertices in a graph
236#'
237#' A vertex is a neighbor of another one (in other words, the two
238#' vertices are adjacent), if they are incident to the same edge.
239#'
240#' @param graph The input graph.
241#' @param v The vertex of which the adjacent vertices are queried.
242#' @param mode Whether to query outgoing (\sQuote{out}), incoming
243#'   (\sQuote{in}) edges, or both types (\sQuote{all}). This is
244#'   ignored for undirected graphs.
245#' @return A vertex sequence containing the neighbors of the input vertex.
246#'
247#' @family structural queries
248#'
249#' @export
250#' @examples
251#' g <- make_graph("Zachary")
252#' n1 <- neighbors(g, 1)
253#' n34 <- neighbors(g, 34)
254#' intersection(n1, n34)
255
256neighbors <- function(graph, v, mode = c("out", "in", "all", "total")) {
257  if (!is_igraph(graph)) {
258    stop("Not a graph object")
259  }
260  if (is.character(mode)) {
261    mode <- igraph.match.arg(mode)
262    mode <- switch(mode, "out"=1, "in"=2, "all"=3, "total"=3)
263  }
264  on.exit( .Call(C_R_igraph_finalizer) )
265  res <- .Call(C_R_igraph_neighbors, graph, as.igraph.vs(graph, v)-1,
266               as.numeric(mode))
267  V(graph)[res + 1]
268}
269
270#' Incident edges of a vertex in a graph
271#'
272#' @param graph The input graph.
273#' @param v The vertex of which the incident edges are queried.
274#' @param mode Whether to query outgoing (\sQuote{out}), incoming
275#'   (\sQuote{in}) edges, or both types (\sQuote{all}). This is
276#'   ignored for undirected graphs.
277#' @return An edge sequence containing the incident edges of
278#'   the input vertex.
279#'
280#' @family structural queries
281#'
282#' @export
283#' @examples
284#' g <- make_graph("Zachary")
285#' incident(g, 1)
286#' incident(g, 34)
287
288incident <- function(graph, v, mode=c("all", "out", "in", "total")) {
289  if (!is_igraph(graph)) {
290    stop("Not a graph object")
291  }
292  if (is_directed(graph)) {
293    mode <- igraph.match.arg(mode)
294    mode <- switch(mode, "out"=1, "in"=2, "all"=3, "total"=3)
295  } else {
296    mode=1
297  }
298  on.exit( .Call(C_R_igraph_finalizer) )
299  res <- .Call(C_R_igraph_incident, graph, as.igraph.vs(graph, v)-1,
300               as.numeric(mode)) + 1L
301
302  if (igraph_opt("return.vs.es")) res <- create_es(graph, res)
303
304  res
305}
306
307#' Check whether a graph is directed
308#'
309#' @param graph The input graph
310#' @return Logical scalar, whether the graph is directed.
311#'
312#' @aliases is.directed
313#' @family structural queries
314#'
315#' @export
316#' @examples
317#' g <- make_ring(10)
318#' is_directed(g)
319#'
320#' g2 <- make_ring(10, directed = TRUE)
321#' is_directed(g2)
322
323is_directed <- function(graph) {
324  if (!is_igraph(graph)) {
325    stop("Not a graph object")
326  }
327  on.exit( .Call(C_R_igraph_finalizer) )
328  .Call(C_R_igraph_is_directed, graph)
329}
330
331#' Incident vertices of some graph edges
332#'
333#' @param graph The input graph
334#' @param es The sequence of edges to query
335#' @param names Whether to return vertex names or
336#'   numeric vertex ids. By default vertex names are used.
337#' @return A two column matrix of vertex names or vertex ids.
338#'
339#' @aliases get.edges get.edge
340#' @family structural queries
341#'
342#' @export
343#' @importFrom stats na.omit
344#' @examples
345#' g <- make_ring(5)
346#' ends(g, E(g))
347
348ends <- function(graph, es, names = TRUE) {
349
350  if (!is_igraph(graph)) {
351    stop("Not a graph object")
352  }
353
354  es2 <- as.igraph.es(graph, na.omit(es)) - 1
355  res <- matrix(NA_integer_, ncol = length(es), nrow = 2)
356
357  on.exit( .Call(C_R_igraph_finalizer) )
358
359  if (length(es) == 1) {
360    res[, !is.na(es)] <- .Call(C_R_igraph_get_edge, graph, es2) + 1
361
362  } else  {
363    res[, !is.na(es)] <- .Call(C_R_igraph_edges, graph, es2) + 1
364  }
365
366  if (names && is_named(graph)) {
367    res <- vertex_attr(graph, "name")[res]
368  }
369
370  matrix(res, ncol = 2, byrow = TRUE)
371}
372
373#' @export
374
375get.edges <- function(graph, es) {
376  ends(graph, es, names = FALSE)
377}
378
379
380#' Find the edge ids based on the incident vertices of the edges
381#'
382#' Find the edges in an igraph graph that have the specified end points. This
383#' function handles multi-graph (graphs with multiple edges) and can consider
384#' or ignore the edge directions in directed graphs.
385#'
386#' igraph vertex ids are natural numbers, starting from one, up to the number
387#' of vertices in the graph. Similarly, edges are also numbered from one, up to
388#' the number of edges.
389#'
390#' This function allows finding the edges of the graph, via their incident
391#' vertices.
392#'
393#' @param graph The input graph.
394#' @param vp The incident vertices, given as vertex ids or symbolic vertex
395#' names. They are interpreted pairwise, i.e. the first and second are used for
396#' the first edge, the third and fourth for the second, etc.
397#' @param directed Logical scalar, whether to consider edge directions in
398#' directed graphs. This argument is ignored for undirected graphs.
399#' @param error Logical scalar, whether to report an error if an edge is not
400#' found in the graph. If \code{FALSE}, then no error is reported, and zero is
401#' returned for the non-existant edge(s).
402#' @param multi Logical scalar, whether to handle multiple edges properly. If
403#' \code{FALSE}, and a pair of vertices are given twice (or more), then always
404#' the same edge id is reported back for them. If \code{TRUE}, then the edge
405#' ids of multiple edges are correctly reported.
406#' @return A numeric vector of edge ids, one for each pair of input vertices.
407#' If there is no edge in the input graph for a given pair of vertices, then
408#' zero is reported. (If the \code{error} argument is \code{FALSE}.)
409#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
410#' @export
411#' @family structural queries
412#'
413#' @examples
414#'
415#' g <- make_ring(10)
416#' ei <- get.edge.ids(g, c(1,2, 4,5))
417#' E(g)[ei]
418#'
419#' ## non-existant edge
420#' get.edge.ids(g, c(2,1, 1,4, 5,4))
421#'
422get.edge.ids <- function(graph, vp, directed=TRUE, error=FALSE, multi=FALSE) {
423  if (!is_igraph(graph)) {
424    stop("Not a graph object")
425  }
426  on.exit( .Call(C_R_igraph_finalizer) )
427  .Call(C_R_igraph_get_eids, graph, as.igraph.vs(graph, vp)-1,
428        as.logical(directed), as.logical(error), as.logical(multi)) + 1
429}
430
431
432#' Order (number of vertices) of a graph
433#'
434#' @param graph The graph
435#' @return Number of vertices, numeric scalar.
436#'
437#' @aliases vcount
438#' @family structural queries
439#'
440#' @export
441#' @examples
442#' g <- make_ring(10)
443#' gorder(g)
444
445gorder <- gorder
446
447#' Adjacent vertices of multiple vertices in a graph
448#'
449#' This function is similar to \code{\link{neighbors}}, but it queries
450#' the adjacent vertices for multiple vertices at once.
451#'
452#' @param graph Input graph.
453#' @param v The vertices to query.
454#' @param mode Whether to query outgoing (\sQuote{out}), incoming
455#'   (\sQuote{in}) edges, or both types (\sQuote{all}). This is
456#'   ignored for undirected graphs.
457#' @return A list of vertex sequences.
458#'
459#' @family structural queries
460#' @export
461#' @examples
462#' g <- make_graph("Zachary")
463#' adjacent_vertices(g, c(1, 34))
464
465adjacent_vertices <- function(graph, v,
466                               mode = c("out", "in", "all", "total")) {
467
468  if (!is_igraph(graph)) stop("Not a graph object")
469
470  vv <- as.igraph.vs(graph, v) - 1
471  mode <- switch(match.arg(mode), "out" = 1, "in" = 2, "all" = 3, "total" = 3)
472
473  on.exit(.Call(C_R_igraph_finalizer) )
474
475  res <- .Call(C_R_igraph_adjacent_vertices, graph, vv, mode)
476
477  if (igraph_opt("return.vs.es")) {
478    res <- lapply(res, function(x) create_vs(graph, x + 1))
479  }
480
481  if (is_named(graph)) names(res) <- V(graph)$name[vv + 1]
482
483  res
484}
485
486#' Incident edges of multiple vertices in a graph
487#'
488#' This function is similar to \code{\link{incident}}, but it
489#' queries multiple vertices at once.
490#'
491#' @param graph Input graph.
492#' @param v The vertices to query
493#' @param mode Whether to query outgoing (\sQuote{out}), incoming
494#'   (\sQuote{in}) edges, or both types (\sQuote{all}). This is
495#'   ignored for undirected graphs.
496#' @return A list of edge sequences.
497#'
498#' @family structural queries
499#' @export
500#' @examples
501#' g <- make_graph("Zachary")
502#' incident_edges(g, c(1, 34))
503
504incident_edges <- function(graph, v,
505                           mode = c("out", "in", "all", "total")) {
506
507  if (!is_igraph(graph)) stop("Not a graph object")
508
509  vv <- as.igraph.vs(graph, v) - 1
510  mode <- switch(match.arg(mode), "out" = 1, "in" = 2, "all" = 3, "total" = 3)
511
512  on.exit(.Call(C_R_igraph_finalizer) )
513
514  res <- .Call(C_R_igraph_incident_edges, graph, vv, mode)
515
516  if (igraph_opt("return.vs.es")) {
517    res <- lapply(res, function(x) create_es(graph, x + 1))
518  }
519
520  if (is_named(graph)) names(res) <- V(graph)$name[vv + 1]
521
522  res
523}
524