Cycles, motifs and epidemics
Cycles and cycle bases, acyclic graphs and feedback sets, Eulerian paths, triangles and census counts, motifs, graphlets, and the SIR epidemic model.
30 entries: 27 functions and 3 types. Part of the Open Graph reference; All functions lists every entry on one page, with a filter. The badges say how each entry is checked.
Types
cycle_handler_ttype#
Exact
The type of the function simple_cycles_callback calls for each cycle.
typedef og_error_t og_cycle_handler_t(
const og_vector_int_t *vertices,
const og_vector_int_t *edges,
void *arg
);
The handler receives one cycle: its vertex ids and its edge ids in walking order, and the arg pointer given to simple_cycles_callback. Both vectors are owned by the search and change after the call returns, so the handler must copy anything it keeps and must not modify or destroy them.
- Parameters
verticesThe cycle's vertex ids in walking order (read only, valid during the call).edgesThe cycle's edge ids in walking order (read only, valid during the call).argThe caller's pointer, unchanged.
- Returns
OG_SUCCESSto go on,OG_STOPto stop the search quietly, any other code to stop it with that error.- Checked
- Same signature as igraph 1.0's type, with og_ names.
- C name
og_cycle_handler_t- In notebooks
Og.CycleHandler- igraph 1.0
igraph_cycle_handler_t- See also
simple_cycles_callback
motifs_handler_ttype#
Exact
The type of the function motifs_randesu_callback calls for each motif.
typedef og_error_t og_motifs_handler_t(
const og_t *graph,
const og_vector_int_t *vids,
og_int_t isoclass,
void *extra
);
The handler receives the graph searched, the vertex ids of one motif and the motif's isomorphism class, plus the extra pointer given to motifs_randesu_callback. vids belongs to the search: read it during the call, copy what you keep, never modify it.
- Parameters
graphThe graph being searched.vidsThe motif's vertex ids (read only, valid during the call).isoclassThe motif's isomorphism class, asisoclassnumbers it.extraThe caller's pointer, unchanged.
- Returns
OG_SUCCESSto go on,OG_STOPto stop quietly, any other code to stop with that error.- Checked
- Same signature as igraph 1.0's type, with og_ names.
- C name
og_motifs_handler_t- In notebooks
Og.MotifsHandler- igraph 1.0
igraph_motifs_handler_t- See also
motifs_randesu_callback
sir_ttype#
Exact
One simulated SIR epidemic: event times and compartment sizes.
typedef struct og_sir_t {
og_vector_t times;
og_vector_int_t no_s, no_i, no_r;
} og_sir_t;
All four vectors have the same length, one entry per event plus the start: entry 0 is time 0 with one infected vertex, and the last entry is the time the last infected vertex recovered. no_s + no_i + no_r equals |V| at every entry. sir allocates these structs; sir_destroy releases their vectors.
- Parameters
timesEvent times, increasing, starting at 0.no_sSusceptible vertices after each event.no_iInfected vertices after each event.no_rRecovered vertices after each event.
- Checked
- The four fields igraph 1.0's type has, with og_ vector types.
- C name
og_sir_t- In notebooks
Og.SirType- igraph 1.0
igraph_sir_t- See also
sir,sir_destroy
Cycles
find_cycle#
Unit tests
Returns one cycle of the graph, or two empty vectors when it has none.
og_error_t og_find_cycle(
const og_t *graph,
og_vector_int_t *vertices,
og_vector_int_t *edges,
og_neimode_t mode
);
A depth-first search starts from vertex 0, then from each vertex not yet visited, and stops at the first edge that leads back to a vertex still on the search stack (a self-loop counts). That vertex and the part of the stack above it are the cycle: vertices lists them in walking order, and edges[i] is the edge from vertices[i] to the next vertex, the last one closing the cycle at vertices[0].
In a directed graph, mode OG_OUT follows edge directions, OG_IN walks them backwards and OG_ALL ignores them; in an undirected graph the mode does not matter. The edge just used to arrive is never walked back, but a parallel copy of it may be, so two parallel undirected edges form a cycle of length 2.
Which cycle is returned is Open Graph's own choice (the first one this search meets). Use simple_cycles to list them all.
- Parameters
graphThe graph.verticesOutput, may be NULL: the cycle's vertex ids in walking order (emptied when there is no cycle).edgesOutput, may be NULL: the cycle's edge ids in walking order (emptied when there is no cycle).modeOG_OUT,OG_INorOG_ALL; ignored for undirected graphs.
- Returns
OG_SUCCESS, also when the graph has no cycle.- Errors
OG_EINVMODEmode is notOG_OUT,OG_INorOG_ALL.OG_ENOMEMNot enough memory.
- Complexity
- O(|V| + |E|).
- Checked
- python-igraph exposes no find_cycle, so the cycle chosen is not compared. Checked by properties in all three modes on the 410 DAG fixture graphs and a third of the simple-cycle fixtures (loops, multi-edges, both directednesses): a cycle comes back exactly when one exists, and it is a valid closed walk with matching edges.
- C name
og_find_cycle- In notebooks
Og.FindCycle- igraph 1.0
igraph_find_cycle- See also
simple_cycles,is_dag,is_acyclic
simple_cycles#
Exact
Lists every simple cycle of the graph, as vertex lists, edge lists or both.
og_error_t og_simple_cycles(
const og_t *graph,
og_vector_int_list_t *vertices,
og_vector_int_list_t *edges,
og_neimode_t mode,
og_int_t min_cycle_length,
og_int_t max_cycle_length,
og_int_t max_results
);
A simple cycle repeats no vertex. Each one is reported once. Lengths are counted in edges: a self-loop is a cycle of length 1, two parallel undirected edges (or u->v and v->u in a directed graph) a cycle of length 2. vertices and edges get one vector per cycle, at the same position for the same cycle, both in walking order.
The search is Johnson's circuit enumeration (SIAM J. Comput., 1975): one start vertex at a time, kept inside the start's strongly connected part (for undirected searches, its 2-edge-connected part), with blocking so that no dead end is walked twice. With a maximum length it uses backtracking pruned by breadth-first distances back to the start instead, since blocking is not valid under a length bound.
The order of the cycles and of the ids inside each is python-igraph 1.0's: start vertices in increasing id, neighbours in adjacency order. In a directed graph mode OG_ALL treats the edges as undirected. python-igraph 1.0 misses some directed cycles; Open Graph reports all of them, with python-igraph's list a subsequence of its own.
- Parameters
graphThe graph.verticesOutput list, may be NULL: one vertex-id vector per cycle. Cleared first.edgesOutput list, may be NULL: one edge-id vector per cycle. Cleared first.modeOG_OUT,OG_INorOG_ALL; ignored for undirected graphs.min_cycle_lengthShortest cycle to report, in edges; negative means no lower bound.max_cycle_lengthLongest cycle to report, in edges; negative means no upper bound. 0, or a maximum below the minimum, gives no cycles.max_resultsStop after this many cycles;OG_UNLIMITED(any negative value) keeps them all, 0 returns none.
- Returns
OG_SUCCESS.- Errors
OG_EINVALmode is notOG_OUT,OG_INorOG_ALL(EINVAL, as python-igraph reports it, not EINVMODE).OG_ENOMEMNot enough memory.
- Complexity
- Without a maximum length O((|V| + |E|)(c + 1)) for c cycles (Johnson's bound). The number of cycles itself can grow exponentially with the graph; with a maximum length the backtracking search may take longer on dense graphs.
- Checked
- Identical lists in python-igraph's order on 860 fixture cases (all modes, length bounds, loops, multi-edges, 0-300 vertices) and 13,700 more random cases. In 30 directed fixture cases python-igraph 1.0 misses cycles; there an independent brute-force enumeration is the reference and Open Graph returns every cycle. Benchmark (22k-60k edges): 0.04-0.20x python-igraph's time.
- C name
og_simple_cycles- In notebooks
Og.SimpleCycles- igraph 1.0
igraph_simple_cycles- See also
simple_cycles_callback,find_cycle,minimum_cycle_basis
#include "og.h"
#include <stdio.h>
int main(void) {
og_t g;
og_vector_int_list_t cycles;
og_int_t i, j;
/* directed square 0->1->2->3->0 with the chord 1->3 */
og_small(&g, 4, OG_DIRECTED, 0,1, 1,2, 2,3, 3,0, 1,3, -1);
og_vector_int_list_init(&cycles, 0);
og_simple_cycles(&g, &cycles, NULL, OG_OUT, -1, -1, OG_UNLIMITED);
for (i = 0; i < og_vector_int_list_size(&cycles); i++) {
const og_vector_int_t *c = og_vector_int_list_get_ptr(&cycles, i);
for (j = 0; j < og_vector_int_size(c); j++) {
printf("%" OG_PRId " ", OG_VECTOR(*c)[j]);
}
printf("\n"); /* 0 1 2 3, then 0 1 3 */
}
og_vector_int_list_destroy(&cycles);
og_destroy(&g);
return 0;
}
simple_cycles_callback#
Exact
Calls a function for every simple cycle instead of collecting them.
og_error_t og_simple_cycles_callback(
const og_t *graph,
og_neimode_t mode,
og_int_t min_cycle_length,
og_int_t max_cycle_length,
og_cycle_handler_t *callback,
void *arg
);
The same search as simple_cycles: the same cycles, in the same order, with the same length bounds and modes. Each cycle is handed to callback as two vectors (vertex ids and edge ids in walking order) that belong to the search and are valid only during the call; copy what you want to keep.
The handler returns OG_SUCCESS to go on, OG_STOP to end the search with success, or any other code to end it with that error. This is the way to process very many cycles without holding them in memory, or to stop at the first one that meets a condition.
- Parameters
graphThe graph.modeOG_OUT,OG_INorOG_ALL; ignored for undirected graphs.min_cycle_lengthShortest cycle to report, in edges; negative means no lower bound.max_cycle_lengthLongest cycle to report, in edges; negative means no upper bound.callbackThe handler, called once per cycle. Must not be NULL.argPassed unchanged to every call of the handler.
- Returns
OG_SUCCESSwhen the search finished or the handler returnedOG_STOP; otherwise the handler's error code.- Errors
OG_EINVALcallback is NULL, or mode is notOG_OUT,OG_INorOG_ALL.- handler's code: The handler returned an error code other than
OG_STOP. OG_ENOMEMNot enough memory.
- Complexity
- As
simple_cycles, plus the handler's own time. - Checked
- Same engine and order as
simple_cycles, compared on the same fixtures;OG_STOPends quietly and other codes are passed on (tested). - C name
og_simple_cycles_callback- In notebooks
Og.SimpleCyclesCallback- igraph 1.0
igraph_simple_cycles_callback- See also
simple_cycles,cycle_handler_t
fundamental_cycles#
Exact
Returns the cycles closed by the non-tree edges of a breadth-first tree: a cycle basis.
og_error_t og_fundamental_cycles(
const og_t *graph,
const og_vector_t *weights,
og_vector_int_list_t *result,
og_int_t start_vid,
og_real_t bfs_cutoff
);
Edge directions are ignored. A breadth-first search builds a spanning tree; each edge outside the tree closes exactly one cycle with the tree paths from its ends to their lowest common ancestor. Searching every component gives |E| - |V| + c cycles (c components), a basis of the cycle space. Each cycle is a vector of edge ids: the closing edge, the tree path from one end up to the common ancestor, then the path down to the other end. A self-loop is a cycle of one edge, two parallel edges one of two.
start_vid >= 0 searches only that vertex's component, from that vertex; a negative start_vid searches every component from its smallest vertex, in that order. A bfs_cutoff >= 0 stops the search at that depth, so only cycles near the root are found (then not a full basis); negative means no limit. The order of the cycles and of the edges inside each is python-igraph 1.0's.
- Parameters
graphThe graph (directions ignored).weightsNot used (as in igraph 1.0); pass NULL.resultOutput list: one edge-id vector per cycle. Cleared first.start_vidRoot vertex, or negative for every component.bfs_cutoffDepth limit of the search; negative for none.
- Returns
OG_SUCCESS.- Errors
OG_EINVALstart_vid is not a vertex of the graph (EINVAL, the code python-igraph 1.0 reports, not EINVVID).OG_ENOMEMNot enough memory.
- Complexity
- O(|V| + |E|) for the search, plus the total length of the cycles returned.
- Checked
- 269 fixture graphs x start (none or a random vertex) x cutoff (none, 0 to 3): identical cycles in identical order, edge order inside each cycle included.
- C name
og_fundamental_cycles- In notebooks
Og.FundamentalCycles- igraph 1.0
igraph_fundamental_cycles- See also
minimum_cycle_basis,simple_cycles
minimum_cycle_basis#
Exact
Returns a cycle basis of the smallest total length (lengths in edges).
og_error_t og_minimum_cycle_basis(
const og_t *graph,
const og_vector_t *weights,
og_vector_int_list_t *result,
og_real_t bfs_cutoff,
og_bool_t complete,
og_bool_t use_cycle_order
);
Edge directions are ignored and every edge has length 1 (weights are not used). The basis has |E| - |V| + c cycles for c connected components. Candidates are the fundamental cycles of breadth-first trees grown from candidate roots (vertices of degree 2 or more that have degree 3 or more, a self-loop, or were not reached by an earlier root's search); they are sorted by length, then lexicographically by their sorted edge ids, and taken in that order whenever they are independent over GF(2) of those already taken (Horton, SIAM J. Comput., 1987).
Each cycle is a vector of edge ids, in increasing order, or in the order of a walk around the cycle starting with its smallest edge when use_cycle_order is true. The cycles come sorted by length, then lexicographically. A bfs_cutoff >= 0 limits the depth of each search, and the basis can then be incomplete; with complete true the first root of each component searches without the limit, so the basis is always complete. A negative cutoff means no limit.
- Parameters
graphThe graph (directions ignored).weightsNot used (as in igraph 1.0); pass NULL.resultOutput list: one edge-id vector per basis cycle. Cleared first.bfs_cutoffDepth limit of each breadth-first search; negative for none.completeWith a cutoff: still return a complete basis.use_cycle_ordertrue: edges in walking order around each cycle; false: ascending ids.
- Returns
OG_SUCCESS.- Errors
OG_ENOMEMNot enough memory.
- Complexity
- Polynomial: one breadth-first search per candidate root, O(|V| (|V| + |E|)) in all, then Gaussian elimination over GF(2) of the sorted candidates.
- Checked
- 269 fixture graphs (62 of them multigraphs) x cutoff (none, 1, 2) x complete x use_cycle_order: identical bases, order and edge order; 2,100 random multigraphs x 18 argument sets identical.
- C name
og_minimum_cycle_basis- In notebooks
Og.MinimumCycleBasis- igraph 1.0
igraph_minimum_cycle_basis- See also
fundamental_cycles,simple_cycles
Acyclic graphs and feedback sets
is_dag#
Exact
Tests for a DAG: a directed graph without directed cycles.
og_error_t og_is_dag(const og_t *graph, og_bool_t *res);
True when the graph is directed and has no directed cycle. A self-loop is a cycle, so a graph with one is not a DAG. Undirected graphs always give false; is_acyclic answers the corresponding question for them.
The test is Kahn's (CACM, 1962): repeatedly remove a vertex with no incoming edge left; the graph is a DAG exactly when every vertex gets removed. The answer is stored in the graph's property cache, so asking again costs nothing until the graph changes.
- Parameters
graphThe graph.resOutput: true for a DAG.
- Returns
OG_SUCCESS.- Errors
OG_ENOMEMNot enough memory.
- Complexity
- O(|V| + |E|); O(1) when the answer is cached.
- Checked
- 410 fixture graphs (self-loops, undirected graphs), the cached answer and its reset after a change. Benchmark (20k vertices, 100k edges): 0.37x (a DAG) and 0.48x (not a DAG) python-igraph's time.
- C name
og_is_dag- In notebooks
Og.IsDag- igraph 1.0
igraph_is_dag- See also
is_acyclic,topological_sorting,find_cycle,feedback_arc_set
is_acyclic#
Exact
Tells whether the graph has no cycle at all, directed or undirected.
og_error_t og_is_acyclic(const og_t *graph, og_bool_t *res);
For a directed graph this is is_dag: no directed cycle. For an undirected graph it asks whether the graph is a forest, so a self-loop or two parallel edges already make it cyclic. Both answers are kept in the graph's property cache.
- Parameters
graphThe graph.resOutput: true when the graph has no cycle.
- Returns
OG_SUCCESS.- Errors
OG_ENOMEMNot enough memory.
- Complexity
- O(|V| + |E|); O(1) when the answer is cached.
- Checked
- 410 fixture graphs, directed and undirected (loops and multi-edges included), cached and recomputed. Undirected graphs use
is_forest. - C name
og_is_acyclic- In notebooks
Og.IsAcyclic- igraph 1.0
igraph_is_acyclic- See also
is_dag,is_forest,find_cycle
topological_sorting#
Exact
Orders the vertices of a DAG so that every edge points the same way along the order.
og_error_t og_topological_sorting(
const og_t *graph,
og_vector_int_t *res,
og_neimode_t mode
);
With OG_OUT every edge goes from an earlier to a later vertex of res (sources first); with OG_IN every edge goes from a later to an earlier one (sinks first). Self-loops are ignored, so a graph whose only cycles are self-loops can still be sorted.
The order is Kahn's with a first-in, first-out queue: the vertices with no incoming edge (OG_OUT; no outgoing edge for OG_IN) enter the queue in increasing id, and taking a vertex out releases its successors in adjacency order. This is exactly python-igraph 1.0's order. A graph with a directed cycle has no such order: res is then emptied and the function fails.
- Parameters
graphA directed graph.resOutput: the |V| vertex ids in order.modeOG_OUT(edges point forward) orOG_IN(edges point backward).
- Returns
OG_SUCCESS.- Errors
OG_EINVALThe graph is undirected (checked before the mode).OG_EINVALmode isOG_ALL.OG_EINVALThe graph has a directed cycle other than a self-loop; res is emptied.OG_EINVMODEmode is not a known mode.OG_ENOMEMNot enough memory.
- Complexity
- O(|V| + |E|).
- Checked
- Identical orders for
OG_OUTandOG_INon 410 fixture graphs. Benchmark (20k vertices, 100k edges): 0.21x (OUT) and 0.23x (IN) python-igraph's time. - C name
og_topological_sorting- In notebooks
Og.TopologicalSorting- igraph 1.0
igraph_topological_sorting- See also
is_dag,feedback_arc_set
#include "og.h"
#include <stdio.h>
int main(void) {
og_t g;
og_vector_int_t order;
og_bool_t dag;
og_int_t i;
og_small(&g, 5, OG_DIRECTED, 3,1, 1,0, 3,2, 2,0, 4,2, -1);
og_is_dag(&g, &dag);
og_vector_int_init(&order, 0);
if (dag) {
og_topological_sorting(&g, &order, OG_OUT);
for (i = 0; i < og_vector_int_size(&order); i++) {
printf("%" OG_PRId " ", OG_VECTOR(order)[i]); /* 3 4 1 2 0 */
}
}
og_vector_int_destroy(&order);
og_destroy(&g);
return 0;
}
feedback_arc_set#
Exact Partial
Finds a set of edges whose removal leaves the graph without cycles, exactly or with the Eades heuristic.
og_error_t og_feedback_arc_set(
const og_t *graph,
og_vector_int_t *result,
const og_vector_t *weights,
og_fas_algorithm_t algo
);
A feedback arc set meets every cycle: removing it leaves a DAG (directed graphs) or a forest (undirected graphs). The aim is the lightest such set (with weights) or the smallest (without). result gets the edge ids of the set in increasing order. Self-loops are always in the set.
Undirected graphs: the edges outside a maximum-weight spanning forest (without weights, a breadth-first forest), which is always optimal. Directed graphs, by algo: OG_FAS_APPROX_EADES orders the vertices greedily (Eades, Lin and Smyth, 1993: sinks to the back, sources to the front, otherwise the vertex with the largest out-weight minus in-weight) and returns the edges that point backwards: fast, not always optimal. OG_FAS_EXACT_IP and OG_FAS_EXACT_IP_CG (the same method) return a minimum-weight set: a minimum hitting set of a family of cycles that grows (shortest cycles, found by breadth-first search) until what is left is acyclic, solved per strongly connected part by Open Graph's own branch and bound, with no external solver. OG_FAS_EXACT_IP_TI reaches the same minimum through the vertex-ordering form of the problem.
With the hitting-set methods an edge of negative weight is always taken (it only lowers the total) and a DAG gives an empty set. Among equally light sets they return the colexicographically smallest by edge id, the choice python-igraph makes in the cases checked. The problem is NP-hard: the exact methods can take very long on large, dense strongly connected parts.
- Parameters
graphThe graph.resultOutput, may be NULL: the edge ids of the set, ascending.weightsEdge weights, or NULL for all 1. Length |E|, finite; negative values are allowed.algoog_fas_algorithm_t:OG_FAS_APPROX_EADES,OG_FAS_EXACT_IP,OG_FAS_EXACT_IP_CGorOG_FAS_EXACT_IP_TI. Not used for undirected graphs (always optimal there).
- Returns
OG_SUCCESS.- Errors
OG_EINVALalgo is not one of the four methods.OG_EINVALweights does not have |E| entries, or holds NaN or an infinity.OG_ENOMEMNot enough memory.
- Complexity
- Eades: O((|V| + |E|) log |V|) with its heap. Exact methods: exponential in the worst case (NP-hard), fast when the strongly connected parts are small. Undirected graphs: O(|V| + |E|) without weights, O(|E| log |E|) with weights.
- Checked
- Coverage status partial. Exact methods always reach igraph's minimum weight; the same set as igraph's on 230/230 fixture graphs and 257/260 tie-heavy ones (EXACT_IP_TI: same weight on 201/201, same set on 164/201). Eades: identical on every loop-free graph and on 517 of 525 graphs with self-loops; in 3 of the rest python-igraph's own answer is not a feedback arc set, while Open Graph's always is. All 430 undirected cases identical.
- C name
og_feedback_arc_set- In notebooks
Og.FeedbackArcSet- igraph 1.0
igraph_feedback_arc_set- See also
feedback_vertex_set,is_dag,topological_sorting,minimum_spanning_tree
feedback_vertex_set#
Within tolerance
Finds a minimum-weight set of vertices that meets every cycle.
og_error_t og_feedback_vertex_set(
const og_t *graph,
og_vector_int_t *result,
const og_vector_t *vertex_weights,
og_fvs_algorithm_t algo
);
Removing the vertices of result leaves the graph acyclic: a DAG for a directed graph (edge directions respected), a forest for an undirected one. The set is a true minimum of the total vertex weight (of the vertex count without weights). result lists the vertex ids in increasing order.
The search is Open Graph's own branch and bound for a minimum hitting set over cycles generated as needed: in directed graphs a shortest cycle through each vertex, in undirected graphs, for each edge, a shortest path between its ends that avoids it. Each strongly connected part (directed) or connected part (undirected) is solved on its own. A vertex with a self-loop, and a vertex of negative weight, is always taken; an acyclic graph gives an empty set.
When several sets are optimal, the one returned can differ from igraph's, whose choice comes from its integer-programming solver.
- Parameters
graphThe graph.resultOutput, may be NULL: the vertex ids of the set, ascending.vertex_weightsVertex weights, or NULL for all 1. Length |V|, finite.algoog_fvs_algorithm_t:OG_FVS_EXACT_IP, the only method.
- Returns
OG_SUCCESS.- Errors
OG_EINVALalgo is notOG_FVS_EXACT_IP.OG_EINVALvertex_weights does not have |V| entries, or holds NaN or an infinity.OG_ENOMEMNot enough memory.
- Complexity
- Exponential in the worst case (the problem is NP-hard); fast when the cyclic parts are small.
- Checked
- Minimum weight equal to igraph's on all 240 fixture graphs (directed and undirected, loops, multi-edges, weights); the same set on 231 of them, other optima on the rest. Parity here means an equal optimal weight.
- C name
og_feedback_vertex_set- In notebooks
Og.FeedbackVertexSet- igraph 1.0
igraph_feedback_vertex_set- See also
feedback_arc_set,is_acyclic
Eulerian paths and cycles
is_eulerian#
Exact
Tells whether the graph has an Eulerian path and whether it has an Eulerian cycle.
og_error_t og_is_eulerian(
const og_t *graph,
og_bool_t *has_path,
og_bool_t *has_cycle
);
An Eulerian path uses every edge exactly once; an Eulerian cycle is such a path that ends where it started. All edges must lie in one connected part (isolated vertices do not matter; in a directed graph connectivity ignores directions). Undirected: a cycle needs every degree even, a path at most two odd degrees. Directed: a cycle needs every vertex's in-degree equal to its out-degree, a path allows one vertex with one more out-edge and one with one more in-edge. A self-loop adds 2 to its vertex's degree.
A graph without edges has both (the empty walk).
- Parameters
graphThe graph.has_pathOutput, may be NULL: true when an Eulerian path exists.has_cycleOutput, may be NULL: true when an Eulerian cycle exists.
- Returns
OG_SUCCESS.- Errors
OG_ENOMEMNot enough memory.
- Complexity
- O(|V| + |E|).
- Checked
- python-igraph has no Eulerian functions: 26 cases recorded from the igraph 1.0.1 C library used as a black box, plus 600 random graphs against the degree and connectivity conditions.
- C name
og_is_eulerian- In notebooks
Og.IsEulerian- igraph 1.0
igraph_is_eulerian- See also
eulerian_cycle,eulerian_path
eulerian_cycle#
Exact
Returns an Eulerian cycle: a closed walk that uses every edge exactly once.
og_error_t og_eulerian_cycle(
const og_t *graph,
og_vector_int_t *edge_res,
og_vector_int_t *vertex_res
);
Hierholzer's algorithm with an explicit stack. The walk starts at the lowest vertex that has an edge and, at each vertex, takes the first unused edge in incidence order (undirected graphs: the edges listed by incident, directed graphs: the out-edges). edge_res gets the |E| edge ids in walking order and vertex_res the |E| + 1 vertices passed, the first repeated at the end. These rules give igraph 1.0's walks on every recorded case. A graph without edges gives two empty vectors.
- Parameters
graphThe graph.edge_resOutput, may be NULL: the edge ids in walking order.vertex_resOutput, may be NULL: the vertex ids in walking order, start repeated at the end.
- Returns
OG_SUCCESS.- Errors
OG_ENOSOLThe graph has no Eulerian cycle.OG_ENOMEMNot enough memory.
- Complexity
- O(|V| + |E|).
- Checked
- Identical vertex and edge walks on the cases recorded from the igraph 1.0.1 C library (loops, multi-edges, isolated vertices; 10 of them predicted before recording); 600 random walks validated.
- C name
og_eulerian_cycle- In notebooks
Og.EulerianCycle- igraph 1.0
igraph_eulerian_cycle- See also
eulerian_path,is_eulerian
eulerian_path#
Exact
Returns an Eulerian path: a walk that uses every edge exactly once.
og_error_t og_eulerian_path(
const og_t *graph,
og_vector_int_t *edge_res,
og_vector_int_t *vertex_res
);
As eulerian_cycle, except for the start: in an undirected graph the lowest vertex of odd degree when there is one, in a directed graph the vertex with one more out-edge than in-edges when there is one, otherwise the lowest vertex that has an edge (the path is then a cycle). edge_res gets |E| edge ids, vertex_res |E| + 1 vertex ids.
- Parameters
graphThe graph.edge_resOutput, may be NULL: the edge ids in walking order.vertex_resOutput, may be NULL: the vertex ids in walking order.
- Returns
OG_SUCCESS.- Errors
OG_ENOSOLThe graph has no Eulerian path.OG_ENOMEMNot enough memory.
- Complexity
- O(|V| + |E|).
- Checked
- Identical walks on the cases recorded from the igraph 1.0.1 C library, start vertex included.
- C name
og_eulerian_path- In notebooks
Og.EulerianPath- igraph 1.0
igraph_eulerian_path- See also
eulerian_cycle,is_eulerian
Triangles and census
dyad_census#
Exact
Counts the mutual, asymmetric and unconnected pairs of vertices.
og_error_t og_dyad_census(
const og_t *graph,
og_real_t *mut,
og_real_t *asym,
og_real_t *null
);
Each unordered pair of distinct vertices is a dyad (Holland and Leinhardt, 1970): mutual when edges join it in both directions, asymmetric when only one direction is present, null when no edge joins it. In an undirected graph every connected pair is mutual. Self-loops are ignored and parallel edges count once. The three numbers add up to |V| (|V| - 1) / 2 and are returned as reals, which do not overflow on large graphs.
- Parameters
graphThe graph.mutOutput, may be NULL: the number of mutual pairs.asymOutput, may be NULL: the number of asymmetric pairs.nullOutput, may be NULL: the number of unconnected pairs.
- Returns
OG_SUCCESS.- Errors
OG_ENOMEMNot enough memory.
- Complexity
- O(|V| + |E|).
- Checked
- 262 fixture graphs, directed and undirected, with loops and multi-edges.
- C name
og_dyad_census- In notebooks
Og.DyadCensus- igraph 1.0
igraph_dyad_census- See also
triad_census,reciprocity
triad_census#
Exact Threads
Counts the triads of a directed graph in each of the 16 isomorphism classes.
og_error_t og_triad_census(const og_t *graph, og_vector_t *res);
Every set of three vertices is a triad, classified by the dyads among its members (Davis and Leinhardt, 1972). res gets 16 counts in the standard MAN order: 003, 012, 102, 021D, 021U, 021C, 111D, 111U, 030T, 030C, 201, 120D, 120U, 120C, 210, 300. For example 003 is the empty triad, 012 a single edge, 102 a single mutual pair, 021D the out-star A->B, A->C, 021U the in-star, 021C the path A->B->C, 030T the transitive triad, 030C the directed 3-cycle and 300 three mutual pairs.
The count uses Batagelj and Mrvar's subquadratic method (Social Networks, 2001): each triad with at least one connected pair is found once, from its smallest connected pair, and the empty triads come by subtraction from |V| (|V| - 1) (|V| - 2) / 6. Self-loops are ignored. On multigraphs python-igraph's rule is followed for the triads with a single connected pair: a directed pair joined by two or more arcs counts as mutual (102), an undirected pair joined by two or more edges as unconnected. An undirected graph gives a warning, and each edge then counts as a mutual pair.
- Parameters
graphThe graph, normally directed.resOutput: 16 counts, resized as needed.
- Returns
OG_SUCCESS.- Errors
OG_ENOMEMNot enough memory.
- Complexity
- O(|E| x d_max + |V|), d_max the largest degree.
- Checked
- Identical to python-igraph on 262 fixture graphs, multigraphs included. Benchmark (20k vertices / 100k edges and 2k vertices / 100k edges): 0.07x and 0.03x python-igraph's time.
- Threads
- With
set_num_threads> 1 and enough work, the vertices are split over tasks that count into their own integer counters; the sums are exactly the one-thread result for any thread count. - C name
og_triad_census- In notebooks
Og.TriadCensus- igraph 1.0
igraph_triad_census- See also
dyad_census,motifs_randesu
#include "og.h"
#include <stdio.h>
/* Who follows whom among six accounts: down a hierarchy, or round in circles? */
int main(void) {
og_t follows;
og_vector_t census;
og_small(&follows, 6, OG_DIRECTED,
1,2, 2,3, 1,3, /* 1 follows 2 and 3, and 2 follows 3 */
3,4, 4,5, 5,3, /* 3, 4 and 5 follow each other round */
-1); /* 0 follows no one */
og_vector_init(&census, 0);
og_triad_census(&follows, &census);
/* in the MAN order: 003 (no ties) at 0, 030T (transitive) at 8, 030C (a cycle) at 9 */
printf("transitive %g, cyclic %g, without ties %g, of %g triads\n",
OG_VECTOR(census)[8], OG_VECTOR(census)[9], OG_VECTOR(census)[0],
og_vector_sum(&census));
/* prints: transitive 1, cyclic 1, without ties 4, of 20 triads */
og_vector_destroy(&census);
og_destroy(&follows);
return 0;
}
count_adjacent_triangles#
Exact
Counts, for each selected vertex, the triangles it belongs to.
og_error_t og_count_adjacent_triangles(
const og_t *graph,
og_vector_t *res,
const og_vs_t vids
);
A triangle is three vertices joined pairwise. Edge directions, self-loops and edge multiplicities are ignored. res[i] is the count for the i-th vertex of vids, in the selector's order.
When vids covers at least an eighth of the vertices, every triangle is listed once by the forward method of Schank and Wagner (2005) and credited to its three corners. For fewer vertices, each one's common neighbours with every neighbour are counted from the smaller side, so a high-degree neighbour costs no more than the vertex's own degree.
- Parameters
graphThe graph.resOutput: one count per selected vertex.vidsThe vertices to count for (a vertex selector, e.g.vss_all()).
- Returns
OG_SUCCESS.- Errors
OG_EINVVIDvids holds a vertex id outside the graph.OG_ENOMEMNot enough memory.
- Complexity
- Many vertices: O(|E|^1.5). A few: for each, the sum over its neighbours of the smaller of the two degrees (times a logarithm for the lookups).
- Checked
- Not in python-igraph's API: compared with a brute-force count from the definition on 262 graphs (all vertices and one at a time, so both methods), with counts derived from python-igraph's list_triangles on 900 more random graphs, and on a 3,000-spoke wheel.
- C name
og_count_adjacent_triangles- In notebooks
Og.CountAdjacentTriangles- igraph 1.0
igraph_count_adjacent_triangles- See also
count_triangles,list_triangles,transitivity_local_undirected
count_triangles#
Exact
Counts the triangles of the graph.
og_error_t og_count_triangles(const og_t *graph, og_real_t *res);
A triangle is three vertices joined pairwise; edge directions, self-loops and edge multiplicities are ignored. The count uses the forward method of Schank and Wagner (WEA 2005): vertices are ranked by degree, largest first, and each vertex looks only towards neighbours ranked after it, so every triangle is found once. The result is a real number.
- Parameters
graphThe graph.resOutput: the number of triangles.
- Returns
OG_SUCCESS.- Errors
OG_ENOMEMNot enough memory.
- Complexity
- O(|E|^1.5).
- Checked
- Equal to the number of triangles python-igraph's list_triangles gives, on 262 graphs.
- C name
og_count_triangles- In notebooks
Og.CountTriangles- igraph 1.0
igraph_count_triangles- See also
list_triangles,count_adjacent_triangles
list_triangles#
Exact
Lists every triangle of the graph, three vertex ids each.
og_error_t og_list_triangles(const og_t *graph, og_vector_int_t *res);
res gets 3k vertex ids for k triangles: res[3i], res[3i+1] and res[3i+2] are the corners of triangle i. Same conventions and method as count_triangles (directions, loops and multiplicities ignored; each triangle once). The order of the triangles and of the corners inside each is python-igraph 1.0's: it follows the degree ranking (largest degree first, ties by id) of the vertex each triangle is found from.
- Parameters
graphThe graph.resOutput: the corners, three per triangle.
- Returns
OG_SUCCESS.- Errors
OG_ENOMEMNot enough memory.
- Complexity
- O(|E|^1.5) plus the size of the output.
- Checked
- Identical listing, python-igraph's order, on 262 graphs.
- C name
og_list_triangles- In notebooks
Og.ListTriangles- igraph 1.0
igraph_list_triangles- See also
count_triangles,count_adjacent_triangles,cliques
Motifs
motifs_randesu#
Exact Threads
Counts the motifs (connected induced subgraphs) of a given size in each isomorphism class.
og_error_t og_motifs_randesu(
const og_t *graph,
og_vector_t *hist,
og_int_t size,
const og_vector_t *cut_prob
);
Every connected induced subgraph on size vertices is enumerated once, with Wernicke's ESU algorithm (IEEE/ACM TCBB, 2006): each subgraph is grown from its smallest vertex, extending only to larger neighbours. hist gets one entry per isomorphism class of graphs with size vertices, indexed as isoclass numbers the classes; the entries of classes that are not connected are NaN, since such subgraphs are never counted. Directed graphs support sizes 3 and 4 (edge directions define the class), undirected graphs sizes 3 to 6. Self-loops are ignored and parallel edges count once.
cut_prob (one entry per level of the search, or NULL) turns the count into RAND-ESU: at level d a branch is cut with probability cut_prob[d], so each subgraph is counted with probability equal to the product of (1 - cut_prob[d]). The random draws come from Open Graph's default generator. NULL or all zeros counts exactly.
- Parameters
graphThe graph.histOutput: one count per isomorphism class (NaN for the disconnected classes).sizeMotif size: 3 or 4 for directed graphs, 3 to 6 for undirected ones.cut_probCut probabilities, one per level (length size), or NULL for an exact count.
- Returns
OG_SUCCESS.- Errors
OG_UNIMPLEMENTEDsize is not supported for this kind of graph.OG_EINVALcut_prob does not have size entries.OG_ENOMEMNot enough memory.
- Complexity
- Proportional to the number of connected induced subgraphs of that size, which grows quickly with the size and the degrees.
- Checked
- With cut_prob NULL or all zeros: identical histograms (NaN classes included) for sizes 3-4 directed and 3-6 undirected on the fixture graphs. With cut probabilities (random): parity quality, the mean over seeds matches the expected count within confidence bounds.
- Threads
- Without cut probabilities the start vertices are split over tasks, each with its own search state and histogram; counts are whole numbers, so the result is the one-thread result. With cut probabilities the search stays on the calling thread.
- C name
og_motifs_randesu- In notebooks
Og.MotifsRandesu- igraph 1.0
igraph_motifs_randesu- See also
motifs_randesu_no,motifs_randesu_estimate,motifs_randesu_callback,isoclass,triad_census
#include "og.h"
#include <stdio.h>
int main(void) {
og_t g;
og_vector_t hist;
og_int_t c;
og_famous(&g, "Zachary");
og_vector_init(&hist, 0);
og_motifs_randesu(&g, &hist, 3, NULL); /* exact count */
for (c = 0; c < og_vector_size(&hist); c++) {
printf("class %" OG_PRId ": %g\n", c, OG_VECTOR(hist)[c]); /* NaN for 0 and 1 */
}
og_vector_destroy(&hist);
og_destroy(&g);
return 0;
}
motifs_randesu_no#
Exact Threads
Counts all connected induced subgraphs of a given size, without classifying them.
og_error_t og_motifs_randesu_no(
const og_t *graph,
og_real_t *no,
og_int_t size,
const og_vector_t *cut_prob
);
The same enumeration as motifs_randesu, keeping only the total. Since no class tables are needed, any size of 3 or more works, in directed and undirected graphs. With cut_prob the total counts only the subgraphs the random search keeps (see motifs_randesu). The result is a real number.
- Parameters
graphThe graph.noOutput: the number of connected induced subgraphs found.sizeSubgraph size, 3 or more.cut_probCut probabilities, one per level (length size), or NULL.
- Returns
OG_SUCCESS.- Errors
OG_EINVALsize is below 3.OG_EINVALcut_prob does not have size entries.OG_ENOMEMNot enough memory.
- Complexity
- Proportional to the number of connected induced subgraphs of that size.
- Checked
- Identical totals for every size tried, also sizes without class tables, on the fixture graphs.
- Threads
- As
motifs_randesu: start vertices split over tasks when there are no cut probabilities; whole-number counts, identical result. - C name
og_motifs_randesu_no- In notebooks
Og.MotifsRandesuNo- igraph 1.0
igraph_motifs_randesu_no- See also
motifs_randesu,motifs_randesu_estimate
motifs_randesu_estimate#
Exact Threads
Estimates the number of connected subgraphs of a given size from a sample of start vertices.
og_error_t og_motifs_randesu_estimate(
const og_t *graph,
og_real_t *est,
og_int_t size,
const og_vector_t *cut_prob,
og_int_t sample_size,
const og_vector_int_t *parsample
);
ESU finds each connected induced subgraph from its smallest vertex. This function runs the search from the sampled vertices only and scales the count by |V| / (sample size). With parsample the sample is the given vertices; without it, sample_size distinct vertices are drawn uniformly (a partial Fisher-Yates shuffle) with Open Graph's default random generator. Any size of 3 or more works; cut_prob applies as in motifs_randesu.
- Parameters
graphThe graph.estOutput: the estimated total.sizeSubgraph size, 3 or more.cut_probCut probabilities, one per level (length size), or NULL.sample_sizeNumber of vertices to draw when parsample is NULL: 1 to |V|.parsampleThe sample of start vertices, or NULL to draw one.
- Returns
OG_SUCCESS.- Errors
OG_EINVALsize is below 3, or cut_prob does not have size entries.OG_EINVALparsample is empty.OG_EINVVIDparsample holds a vertex id outside the graph.OG_EINVALparsample is NULL and sample_size is below 1 or above |V|.OG_ENOMEMNot enough memory.
- Complexity
- Proportional to the number of subgraphs found from the sampled vertices.
- Checked
- With a given sample: identical to python-igraph. With a drawn sample the draws are Open Graph's own; the estimate is unbiased (tested statistically).
- Threads
- Without cut probabilities the sampled start vertices are split over tasks; whole-number counts, identical result. The sample itself is drawn on the calling thread.
- C name
og_motifs_randesu_estimate- In notebooks
Og.MotifsRandesuEstimate- igraph 1.0
igraph_motifs_randesu_estimate- See also
motifs_randesu_no,motifs_randesu
motifs_randesu_callback#
Exact
Hands every motif found, with its vertices and its isomorphism class, to a handler.
og_error_t og_motifs_randesu_callback(
const og_t *graph,
og_int_t size,
const og_vector_t *cut_prob,
og_motifs_handler_t *callback,
void *extra
);
The same search as motifs_randesu (sizes 3-4 directed, 3-6 undirected; cut_prob as there). For each connected induced subgraph the handler gets the graph, the subgraph's vertex ids (a vector owned by the search, valid only during the call) and its isomorphism class as isoclass numbers it. The calls come in python-igraph's order. The handler returns OG_SUCCESS to go on, OG_STOP to end the search with success, or another code to end it with that error.
- Parameters
graphThe graph.sizeMotif size: 3 or 4 for directed graphs, 3 to 6 for undirected ones.cut_probCut probabilities, one per level, or NULL.callbackThe handler. Must not be NULL.extraPassed unchanged to every call.
- Returns
OG_SUCCESSwhen the search finished or the handler returnedOG_STOP; otherwise the handler's error code.- Errors
OG_UNIMPLEMENTEDsize is not supported for this kind of graph.OG_EINVALcut_prob does not have size entries, or callback is NULL.- handler's code: The handler returned an error code other than
OG_STOP. OG_ENOMEMNot enough memory.
- Complexity
- As
motifs_randesu, plus the handler's own time. - Checked
- The (vertex set, class) calls in python-igraph's order on the small fixture graphs;
OG_STOPand error codes from the handler tested. - C name
og_motifs_randesu_callback- In notebooks
Og.MotifsRandesuCallback- igraph 1.0
igraph_motifs_randesu_callback- See also
motifs_randesu,motifs_handler_t
Graphlets
graphlets#
Within tolerance
Decomposes a weighted graph into weighted cliques (graphlets).
og_error_t og_graphlets(
const og_t *graph,
const og_vector_t *weights,
og_vector_int_list_t *cliques,
og_vector_t *Mu,
og_int_t niter
);
The model of Azari Soufiani and Airoldi (AISTATS 2012): every edge's weight is explained as the sum of the weights of the cliques that contain it. graphlets runs graphlets_candidate_basis, then graphlets_project from all-ones starting weights for niter iterations, and returns the cliques sorted by decreasing weight Mu (equal weights keep the candidate order).
The graph must be simple once directions are ignored: no self-loops, no parallel edges, no pair joined in both directions. Weights must be finite and not negative; NULL means all 1.
- Parameters
graphA simple graph (directions ignored).weightsEdge weights, or NULL for all 1; length |E|, finite, >= 0.cliquesOutput list: the vertex ids of each graphlet.MuOutput: each graphlet's weight, in the order of cliques (decreasing).niterNumber of EM iterations, >= 0.
- Returns
OG_SUCCESS.- Errors
OG_EINVALThe graph has a self-loop, parallel edges or a mutual pair.OG_EINVALweights has the wrong length, or a negative, NaN or infinite entry.OG_EINVALniter is negative.OG_ENOMEMNot enough memory.
- Complexity
- The candidate search enumerates maximal cliques at every weight level (exponential in the worst case, fast on sparse graphs); each EM iteration costs the total number of edges inside the candidates.
- Checked
- python-igraph has no graphlets: 18 cases recorded from the igraph 1.0.1 C library (black box) and 220 model cases; cliques equal and Mu within 1e-9 (2 more recorded graphs within 7e-15 relative).
- C name
og_graphlets- In notebooks
Og.Graphlets- igraph 1.0
igraph_graphlets- See also
graphlets_candidate_basis,graphlets_project,maximal_cliques
graphlets_candidate_basis#
Exact
Finds the candidate cliques for a graphlet decomposition, each with its threshold.
og_error_t og_graphlets_candidate_basis(
const og_t *graph,
const og_vector_t *weights,
og_vector_int_list_t *cliques,
og_vector_t *thresholds
);
Algorithm 1 of Azari Soufiani and Airoldi (2012): a clique is a candidate when it is maximal in the graph of the edges of weight >= t for some weight level t. Each candidate is listed once, with its threshold: the largest such t, which is its own smallest edge weight. Isolated vertices are single-vertex candidates with an infinite threshold.
Maximal cliques are found by Bron-Kerbosch with Tomita's pivot in a degeneracy order (Eppstein, Loffler and Strash). The order of the candidates is Open Graph's own and can differ from igraph's; compare the result as a set. The graph must be simple once directions are ignored.
- Parameters
graphA simple graph (directions ignored).weightsEdge weights, or NULL for all 1; length |E|, finite, >= 0.cliquesOutput list: the vertex ids of each candidate.thresholdsOutput, may be NULL: each candidate's threshold, in the order of cliques.
- Returns
OG_SUCCESS.- Errors
OG_EINVALThe graph has a self-loop, parallel edges or a mutual pair.OG_EINVALweights has the wrong length, or a negative, NaN or infinite entry.OG_ENOMEMNot enough memory.
- Complexity
- Maximal-clique enumeration at each weight level: exponential in the worst case, fast on sparse graphs.
- Checked
- Equal to igraph's candidates and thresholds as a set (order not compared) on the 18 cases recorded from the igraph 1.0.1 C library, and to the model set built from python-igraph's maximal_cliques on 220 seeded graphs.
- C name
og_graphlets_candidate_basis- In notebooks
Og.GraphletsCandidateBasis- igraph 1.0
igraph_graphlets_candidate_basis- See also
graphlets,graphlets_project,maximal_cliques
graphlets_project#
Within tolerance
Fits weights to given cliques so that together they explain the edge weights.
og_error_t og_graphlets_project(
const og_t *graph,
const og_vector_t *weights,
const og_vector_int_list_t *cliques,
og_vector_t *Mu,
og_bool_t startMu,
og_int_t niter
);
Expectation-maximisation (the Richardson-Lucy update) for the Poisson model of Azari Soufiani and Airoldi (2012). With lambda_e the sum of Mu over the cliques that contain edge e, one iteration updates every clique at once: Mu_k becomes Mu_k times the sum, over the edges e inside clique k, of w_e / (lambda_e + 1e-4), divided by s_k (s_k + 1) / 2, where s_k is the clique's vertex count. The constant 1e-4 and the size term were measured on the igraph 1.0.1 C library used as a black box, so the weights agree with igraph's.
startMu true starts from the values in Mu (one per clique), false from all ones. Mu receives the weights after niter iterations, in the order of cliques. Pairs inside a clique that are not edges of the graph are skipped.
- Parameters
graphA simple graph (directions ignored).weightsEdge weights, or NULL for all 1; length |E|, finite, >= 0.cliquesThe cliques to weigh: one vertex-id vector each.MuInput (when startMu is true) and output: one weight per clique.startMutrue: start from Mu; false: start from all ones.niterNumber of iterations, >= 0.
- Returns
OG_SUCCESS.- Errors
OG_EINVALThe graph has a self-loop, parallel edges or a mutual pair.OG_EINVALweights has the wrong length, or a negative, NaN or infinite entry.OG_EINVALniter is negative, or startMu is true and Mu does not have one entry per clique.OG_EINVALA clique lists the same vertex twice.OG_EINVVIDA clique holds a vertex id outside the graph.OG_ENOMEMNot enough memory.
- Complexity
- O(niter x the total number of edges inside the cliques), after one pass that collects those edges.
- Checked
- Mu within 1e-9 of the answers recorded from the igraph 1.0.1 C library (given cliques, niter, startMu true and false); the measured update constants reproduce every recorded Mu to 1e-15.
- C name
og_graphlets_project- In notebooks
Og.GraphletsProject- igraph 1.0
igraph_graphlets_project- See also
graphlets,graphlets_candidate_basis
Epidemics
sir#
Same quality
Simulates the SIR epidemic model on the graph, no_sim times.
og_error_t og_sir(
const og_t *graph,
og_real_t beta,
og_real_t gamma,
og_int_t no_sim,
og_vector_ptr_t *result
);
Each run starts with one infected vertex, drawn uniformly, and every other vertex susceptible. An infected vertex recovers at rate gamma; a susceptible vertex is infected at rate beta times its number of infected neighbours (Kermack and McKendrick's model on a network). Events are simulated exactly in continuous time with Gillespie's direct method (1977) until no vertex is infected. Each run becomes an sir_t: the event times and the numbers of susceptible, infected and recovered vertices after each event, entry 0 being the start at time 0.
The graph must be simple once directions are ignored; a directed graph is treated as undirected, with a warning. Random numbers come from Open Graph's default generator: one seed repeats the runs exactly on every platform, but the runs differ from igraph's for the same seed.
result is a pointer vector of the caller's. Whatever it held is released first; it then receives no_sim pointers to sir_t (allocated with malloc), and sir_destroy becomes its item destructor, so vector_ptr_destroy_all(result) frees everything.
- Parameters
graphA graph that is simple once directions are ignored, with at least one vertex.betaInfection rate per infected neighbour: finite, >= 0.gammaRecovery rate: finite, > 0.no_simNumber of runs, >= 0.resultAn initialised pointer vector; receives onesir_t* per run.
- Returns
OG_SUCCESS.- Errors
OG_EINVALThe graph has no vertices.OG_EINVALbeta is negative or not finite, or gamma is not positive or not finite.OG_EINVALThe graph has a self-loop, parallel edges or a mutual pair.OG_EINVALno_sim is negative.OG_ENOMEMNot enough memory.
- Complexity
- Per run O((|V| + |E|) log |V|): each vertex is infected and recovers at most once, and each event updates its neighbours' rates in a partial-sum tree.
- Checked
- python-igraph has no SIR. Mean final size and mean peak within confidence bounds of 10,000 runs per graph recorded from the igraph 1.0.1 C library (black box) on 5 graphs; closed forms for one and two vertices; seeded runs repeat exactly.
- C name
og_sir- In notebooks
Og.Sir- igraph 1.0
igraph_sir- See also
sir_t,sir_destroy
#include "og.h"
#include <stdio.h>
int main(void) {
og_t g;
og_vector_ptr_t runs;
og_int_t i;
og_famous(&g, "Zachary");
og_rng_seed(og_rng_default(), 42);
og_vector_ptr_init(&runs, 0);
og_sir(&g, 0.1, 1.0, 100, &runs); /* beta 0.1, gamma 1, 100 runs */
for (i = 0; i < 3; i++) {
const og_sir_t *r = (const og_sir_t *) og_vector_ptr_get(&runs, i);
og_int_t last = og_vector_size(&r->times) - 1;
printf("run %" OG_PRId ": %" OG_PRId " recovered at t = %.2f\n", i,
OG_VECTOR(r->no_r)[last], OG_VECTOR(r->times)[last]);
}
og_vector_ptr_destroy_all(&runs); /* frees every run too */
og_destroy(&g);
return 0;
}
sir_destroy#
Unit tests
Releases the four vectors of one sir_t.
void og_sir_destroy(og_sir_t *sir);
Destroys times, no_s, no_i and no_r; the struct itself is not freed (sir allocated it with malloc, and vector_ptr_destroy_all on sir's result frees both). Safe on NULL and on a run already destroyed. sir installs it as its result vector's item destructor.
- Parameters
sirThe run to release, or NULL.
- Returns
- Nothing.
- Complexity
- O(1).
- Checked
- Frees the four vectors; safe twice; tested as the result vector's item destructor.
- C name
og_sir_destroy- In notebooks
Og.SirDestroy- igraph 1.0
igraph_sir_destroy- See also
sir,sir_t