cograph 2.6.11
plot_mcml(): the figure’s height can now be controlled
plot_mcml() is drawn with a locked aspect ratio from fixed layout numbers, so its shape never changed: a taller image only added white space (an 8 x 12 in image used 59% of its height, 8 x 16 in 44%). And the argument documented for this, layer_spacing, was accepted and then ignored – 2 and 30 drew the same figure.
layer_spacing now works, and takes three kinds of value:
-
NULL(default) – the automatic layout, exactly as before. Existing figures are unchanged. -
"fill"– the gap between the two layers is stretched so the figure uses the full height of the image it is drawn on. Change the image height and the plot follows. Shapes stay round; on a wide image, where height is what binds, nothing changes. - a positive number – the distance from the centre of the bottom layer to the centre of the summary layer, in the units of
spacing. Overridesinter_layer_gap. A value that overlaps the layers raises acograph_layers_overlapwarning; anything else invalid raisescograph_bad_layer_spacing.
Bug fix: a shrunken plot_htna() legend could still run off the page
When a side legend is too large for its band it is scaled down to fit (new in 2.6.10). That scaling made one proportional adjustment, but a legend’s size is not proportional to its text size – symbols and padding scale differently, and font hinting makes text widths step – so under Cairo fonts (Linux, Windows) the result could still extend 0.5% past the edge of the page, while fitting exactly on macOS. The legend is now re-measured after each adjustment until the measured box fits. Legends that already fit are unaffected.
cograph 2.6.10
plot_htna() gains legend_size, and its legend is no longer oversized
The “Groups” legend was drawn at a hard-coded cex = 1.4 – larger than the node labels it explains – with no argument to change it. plot_htna() now takes legend_size (a cex, default 0.8, the same default and meaning as splot(legend_size = )), and the legend’s symbols are sized from it. The default legend is therefore visibly smaller than in 2.6.9; pass legend_size = 1.4 for the old text size. Anything other than a single positive number raises a cograph_bad_legend_size error.
Bug fix: the plot_htna() legend overlapped the network and was cut off
With a side legend (legend_position = "bottom", the default, or "top", "left", "right"), the “Groups” legend was drawn partly on top of the network and partly off the page, at every figure size. Two causes:
-
plot_htna()reserved the legend’s margin withpar(mar = ), whichsplot()replaces with its ownmarginsbefore drawing. The margin now travels assplot(margins = ), so the band the legend needs really exists. - The legend was pushed out of the plot with a negative
inset, which is a fraction of the plot region and moves the box by only a sliver of its own height. The legend is now measured first and anchored by coordinates in the centre of its band.
The band is sized from the legend itself (rows, title, longest group name, legend_size, and the device’s text scale) rather than a fixed 6.5 lines. If a legend still cannot fit – many long group names on a small figure – it is scaled down instead of overlapping the plot or leaving the page.
A margin passed explicitly (mar = or margins =) is left untouched. With legend_position = "top" and a title, the title is drawn on the outer lines of the top margin and the legend below it; previously splot() centred the title in that margin, on top of the legend. Corner positions ("topright" etc.) are unchanged and still draw inside the plot box.
cograph 2.6.9
A full audit of the reference manual
Every exported and internal function was read against its own roxygen block. Everything below is a documentation correction unless a heading says otherwise.
Examples no longer assume a suggested package is installed
igraph is in Suggests, but examples throughout the manual failed without it. 78 roxygen example blocks are now @examplesIf requireNamespace("igraph", quietly = TRUE).
Most built their example network with igraph::make_ring(), igraph::make_star() or igraph::make_graph("Zachary") and were found by reading the examples. The last 21 were not: network_summary(), network_girth(), network_radius(), degree_distribution(), dyad_census(), ego_networks(), shortest_paths() and the rest of that family take a plain matrix and reach igraph internally through to_igraph(), so nothing in the example text revealed the dependency. They were found by removing igraph and running the manual.
Verified by running every example in a library where igraph (and therefore tna) could not be loaded: 403 topics, no failures.
... documented an argument that could not be passed
32 verbs – the community detection family, the network_* summaries, assortativity(), core_periphery(), dyad_census(), ego_networks(), shortest_paths(), k_shortest_paths(), rich_club(), rich_club_local(), robustness() and vulnerability() – documented ... as “additional arguments passed to to_igraph()”. to_igraph() is function(x, directed = NULL): it has no ..., so anything passed raised an “unused argument” error. Each site now says what is true. Where directed is already an explicit formal the dots are documented as unused; where it is not, they are documented as carrying directed and nothing else.
A lost title, a lost contract
plot_mcml()’s title line was missing its leading #, so R parsed it as a stray top-level string and roxygen took the next ten lines of prose as the title. man/plot_mcml.Rd now has the title it was meant to have.
A roxygen block in blob-helpers.R had been separated from its function by a later comment banner, which silently moved .expand_repeated_nodes()’s @param and @return onto the one-line accessor that followed it.
Corrections to documented behaviour
Among the factual fixes: core_periphery()’s example printed a component that does not exist; network_global_efficiency() documented the wrong value for its own example; community_consensus() documented ... as reaching the detection method when it is discarded; degree_distribution() marked three always-present components as conditional; binarize(signed = TRUE) was described as producing -1 rather than +1 or -1; get_edges() did not say its from/to are integer indices rather than labels; several verbs did not name the classed condition they raise; and the register_shape() and register_layout() examples overwrote a built-in shape and layout for the rest of the session. show_zero_edges in from_tna() and from_qgraph() is documented as having no effect, which is what it has.
The plot_tna() example that demonstrated custom colours with rainbow(5) now uses palette_colorblind(5): the reference manual was teaching the one palette the package’s own style rules exclude.
British spellings in roxygen prose were normalized to the en-US that DESCRIPTION declares (513 words across 79 files; comments only).
One fewer dependency
scales has been dropped from Suggests. It was used for exactly one call – scales::squish as the out-of-bounds handler on the centrality heatmap’s fill scale – and that behaviour is now a nine-line base R helper. Clamping is unchanged: a finite value outside the limits is pulled to the nearer end, and NA and the infinities are left alone.
show_zero_edges now does something
from_tna() and from_qgraph() have accepted and documented a show_zero_edges argument for several releases without ever reading it. Zero is how a weight matrix stores “no edge”, so an edge whose weight rounds to zero at weight_digits disappears from the plot, and there was no way to prevent that. With show_zero_edges = TRUE such an edge is now drawn at the smallest magnitude weight_digits can express, carrying its sign; every other weight is untouched. The default, FALSE, behaves exactly as before.
Three parameters now say what is wrong with them
ld_radius, s_shell_a and comm_r were unvalidated, so a value outside their domain surfaced as an opaque failure from deep inside a kernel – values must be length 1, but FUN(X[[1]]) result is length 0 for ld_radius = 0, and an all-NA result with a coercion warning for a comm_r that is neither "max_intra" nor a number. All three now raise a classed cograph_bad_parameter error naming the argument and its domain, matching the other tuning parameters in the centrality surface.
Two regressions against reverse dependencies
Both were introduced before this release and are found by checking cograph against the eight packages that depend on it. All eight now check identically against this version and against the current CRAN cograph.
centrality() failed with arguments imply differing number of rows on a network whose node table carries no label column. The node-label fallback tested only for NULL, and a source with no labels arrives as a zero-length character vector instead, so the fallback to node indices never fired and the node column came out empty beside full-length measure columns. The fallback now checks that there is exactly one label per node, which also rejects a label vector of the wrong length.
to_df() and to_data_frame() return from, to and weight again. When these stopped routing through igraph, the round-trip that used to discard extra edge columns went with it, and the verb silently began returning every column the edge table carried. They are the narrow conversion verbs; to get the edge table whole – including columns mutate_edges() computed – use as.data.frame() on the network, which is the accessor and is unchanged.
Fixes
centrality(measures = "hubbell") refused to report a divergent result only when the largest real part of the scaled spectrum reached 1. The Neumann series behind Hubbell diverges once the spectral radius does, so a signed network carrying a complex pair of modulus above 1 – or a real eigenvalue below -1 – was reported as a confident number instead of NA. The guard now tests the modulus. For a non-negative weight matrix the Perron root is real, positive and equal to the spectral radius, so no unsigned network changes.
centrality(measures = "delta_closeness", closeness_delta = 0) scored every node of a disconnected graph 1. R evaluates Inf^0 as 1 and is.finite(1) is TRUE, so an unreachable pair passed the filter and counted as fully close. The distance matrix is now screened as well. Only closeness_delta = 0 is affected; every positive delta already behaved.
centrality_expected_influence_1() and centrality_expected_influence_2() read their result column by $ partial matching, because the column is really expected_influence_1_out. They now index the column by name. Results are unchanged.
mcml()’s deprecation marker no longer depends on the lifecycle package, which was not declared anywhere, and no longer points at an SVG that the package does not ship – the HTML help image for that topic was broken.
cograph 2.6.5
Direction cues on simplicial pathway panels
A simplex is a set of vertices, so a blob on its own cannot say which state came first: A -> B -> B and B -> A -> B drew the identical shape. With dismantled = TRUE each panel shows one ordered pathway, so plot_simplicial() can now draw the traversal three ways, selected with direction_cues: a light-to-dark core ramp along the path ("shade"), a ring whose highlight peaks on the side facing the next state ("ring"), and an arrowhead just outside each node aimed at its successor ("arrows").
direction defaults to NULL, which turns the cues on exactly when dismantled = TRUE. A single combined blob cannot express direction, so direction = TRUE with dismantled = FALSE raises a classed cograph_direction_needs_panels error rather than drawing a misleading figure. legend draws the in-figure legend strip beneath a dismantled grid and defaults to TRUE whenever the cues are on.
Unordered pathways are no longer forced into a source/target split
An association-rule itemset and a clique of a simplicial complex are sets: every member is co-equal and there is no target at all. These are now carried through the pipeline as genuinely unordered — ordered = FALSE — instead of having a source/target split imposed on them, and their panels are titled with a member list rather than a path. Ordered HON/HYPA/MOGen pathways are unaffected.
cograph 2.6.4
Fixes to the parallel motif null
A permutation replicate that does not come back intact from a parallel worker now raises an error. Previously only an explicit worker error was caught; a worker killed by the operating system returns NULL for its whole chunk with only a warning, and those gaps were silently filled by recycling the surviving replicates – part of a permutation null replaced by duplicates, with no warning and no error.
The serial replicate path no longer leaves the session’s random number generator switched to L’Ecuyer-CMRG. motifs() itself never used that path, but the package’s own test suite did, which changed the random fixtures of every test file that ran afterwards.
cores validation now raises a classed cograph_bad_cores condition.
cograph 2.6.3
Clearer errors when igraph is not installed
igraph is a suggested dependency. Functions that need it – including to_igraph(), motif_census(), detect_communities(), robustness(), vulnerability(), rich_club() and network_summary() – now raise a cograph_missing_suggest error naming the function and how to install igraph, instead of R’s bare “there is no package called ‘igraph’”. Functions that need no igraph, such as motifs(significance = FALSE), subgraphs(), extract_triads() and centrality(), keep working without it.
Faster motif permutation tests
motifs() on individual-level data (a tna model, or an edge list with an actor column) now runs its permutation null several times faster — on tna::group_regulation with the default n_perm = 1000, 255s to 39s. The null builds class counts directly instead of materialising and then re-counting a row per node triple, and the triple indices are computed once per state space rather than once per unit per replicate.
Results are unchanged: for a given seed the permutation draws, and every count, expectation, z score and p value, are identical to previous versions.
Faster extract_motifs(significance = TRUE)
The instance-level permutation null now counts (triple, class) pairs directly instead of building a labelled row per triple per unit and re-aggregating it by a pasted key. On tna::group_regulation at n_perm = 1000, 428s to 32s. Results are unchanged for a given seed.
motifs(cores = )
The individual-level permutation null can now run across worker processes. cores = 1 remains the default and is byte-for-byte the previous behaviour.
cores > 1 gives each replicate its own L’Ecuyer-CMRG stream, so a result depends on seed alone – not on the worker count, and not on how replicates were chunked across workers. Repeated parallel runs of one seed agree exactly, at any cores. Those are a different set of draws from the serial path, so p-values from cores > 1 will not match a cores = 1 run of the same seed; both are valid permutation nulls. Forking is used where available, with a PSOCK cluster on Windows.
Measured on 10,000 simulated sequences (10 states) at n_perm = 400: 103.0s at cores = 1, 20.5s at cores = 10.
On Windows the null runs through a PSOCK cluster, which is exercised in the test suite on every platform.
A replicate that does not come back intact from a worker – an error, a NULL from a killed process, or a wrong-length result – raises a cograph_parallel_failure error naming the problem, rather than being folded into the null matrix. If the available core count cannot be detected, cores > 1 is used but reported with a cograph_cores_undetected warning.
plot_mcml(expand = )
The top layer can now be drawn at a finer resolution than the partition: named clusters appear as their member states while every other cluster stays a single node, and the bottom layer still shows the partition, so an expanded state sits inside its cluster’s shell and is linked to its own summary node. expand = "all" (or TRUE) expands every cluster.
The expanded macro is re-counted from the input with a refined partition, using cograph’s own cluster_summary(); a k x k aggregate cannot be disaggregated after the fact. Only a pre-built cluster_summary or mcml, which carries no source to re-count from, falls back to Nestimate::macro_network(), and says so with a cograph_expand_unavailable error when that is unavailable.
This also fixes a silent defect in the previous layout code: the top layer was indexed positionally against the cluster count, so a macro with more nodes than the partition was truncated to its first k rows and drawn under the cluster names — a confident, wrong figure with no error. It is now matched by name, and a macro wider than the partition that cograph did not build itself is refused.
cograph 2.6.1
Fixes for defects an adversarial review found in the 2.6.0 wrangling verbs. All of them were introduced in 2.6.0 except the last, which was older.
Signed weights are no longer deleted
Zero is how cograph stores “no edge”, so it must never be compared against a real weight. to_undirected(), symmetrize(), spanning_tree() and bind_networks() did exactly that, which silently deleted edges in networks that carry negative weights — correlation and partial-correlation networks above all:
- a one-way edge of weight
-2was deleted bymethod = "max", becausepmax(-2, 0)is0; - a one-way edge of weight
2was deleted bymethod = "min"; -
method = "mean"halved every unreciprocated edge against a phantom reverse arc; -
spanning_tree()returned an empty network on any all-negative graph: Prim chose the right edges, then the mirroring step compared each against zero and erased it; -
bind_networks(weight = "max")lost an edge only one network had if its weight was negative.
Presence is now carried separately from weight throughout: two values are combined only where both arcs exist, and an unreciprocated edge keeps its own weight. Combining to exactly zero raises a cograph_edges_dropped warning rather than shrinking the edge set in silence.
symmetrize() gains method = "mutual" for the reciprocated-only rule (sna’s “strong”). method = "min" no longer means that: it is a weight combination that keeps unreciprocated edges, which is a different operation.
Other fixes
-
contract_nodes()counted an undirected within-group edge twice, because a symmetric matrix holds every such edge twice. It now aggregates the edge table, so a single edge of weight 3 becomes a self-loop of 3, not 6. -
reorder_nodes()checked only the length oforder, so a non-permutation such asc("A", "A", "B")produced duplicate labels or an internal subscript error. It now requires an exact permutation. -
add_edges()andset_edges()accepted the same undirected edge twice (A->BandB->A), leaving the edge table and the weight matrix disagreeing about how many edges exist. Both now reject it. -
mutate_nodes()andmutate_edges()could overwrite the columns the structure is keyed on (label,id,from,to), leaving the node table, edge table and matrix describing different networks. Those columns are now reserved. A weight mutated to zero drops the edge with a classed warning rather than leaving a row the matrix does not have. - Removing every edge now also raises
cograph_isolates_created, as the documented invariant says: the nodes are kept, so they are newly isolated. -
split_components()andcontract_nodes()no longer fail on a zero-node network, and the matrix-level verbs raisecograph_bad_selectionon non-finite weights instead of an internal error several frames later. -
proportionanddensityreject 0, andtop,kandmin_sizereject fractional values, as documented.complement_network()rejectsweight = 0, which would have produced an empty complement. -
bind_networks(directed = FALSE)on directed input returned an “undirected” network whose matrix was asymmetric and whose edge table was empty; it now symmetrises the inputs first. It also carriesx’s metadata, estimation data and node attributes instead of dropping them. - Empty results keep the estimation data, and
as.data.frame()on one keeps the extra edge columns. -
reverse_edges()andnormalize_weights()("max","sum","minmax") keep extra edge columns; they map edges one-to-one, so there was no reason to lose them.network_to_igraph()carries node columns across as vertex attributes, so attributes added withmutate_nodes()survivekeep_format = TRUE. -
parse_matrix()read an undirected matrix from the strict upper triangle, so a self-loop on the diagonal was dropped from the edge table while$weightskept it — the two disagreed, and a later matrix-level verb could resurrect the loop. Undirected self-loops are now edges. This predates 2.6.0.
cograph 2.6.0
Network wrangling
The verbs that reshape a network are now a family with one contract: any supported input, options as named arguments, a cograph_network back (or the input format with keep_format = TRUE), and classed conditions. See ?network_wrangling.
New accessor
as.data.frame() on a cograph_network returns the tidy edge table, with endpoints as labels rather than internal indices, and as.data.frame(what = "nodes") returns the node table. No caller needs to reach into the object with $ any more.
New verbs
- Weights:
threshold_edges(),binarize(),symmetrize(),normalize_weights(),invert_weights(). - Structure:
to_undirected(),to_directed(),reverse_edges(),remove_isolates(),contract_nodes(),split_components(),select_k_core(),spanning_tree(),complement_network(),reorder_nodes(),rename_nodes(). - Editing:
add_nodes(),remove_nodes(),add_edges(),remove_edges(),mutate_nodes(),mutate_edges(),bind_networks().
add_edges() shares its name with igraph::add_edges(); call cograph::add_edges() when igraph is attached.
Wider vocabulary inside expressions
Node expressions gain is_isolated, is_source, is_sink, is_leaf, is_cut, local_transitivity and local_triangles, and any measure centrality() computes can now be named directly — select_nodes(x, harmonic > 0) works, as does select_top(x, n = 5, by = "leverage"). Edge expressions gain is_loop, is_multiple, is_reciprocal, weight_rank, from_community and to_community, and select_edges(by = ) accepts the endpoint metrics.
Bug fixes (all user-visible)
- A network whose last node has no edges no longer crashes
filter_nodes(),select_nodes(),select_edges(),to_df(),to_network()orto_igraph().network_to_igraph()built the graph from the edge list, so every node after the last edge endpoint disappeared and the label assignment then failed. - An undirected network stays undirected through every verb. The rebuilt weight matrix was upper-triangular, so
as_cograph()re-detected the result as directed and every downstream consumer saw half the strength. -
set_edges()andset_nodes()rebuild the stored weight matrix, soto_matrix()can no longer return the pre-edit network, andset_edges()keeps extra edge columns. - Extra columns of an edge-list input (
session,time, …) surviveas_cograph()and are usable in filter expressions, as documented. - Node groups, estimation data, layout and the original source type survive every filter.
- An empty result with
keep_format = TRUEreturns an empty object of the input type instead of erroring with “No such edge attribute”. -
keep_format = TRUEon a tna model returns a rebuilt tna model. -
to_matrix()andto_data_frame()no longer route through igraph.
Behaviour changes
-
Filtering edges no longer removes nodes.
filter_edges(),select_edges()and friends now keep every node, matchingigraph::delete_edges()and tidygraph, and warn (cograph_isolates_created) when the filter left a node without edges. Useremove_isolates(), orkeep_isolates = FALSE, for the old behaviour. -
.keep_isolatesand.keep_edgesare renamed tokeep_isolatesandkeep_edges. The dotted names still work and warn. - Malformed selections are errors of class
cograph_bad_selectionrather than warnings that return something plausible: unknown node names, out-of-range or fractional indices, abetweenthat is not two node sets, an unknown measure inby. - The “Result converted to cograph_network” message is gone; the conversion is documented instead.
-
filter_nodes()computes only the measures its expression names. It used to compute all twelve, including HITS, on every call.
cograph 2.5.0
-
Matrixis no longer imported (nothing used it after the port); the committed test-network fixtures are now tracked; two tests no longer needwithr.
cograph 2.4.9
Centrality without igraph
The whole centrality surface (centrality() and its 191 centrality_* wrappers, edge_centrality(), centralization(), group_centrality(), dispersion(), estrada_index(), trophic_incoherence(), and the centrality vocabulary of the wrangling verbs) now computes on cograph’s own kernels. Every input is turned once into a dense, labelled weight matrix (R/kernels-graph.R); no igraph object is built. igraph stays in Suggests for input conversion of igraph objects and for community detection and layouts.
Equivalence was verified measure by measure against the igraph-backed implementation on a golden corpus of 62 unsigned networks (30 real, 32 synthetic edge cases) under every mode and weighting, at relative tolerance sqrt(.Machine$double.eps), with the following documented exceptions.
-
flow_betweennessstill needs igraph and raisescograph_needs_igraphwhen it is not installed. - Hub, authority and eigenvector scores on graphs whose adjacency (or
A'A) is not primitive are not unique; igraph returned a random member of the eigenspace and sometimes failed to converge. The native kernels are deterministic and verified by eigen-residual tests instead. - Local transitivity and clusterrank on directed graphs with reciprocated dyads now follow igraph’s documented semantics (collapse to a simple undirected graph). igraph 2.3.3 returned a different value after
any_multiple()had been called on the object, which the oldcentrality()always did. -
alphanow computes on weighted graphs with self-loops; igraph 2.3.3 errored there. - Path-based measures are now scale-invariant: the same graph with all weights multiplied by
1e-18gives identical betweenness. igraph’s absolute epsilon does not. - Parallel edges are always combined: a dense weight matrix holds one value per cell, so
simplify = FALSEor"none"now sum them (the old adapters summed them when assembling an adjacency anyway). Onlydegreeon a multigraph changes, counting a parallel pair once. - Weighted local reaching centrality averages weights along one shortest path per target; among tied shortest paths the kernel’s choice is deterministic (lowest predecessor index) where igraph’s was implementation-dependent. Values agree exactly whenever no ties exist.
- Weighted (Barrat) transitivity has a native kernel; like igraph it refuses directed input, with class
cograph_directed_unsupported.
New internal kernels: edge betweenness, Barrat transitivity, average neighbour degree, articulation points, bridges, ego masks, and a loop-preserving coreness matching igraph’s convention.
Test infrastructure: a versioned corpus of test networks under tests/testthat/networks/ (32 real, 35 degenerate, plus local tiers), a golden-file comparison harness, a scale-invariance property test, and a CI job that runs the centrality tests with igraph uninstalled.
cograph 2.4.8
New features
Centrality Batch 51 — trust-PageRank
- Added
centrality_trust_pagerank(), Sheng, Zhu, Wang, Wang and Hou’s trust-PageRank. PageRank’s even split of a node’s score among its neighbours is replaced by a trust-value mixing a similarity ratio with a degree ratio:T(i,j) = (1-k) s(i,j)/sum_{l in N_j} s(j,l) + k d_i/sum_{l in N_j} d_l, fed toTPR_i = (1-alpha)/n + alpha sum_{j in N_i} T(i,j) TPR_j, withsthe fixed point of SimRank restricted to the lines of the graph. New parameterstpr_alpha(0.85),tpr_k(0.85),tpr_decay(1),tpr_tol(1e-14) andtpr_max_iter(1000). -
The Centrality Zoo cites the wrong paper. Its entry 2.381 attributes Trust-PageRank to Sheng et al., Physica A 541:123262, which defines the unrelated global-and-local-structure index of entry 2.149. The formulas it prints are equations (2), (4), (5), (6) and (7) of a different, fully open-access Sheng et al. paper, Algorithms 13(11):280,
doi:10.3390/a13110280, which is what cograph implements. A reader following the Zoo’s reference lands on a different measure. -
The similarity recursion runs on the lines of the graph only, and that is what makes it converge. Algorithm 1 quantifies over connected pairs and table 3 marks every non-adjacent cell with a dash, so a non-adjacent pair contributes zero rather than the
0.1that initialises the lines. The base cases(a,a) = 1is then the only inhomogeneous term, and it reaches a line exactly through the triangles that line carries, so the recursion contracts even at the source’sC = 1. Pinning non-adjacent pairs at0.1instead reproduces neither published fixture. -
Both published fixtures reproduce. Table 3 on page 6 prints seven similarities, and all seven round to their printed two decimals; its
S_vcolumn is the sum of the paper’s own rounded cells rather than the rounded sum, a convention node 5 alone separates. Table 5 on page 10 prints two top-ten rankings: the karate club’s reproduces in order at all ten positions, and the kite’s up to three exact ties forced by its own automorphism group. -
A component with lines but no triangle has no value, and cograph returns
NAthere rather than inventing one. The recursion is then homogeneous, its least non-negative fixed point is zero, and the similarity ratio is0/0. Every path, tree, star, even cycle and complete bipartite graph is in that class, and so is the Petersen graph. Unlikecentrality_dil()andcentrality_lhc(), the quotient is not determined by its numerator, so the1/d_jfallback would silently turn the measure into a degree-ratio PageRank over the whole class; this followscentrality_iec()instead. An isolate is not in the class and keeps the bare(1 - alpha)/n. -
The trust matrix is column-stochastic, so the scores sum to one on a graph without isolates and the iteration count is a convergence tolerance rather than a modelling choice. Both recursions stop on a relative change, because the similarities on one graph span twenty orders of magnitude; a recursion still moving at
tpr_max_iterraisescograph_no_converge. -
The source’s claim that its decay constant
Cdoes not matter is false for the converged recursion. It holds for a homogeneous recursion; the base case makes this one affine, soCenters the resolvent. Moving it from 1 to 0.5 moves the karate club’s similarity ratios by up to 0.141. - Direction, weights, loops and parallel edges are projected onto the simple undirected skeleton the source defines on. Marked costly, so
type = "all"excludes it unless asked for.
Centrality Batch 50 — degree and importance of lines
- Added
centrality_dil(), Liu, Xiong, Shi, Shi and Wang’s degree and importance of lines. A node’s degree is corrected by the share it can claim of the importance of the lines touching it:I_e(m,n) = (k_m - p - 1)(k_n - p - 1) / (p/2 + 1)is the importance of a line,pbeing the number of triangles carrying it;W(i,j) = I_e(i,j) (k_i - 1)/(k_i + k_j - 2)is the endpoint’s share of it; andDIL(i) = k_i + sum over the open neighbourhood of W(i,j). No new parameter. -
lambdaisp/2 + 1, and a text-layer reading gets it wrong. The stacked fraction extracts from the published PDF aslambda = 2p + 1, in the original as much as in the Almasi and Hu (2019) reproduction of it. The equations were read from 300 dpi page images, and the paper’s own worked example settles it in printed prose: atp = 1it writeslambda = 1/2 + 1 = 1.5andI_e45 = 8/3. - No Zoo divergence. Unlike several recent batches, Zoo entry 2.62 transcribes the source correctly, and the original agrees with the Almasi and Hu reproduction symbol for symbol — only the symbol names differ.
-
All three published fixtures reproduce, 29 printed values in total. Figure 1 on page 210 prints
I_e45 = 9and8/3; figure 2 on page 211 prints four edge importances and thenL_v2 = 26/9andL_v5 = 52/15; and table 3 on page 217 prints a value for all 21 nodes of the ARPA network. The figure-1 and figure-2 values match as exact fractions with no tolerance at all, and all 21 table-3 values match under the paper’s own four-decimal rounding, in the descending order it prints them. The edge list read off figure 6 is corroborated by the paper’s own degree column at all 21 nodes. -
An isolated pair of nodes is the one undefined split, and it resolves. The share’s denominator
k_i + k_j - 2vanishes only when both degrees are one, and there the line’s importance is exactly zero as well, so every admissible share of it gives the same contribution. The share is written as zero with the test taken before the division, and both nodes score 1. The source is silent on the case; the choice followscentrality_lhc(), notcentrality_iec(), and the help page says why. -
A score is never below its node’s degree, because
pcan never exceed either endpoint’s degree minus one, and the network’s total excess over degree is exactly the total importance of its lines — the two endpoint shares of a line sum to one. Raw scores are component-local: the measure never reaches past a node’s second neighbours. - Direction, weights, loops and parallel edges are projected onto the simple undirected skeleton the authors state their domain to be. Not costly, so
type = "all"includes it.
Centrality Batch 49 — immediate effects centrality
- Added
centrality_iec(), Friedkin’s immediate effects centrality. A node is scored by how quickly the rest of the network’s influence reaches it:c_IEC(j) = (n - 1) / sum_{i != j} m_ij, whereM = (I - Z + E Z_dg) diag(1/c)is the mean first passage time matrix of the influence chainW = A / rowSums(A),cisW’s left eigenvector at eigenvalue one andZ = (I - W + 1 c')^-1is the fundamental matrix. The sum runs down columnj, so a high score marks a node the network reaches fast. No new parameter. -
The influence matrix carries a unit self-loop, and the self-loop is load-bearing. The source sets
a_ii = 1before row-normalising, a construction it attributes to French (1956) and states twice on page- Its footnote 10 gives the reason — a strong network with
w_ii > 0must be regular, meaning aperiodic — and its footnote 9 the periodic counterexample a zero diagonal admits.
- Its footnote 10 gives the reason — a strong network with
-
This is not cograph’s
centrality_markov(), and the difference is not a rescaling. The package’s own candidate ledger recorded for several rounds that the two differed only in annversusn - 1numerator. That is wrong:markovalso omits the self-loop. The numerator is a constant factor and cannot reorder anything; the self-loop can and does. On the five-node starmarkovgives1.25, 0.161, ...whereiecgives0.5, 0.08, ..., and the two rank the nodes differently on 2 of the 21 connected five-node graphs.markovis unchanged; the two ship side by side, and each help page now points at the other. -
A reducible influence chain is refused, not extended. Without irreducibility the eigenvector of equation (9) has one dimension per closed class, so
cis undetermined anddiag(1/c)undefined. Worse, the closed form does not announce the failure: foriandjin different classesz_ij = 0, and equation (11) returns the entirely finitem_ij = z_jj / c_jwhere the true mean first passage time is infinite. Rather than publish a finite wrong number,iectests the chain by boolean closure before any solve and returnsNAat every node with acograph_undefined_measurewarning. In practice the requirement is a connected undirected graph or a strongly connected digraph. A one-node graph isNAtoo, equation (20) dividing byn - 1 = 0; an empty graph returns no scores. - Direction is kept, because
Wis a matrix of directed influence and rowiis what actoriattends to; there is no in/out/all variant, somode,cutoffandinvert_weightsare ignored. Weights are dropped deliberately —a_ii = 1is calibrated againsta_ij = 1, so a rescaling of the weights would silently re-weight each actor’s self-reliance against the network — and loops in the input are absorbed by the mandated diagonal while parallel edges collapse. The measure is marked costly and is therefore held back fromtype = "all". -
The source prints a complete numerical fixture and all 105 values reproduce. Table 1, pages 1492-1494, gives the measure to three decimals for every node of all 21 connected non-isomorphic five-node graphs. The published audit enumerates those 21 graphs from scratch, recovers each printed row’s node labelling from the table’s own TEC and IEC columns, and matches every value exactly against one of two stated printing rules rather than against a tolerance: 103 of 105 under round-half-away-from-zero at three decimals and 2 under truncation, the two being the exactly halfway
0.1875the paper prints as.187. The 105 printed values of the companion TEC column reproduce as well, three of them needing the same truncation rule.
Centrality Batch 48 — the Lhc index
- Added
centrality_lhc(), the Lhc index of Wang, Yang, Liu and Ma. A node’s influence isC(v) = sum_{u in Phi(v)} k_u (1 + TP(u)) / d^2(uv), a sum over the ballPhi(v)of radiuslhc_radiusin which each member contributes its degree, inflated by its share of the network’s triangles, discounted by the square of its distance; the index isLhc(v) = sum_{w in tau(v)} C(w), that influence summed over the open neighbourhood. The triangle share isTP(u) = NTS(u) / TNTSwithNTS(u)the number of triangles containingu. New parameterlhc_radius. -
The denominator is
TNTS, not the number of triangles, and the paper settles it rather than the Zoo. Immediately after definingTNTSthe source writes that “the total number of triangle structure exists in the network are1/3 * TNTS”, soTNTS = 3 * Deltaand the share sums to exactly one over the nodes. Entry 2.221 of the Centrality Zoo transcribes the structure of both equations correctly but names the denominator “Delta, the total number of triangular structures in the network”, which read literally is three times too small: on the Krackhardt kite that reading scores node 1 at 125.45 where the paper gives 100.15. cograph follows the paper. -
lhc_radiusis the source’s own parameter, exposed with the source’s default. The paper writes itd, states on page 4 that it “is set to be 2”, and sweeps it in section 3 over eleven real networks, reporting “the optimal value of d is about 2-3”. Atlhc_radius = 1the ball collapses to the neighbours; at or above the graph’s radius the score stops moving. The domain is a whole number of at least one and anything else raisescograph_bad_parameter. -
Triangle-free graphs are an explicit cograph decision. Every tree, star, path, even cycle and bipartite graph has
TNTS = 0, makingTP(u)a0/0at every node, and the source never mentions the case. SinceTNTSis a sum of nonnegative counts it vanishes exactly when every numerator does, so there is no share to distribute:TPis written as zero and the index reduces to the pure degree-over-squared-distance sum. The test is made before any division, so no0/0is evaluated. -
Raw scores are not component-local.
TNTSis a global sum, so attaching a disconnected component that carries a triangle rescales every score, while attaching one with no triangle – an isolate included – changes nothing. An isolate scores zero because its neighbourhood is empty; a singleton and every node of an edgeless graph score zero for the same reason. - Direction, weights, loops and parallel edges are dropped to the simple undirected skeleton the source defines on;
mode,cutoffandinvert_weightsare ignored, andnormalized = TRUEmax-scales, the source stating no normalization. - The source prints no numerical example, so there is no published per-node table to reproduce. Acceptance rests on three independent reference routes and on hand-derived closed forms for stars, complete graphs, rings and paths.
Centrality Batch 47 — randomized shortest paths betweenness
- Added
centrality_rsp_betweenness(), the simple randomized shortest paths betweenness of Kivimaki, Lebichot, Saramaki and Saerens. A Boltzmann distribution over the absorbing walks fromstotis tilted by an inverse temperature away from the unbiased random walk and towards low-cost walks, and a node scores the expected number of visits it receives summed over every ordered source-target pair,bet_i = sum_{s,t} (z_si / z_st - z_ti / z_tt) z_itwithZ = (I - W)^-1andW = (D^-1 A) o exp(-beta C). New parametersrsp_betaandrsp_cost. -
The published closed form is defined only on a strongly connected graph, and the source says what to do otherwise. Equation (15) divides by every entry of
Z, and Algorithm 1 takes a strongly connected graph as its input, but the text below equation (9) settles the general case: the derivation “holds only if there exists a path fromstot. Otherwise, naturally,eta_ij(s, t) = 0.” cograph evaluates the closed form masked by reachability, which reproduces equation (15) to machine precision whenever the graph is strongly connected and applies the source’s own zero rule when it is not. - Scores are therefore component-local. Two disjoint triangles score exactly what one triangle scores, and adding a disconnected component – an isolate included – leaves every existing score untouched.
-
A zero out-degree gives a derived zero, not an imputed one.
D^-1is undefined there, so cograph writes that row ofP^refas zero, which is the paper’s own killed random walk read at a node where the walker dies at once;Zthen hasz_ii = 1and the arithmetic gives exactly1 - 1 = 0. An isolate, a singleton graph and every node of an edgeless graph score zero for that reason.NetworkToolbox::rspbc()raises an error on such input andplot-sidecurrent_flow_betweennessreturnsNAon disconnected input; this measure can answer where those cannot, because(I - W)stays nonsingular whatever the connectivity. - Records a divergence from the CRAN reference implementation:
NetworkToolbox::rspbc()masks only the reciprocal half of the term and leaves then Diag(Z')half counting every source, so the two agree exactly on strongly connected input and part company on a disconnected graph. That function additionally rounds to zero decimals and shifts so its minimum is one, post-processing that is nowhere in the paper and is not copied here. -
rsp_betadefaults to 0.01, which is not the source’s number: the paper fixes no default and treats it as a modelling choice. 0.01 is the valueNetworkToolbox::rspbc()recommends, adopted so the two are comparable out of the box; it sits near the random-walk end, so raise it towards 1 and beyond to move the reading towards shortest paths. The domain isrsp_beta > 0and values outside it raisecograph_bad_parameter. -
rsp_costchooses how a weight becomes a cost, which the source leaves free:"inverse"(default) setsC = 1 / w, reading a weight as an affinity, as the CRAN reference hard-codes;"weight"setsC = w, reading it as a distance. The two coincide on a binary graph. Negative and non-finite weights raisecograph_bad_input, Algorithm 1 requiring a non-negative cost matrix. - Direction is read from the graph rather than from
mode, so a reversed input generally scores differently. Marked costly: one densen x ninverse, which the source itself calls the computational bottleneck atO(n^3)time andO(n^2)memory. - The paper prints no table of node scores on a small graph, so there is no published per-node example to reproduce. What is checked against the paper instead is its printed page 9 limit claim, that the score becomes proportional to degree as
betaapproaches zero from above on an undirected network; it holds, and does so at first order inbeta.
Centrality Batch 46 — hybrid characteristic centrality (HCC and EHCC)
- Added
centrality_hcc()andcentrality_ehcc(), the hybrid characteristic centrality of Liu and Zheng and its extension. HCC adds two normalised halves: the extended degreedelta * k + (1 - delta) * sum of the neighbours' degreesover its largest value, and the round in which an E-shell peel removes the node over the number of rounds. EHCC is the closed-neighbourhood sum of HCC, the focal node counted once. New parameterhcc_delta, default the source’s 0.5. -
The source’s printed algorithm contains a typo, and cograph implements the correction its own tables require. Step 3 of the E-shell procedure prints
S_p = arg maxwhile the same sentence callsS_p“the set of minimum nodes” and the paper’s table 2 heads its column “Minimum extended degree” with the increasing values 2, 2.5, 3, 4.5, 5, 6. The minimum reading reproduces every printed row; the literal maximum peel deletes a different set first and finishes in four rounds instead of six. -
The peel recomputes but equation (4) does not. Step 6 updates the extended degrees on the residual graph, which is what the printed table 2 minima require, while equation (4)’s
k^exandk^ex_maxare the original-graph values. The paper’s nodedsettles it: its original 9.5 gives the printed 1.86, its residual 6 gives 1.55. -
Raw scores are not component-local.
k^ex_maxandpos_maxare single global constants, so a disconnected addition rescales the two halves independently rather than by one common factor. - Records an unresolved disagreement with the source: table 3 prints
EHCC(g) = 10.01for its figure 1, where the exact value661/66is 10.015151… and rounds to 10.02. That printed cell is the truncation, but six other printed cells require rounding, so no single convention reproduces all twenty table 3 entries. The other 85 printed values reproduce. - Degenerate cases are stated on the help pages: an isolate has extended degree zero, the global minimum for every
hcc_deltain[0, 1], so it always leaves in the first round; an edgeless graph makes equation (4)’s first term0/0, written as zero, so every node of an edgeless graph — a singleton included — scores exactly 1.hcc_deltaoutside the source’s stated[0, 1]raisescograph_bad_parameterrather than being extended. - Documents a divergence from the Centrality Zoo: it calls the E-shell decomposition “a variant of k-shell decomposition”, which it is not — there is no outer level loop and no repeat-until-stable inner loop. On a four-node path the k-shell reading puts all four nodes in one shell where the E-shell peel takes two rounds.
Centrality Batch 45 — KED method
- Added
centrality_ked(), the KED method of Chen, Xiao, Zeng and Zhang: the degree, weighted by one plus the normalised entropy of the neighbours’ degrees, timesexp(K / N)for the neighbour-degree sumKand the whole graph’s orderN. Two nodes with the same degree and the same number of second neighbours are separated by how evenly their neighbours carry the onward paths. It takes no parameters: equation (6) is a bare product. -
The logarithm base is not a convention to choose. Equation (2) divides the neighbour-degree entropy by the entropy of the uniform distribution on
koutcomes, so the base cancels top and bottom. A full base-ten reading gives identical scores, which is asserted to 60 digits on every verification fixture. -
Raw scores depend on the whole graph’s order, and can reorder.
Nis the vertex count of the whole network, andexp(K / N)shrinks a large neighbour-degree sum more than a small one, so adding a disconnected component is not a rescaling: on a seven-node example in the tests, one extra isolate swaps two nodes’ places. - Records that the source’s stated range
1 <= D <= eis not general. It needsK <= N, which holds on the sparse toy networks of its figure 1 and on only 1,598 of the 5,532 verification fixtures; every node of the five-clique hasK = 16againstN = 5. cograph implements the formula, not the range claim, and raises an error rather than returningInfif the exponent ever leaves the range ofexp(). - Documents a divergence from the Centrality Zoo: its section 2.215 drops the
1 +fromE, dividesKby the largest cluster degree instead of byN, and adds tunable exponents that appear nowhere in the paper. On the source’s own figure 1 that reading gives 13.5914 and 6.5672 where the paper prints 25.9187 and 19.2212. cograph implements the paper and offers no Zoo variant. - Two cases the source never mentions are cograph decisions, stated on the help page: a node with one neighbour has
0/0for its normalised entropy and takes zero, and an isolate scores zero.
Centrality Batch 44 — Local neighbor contribution (LNC)
- Added
centrality_lnc(), the local neighbor contribution of Dai, Wang, Sheng, Sun, Khawaja, Ullah, Dejene and Duan: the chance that a node picking a neighbour at random picks a given one and misses the rest, scaled by its degree, multiplied by the sum of its neighbours’ degrees weighted by their degree centralities. It takes no parameters, which the source advertises as one of its contributions. -
Raw scores depend on the whole graph’s order. The
1 / (n - 1)comes from the source’s degree centrality, wherencounts every node in the network rather than in the component, so adding a disconnected component multiplies every score by(n - 1) / (n' - 1). The ranking is untouched; the raw values are not. - Implements the source’s printed numbers, not its printed equations, and says so. Equations (4) and (5) sum over
j = 1tok, andkis described three incompatible ways: the prose calls it the number of nearest and next nearest neighbours, Algorithm 1 sets it to the degree, and equation (5) read literally carries one factor of the degree too many and returns 6.75 where the paper prints 1.6875. Inverting each of the eleven printed influences givesk = d^2in (4) andk = din (5), the reading implemented here; the alternative literal split of the two factors gives the same product, so the measure itself is unambiguous. - Documents a divergence from the Centrality Zoo: its section 2.238 replaces the focal node’s contribution probability with each neighbour’s and the binomial count with the size of the two-hop neighbourhood. That formula reproduces none of the eleven printed values and inverts the paper’s headline ranking, so no Zoo variant is offered.
- Isolates and the single node of a singleton graph score zero as an explicit cograph extension: the source has no value where the degree, and hence
1 / d, is undefined, and no result is silentlyNaN.
Centrality Batch 43 — Iterative resource allocation (IRA and IIRA)
- Added
centrality_ira(), the iterative resource allocation of Ren, Zeng, Chen, Liao and Liu: every node starts with one unit of resource and hands it repeatedly to its neighbours in proportion to the receiver’s centrality until the amounts stop moving.ira_mass,ira_alpha,ira_tolandira_max_iterexpose the source’stheta,alpha,epsilonand iteration bound. Every non-empty column of the allocation matrix sums to one, so a component’s scores sum to its vertex count. - Added
centrality_iira(), the improved variant of Zhong, Liu and Shang, which scales each share by1 - (1 - beta)^k. That factor is strictly below one, so the resource decays geometrically; the source fixes the step count instead of a tolerance.iira_betaandiira_stepsdefault to the source’s 0.2 and 50, the rawI(50)is returned so the printed example is reproducible, andnormalized = TRUEmax-scales it. Raw IIRA scores from different connected components are on different exponential scales and must not be compared. -
irareports non-convergence instead of hiding it. The allocation matrix is a reversible walk, so on a bipartite component it has an eigenvalue of exactly -1 whose coefficient in the all-ones start is the difference between the two class sizes. When those differ, the resource settles into a period-two cycle and no tolerance is ever met: the three-star alternates for ever between3, 1/3, 1/3, 1/3and1, 1, 1, 1.centrality_ira()then stops atira_max_iter, raises a classedcograph_no_convergewarning naming the largest remaining change, and returns that parity-dependent iterate. Neither source mentions this case. - Documents two divergences from the Centrality Zoo: its IRA entry states the transpose and asks for an eigenvector, which sidesteps rather than reports the bipartite case, and its IIRA formula pairs the numerator index with the wrong neighbourhood, giving a matrix that is stochastic in neither direction and reproduces neither printed quantity. cograph implements the papers.
- Records that one printed table entry is reproduced only from the exact steady state: the source rounds
15/8up to1.88, while the iteration approaches that limit from below and returns1.8749998, which rounds to1.87. No author software exists for either measure.
Centrality Batch 42 — Neighborhood (neighbor distance) centrality
- Added
centrality_neighbor_distance(), the neighborhood centrality of Liu, Tang, Zhou and Do: a benchmark centrality plus its decayed sums over the non-backtracking walks that leave the node.nd_order,nd_decayandnd_massexpose the source’sn,aandtheta, and the defaults (degree, two steps, 0.2) are the setting the Centrality Zoo calls neighbor distance centrality. - Implements the source’s nested sums, in which each level excludes only the node the walk just came from, so an endpoint reached by several walks is counted once per walk. This is not a sum over distance shells. The Zoo’s paraphrase writes k-hop neighbour sets instead; the two agree on trees and differ on any graph with a triangle or a short cycle, and the difference is documented rather than offered as a variant.
- Documents that the source prints no table of node scores, so there is no published numerical fixture, and that no author software exists.
Centrality Batch 41 — Relative-entropy integrated evaluation
- Added
centrality_relative_entropy(), which turns several indexes into discrete distributions and returns the unit-sum distribution with the smallest total relative entropy to all of them: the normalised geometric mean of the index distributions. -
re_indexeschooses the constituents from six the source both defines and gives an evaluating direction (degree, closeness, betweenness, constraint, and the two post-deletion destructiveness indexesn_componentsandlargest_component);re_negativeoverrides which of them are read as “smaller is more important”. The default reproduces the published Kite study’s four-index column, and all three of its printed integrated columns are reproduced. - Documents the two non-standard conventions the source uses – its constraint sums over every node rather than over the neighbours, so it is not Burt’s constraint, and its betweenness counts ordered pairs – along with the reachable-partner closeness extension used outside its connected domain. An index that is zero at every node leaves the definition without a value and raises a classed
cograph_undefined_indexerror rather than returning zeros. -
centrality()no longer lets one undefined measure end a whole tier. A measure named inmeasuresorincludestill raises, but whentype = "basic","extended"or"all"supplied it, an undefined result becomes acograph_undefined_measurewarning and anNAcolumn, matching what the community-partition measures already do withoutmembership.
Centrality Batch 40 — DK-based gravity model
- Added
centrality_dkgm(), whose mass is the degree k-shell index: the original degree plus a shell number refined by the stage at which the node left that shell during the k-shell peeling. - Default
dkgm_radius = 2matches the paper’s printed nine-node example, which the implementation reproduces along with its removal stages, improved shell indices and DK values."auto"applies the paper’s own half-mean-distance rule with cograph rounding. - Documents the published pseudocode/prose disagreement over removing nodes of degree exactly
kversus at mostk, the one-shell placement of isolates, and the single global stage denominator, which makes raw scores depend on disconnected components.
Centrality Batch 39 — Mixed gravitational centralities
- Added
centrality_mixed_gravity()andcentrality_extended_mixed_gravity(), using focal core numbers and partner degrees, with an optional outer sum over immediate neighbors. - Default radius three follows the explicit published reproduction; radius one exposes the Zoo summary’s literal interpretation. Both functions document source provenance, skeleton projections and normalization.
Centrality Batch 38 — Multi-characteristics gravity model
- Added
centrality_mcgm()with the published adaptive coefficient and default radius two, plus explicit radius and coefficient overrides. - Reproduces the nine-node published example. Uses a simple undirected skeleton and documents the eigenvector convention for disconnected graphs. Undefined automatic coefficients require an explicit override.
Centrality Batch 37 — SpectralRank
- Added
centrality_spectralrank()with optional scalar or node-specific diagonal priors. Scores use outgoing neighbors in an augmented graph. - Preserves the paper’s normalization over original and ground nodes; optional package normalization rescales original nodes alone. Uses the Perron eigenvector also on edgeless graphs where plain iteration oscillates.
Centrality Batch 36 — ControlRank
- Added
centrality_controlrank(), the smallest eigenvalue of each grounded symmetric row-Laplacian, retaining original degrees after node deletion. - Supports nonnegative weighted graphs, an explicit outgoing orientation for directed inputs, and signed directed scores. Reproduces the published bi-star example and is marked costly because it solves one spectrum per node.
Centrality Batch 35 — Map equation centrality
- Added
centrality_map_equation()for fixed one-level or leaf-module partitions, with recorded node and unrecorded link teleportation. - Coding conventions are explicit:
paperincludes module-exit flow as defined in the equations;infomapreproduces the author’s visit-only implementation and published table. Both conventions have independent numerical verification. Stable arithmetic retains extremely small scores. - Invalid missing, nonfinite, nonscalar or nonnumeric damping inputs now receive the centrality argument error instead of an incidental R error.
Centrality Batch 34 — Node and Neighbor Layer Information
- Added
centrality_ninl()with the full finite iteration family (default three) and optional radius overrides. The initial score sums degrees within the ceiling of average path length, then propagates through neighbors. - Reproduces all 52 entries in Zhu and Wang’s published example. Uses simple undirected unweighted topology, with explicit disconnected-graph and normalization conventions. Preserves bipartite alternation at large orders.
Centrality Batch 33 — Localized bridging and Extended LBC
- Added
centrality_localized_bridging()andcentrality_extended_local_bridging(): one-hop and two-hop ego betweenness multiplied by the original-graph bridging coefficient. Uses simple, undirected, unweighted topology; the two-hop variant is marked costly. - Corrected the Zoo localized-bridging mapping.
local_bridgingretains its existing inverse-degree product, which is a different score.
Centrality Batch 32 — BG-index and beta power
- Added
centrality_beta_measure()with positive and negative directed variants. Successors share one unit equally among their predecessors; the negative variant reverses the graph. Loops and duplicate arcs are removed, weights ignored, and isolates score zero. - Covers both Zoo labels BG-index and beta-measure. Independent predecessor choice enumeration and the original published diamond example verify numerical scores, with direction and normalization checked through the API.
Centrality Batch 31 — Expected Force and its degree adjustment
- Added
centrality_expected_force()andcentrality_modified_expected_force()from Lawyer’s two-event definition, with explicit sequence multiplicity, boundary-edge counting, direction and exhausted-force conventions. - Corrected the Zoo Expected Force mapping:
expectedcomputes neighbor-degree sums and remains available under that definition. ExF now maps toexpected_force; the original ExFm candidate maps to the modified function. - Verified against independent event enumeration and pinned author C++ on its connected undirected domain. No author code is included in the package.
Centrality Batch 30 — Proximal betweenness
- Added
centrality_proximal_betweenness()with source, target, sum and union variants from Brandes. Uses directed unweighted shortest paths, excludes endpoints, and explicitly preserves ordered-pair raw scaling. - Independent path enumeration and exact integer adjacency powers verify both orientations and overlap counting. Nonfinite path counts raise errors.
Centrality Batch 29 — Bridging capital
- Added
centrality_bridging_capital()with a finite walk horizon and optional source-destination information values. It follows Jackson’s single-entry deletion definition using transmission probabilities between zero and one. - Independent matrix-power and walk-enumeration checks verify repeated-edge counting, direction, loops and valued information. Native tracking avoids cancellation; this dense, costly measure must be requested explicitly.
Centrality Batch 28 — Coleman-Theil hierarchy
- Added
centrality_coleman_theil(), measuring concentration of Burt’s dyadic constraints using mutual tie weights. Follows the author’s explicit isolate-zero and single-contact-one conventions, with organizational multipliers fixed at one. - Verified against NetworkX local constraints, exact rational calculations and high-precision entropy checks. Stable arithmetic preserves small departures from uniformity; weights, direction and parallel ties have documented handling.
Centrality Batch 27 — X-degree
- Added
centrality_x_degree(), counting four-edge nonbacktracking walks centered at each node using original neighbor excess degrees. It uses the simple undirected unweighted skeleton and supports maximum normalization. - Verified against pinned author code, independent nonbacktracking matrix blocks and explicit walk enumeration, including all simple labeled graphs through five vertices. This adds a static score; it does not perform the paper’s iterative immunization strategy.
Centrality Batch 26 — LineRank
- Added
centrality_linerank(), using directed line-graph walks or the ordinary undirected line graph clarified by Kosa et al. (2015). - Exposes probability aggregation and the original pseudocode’s additional edge-weight aggregation as explicit
linerank_aggregationchoices. Supports loops and remaining parallel edge states with documented conventions, uniform dangling redistribution and damping in [0,1). - Uses a native dense stationary solve and is marked costly. Verification compares NetworkX line graphs/PageRank and Markov-chain tree cofactors. The original pseudocode’s inconsistent normalization is not replicated.
Centrality Batch 25 — random walk decay
- Added
centrality_random_walk_decay()withrwd_decayand optionalrwd_node_weights. Scores sum discounted first-arrival probabilities, including the starting node. Retains directed flow, weighted transitions and loops; sinks terminate the walk without restarting it elsewhere. - Supports personalized starting mass, zero decay as a limit, disconnected graphs and normalized scores when raw sums overflow. Per-target absorbing solves make the measure costly; request it explicitly.
- Independently verified using full-resolvent ratios, explicit first-visit series, exact rational arithmetic and high-precision calculations. Reproduces the published Example 4 and 5 tables; the inconsistent Example 3 values are retained as a source discrepancy.
Centrality Batch 24 — graph regularization centrality
- Added
centrality_graph_regularization()with finite nonnegativegrc_gamma, default one. Computes reciprocal diagonal entries of the inverse regularized weighted Laplacian. Isolates and zero regularization score one; disconnected components are independent before normalization. - A component spectral calculation supports extreme regularization and uniform weight scales without overflowing their product. Unresolvable weight ranges or positive spectra raise explicit numerical errors.
- Verified against SciPy direct solves, determinant ratios, high-precision arithmetic and convergence of the retained author implementation. The author’s default ten-term approximation is audited separately and is not claimed to give identical values.
Centrality Batch 23 — adaptive LeaderRank
- Added
centrality_adaptive_leaderrank(), weighting every destination by its original open-neighborhood H-index and adding a ground node with H-index one. Retains source total mass N and omits the ground score without redistribution. Zero H-indices receive zero stationary scores; an all-zero H-index vector yields NaN. -
alr_h_modeselects the H-index convention: all (default), out or in. The paper leaves its directed H-index choice unspecified; these choices are explicit cograph conventions, while resource flow retains input arcs. The focal node is excluded from the H-index calculation. - Verified against independent NetworkX iteration, Markov-chain tree cofactors and a reversible-conductance identity for undirected inputs.
Centrality Batch 22 — weighted LeaderRank
- Added
centrality_weighted_leaderrank()with finitewlr_alpha, default one. Original directed arcs retain unit weight and ground-to-node weights depend on original in-degree. Input edge weights are ignored. Undirected edges are represented as opposite arcs. - Uses the original paper’s N+1 initial mass and omits the ground score without redistribution. Zoo’s N-mass initialization differs by a constant factor; max-normalized scores agree. Negative exponents require positive in-degrees; positive exponents on edgeless graphs return NaN.
- A native stationary solve handles periodic chains and shifted logarithms avoid overflow in degree powers. Verification uses NetworkX lazy-chain iteration, Markov-chain tree cofactors and 100-digit stress references.
Centrality Batch 21 — global structure models
- Added
centrality_global_structure()(GSM),centrality_hybrid_global_structure()(H-GSM) andcentrality_improved_global_structure()(IGSM). GSM uses coreness; H-GSM combines degree and coreness; IGSM uses degree. The latter two use their published adaptive distance exponents. All three use the simple undirected skeleton, with explicit zero contributions for unreachable partners and global size/means that include isolates. - IGSM follows the equation reproduced in Mukhtar et al. (2023); the original Zhu and Wang (2022) full text was unavailable. Its zero or negative distance exponents on sparse disconnected graphs are retained.
- Verified against independent NetworkX calculations, exhaustive small-graph core enumeration, and published focal calculations. H-GSM uses logarithmic sums so normalized results remain available when raw scores or self-influence exceed double precision; 100-digit reference stress checks cover both overflow and very small normalized values.
Centrality Batch 20 — exogenous centrality
- Added
centrality_exogenous()with degree, betweenness and adjusted reverse-closeness bases. Measures the contribution to other nodes’ centrality when the focal node is deleted. Supports directed base directions and retains negative betweenness contributions. - Reverse-closeness retains the original graph size after deletion. All bases use simple binary topology and raw scores before any optional final normalization. Repeated graph deletion places this measure in the costly tier. Verification includes independent NetworkX scores, explicit path enumeration and a documented audit of discrepancies in the original paper’s Florentine table.
Centrality Batch 19 — improved closeness
- Added
centrality_improved_closeness()withicc_alphain [0,1], default 0.2, following Luan et al.’s shortest-path multiplicity formula. Uses the simple undirected skeleton; alpha zero recovers ordinary normalized closeness on connected graphs. Disconnected graphs and singletons score zero under explicit cograph conventions. - Verified against exact integer adjacency powers, NetworkX shortest-path enumeration and igraph’s closeness limit. Logarithmic path counting avoids overflow on graphs with more shortest paths than doubles can hold.
Centrality Batch 18 — weighted clustering degree algorithm
- Added
centrality_cda()withcda_alpha, default 0.5. Returns Wang et al.’s propagation-capability score using degree, strength, Barrat clustering and weighted neighbor contributions. The full weighted calculation retains original weight units and a global maximum weight. - Verified against igraph Barrat clustering and independent Python neighbor-pair enumeration across mixing parameters, including endpoint scaling, disconnected inputs, labels and input projections.
Centrality Batch 17 — extended coreness and gravity
- Added
centrality_extended_coreness(), the two-step aggregation of original core numbers, andcentrality_extended_gravity(), the sum of immediate neighbors’ raw k-shell gravity scores. Both use the simple undirected skeleton and assign isolates zero. - Extended gravity supports
gravity_radius, default three as in Ma et al. The radius applies around each neighbor before the outer sum. Independent checks use NetworkX cores and distances and an exhaustive core-number oracle on small graphs, covering multiple radii and input projections.
Centrality Batch 16 — node resistance curvature
- Added
centrality_resistance_curvature(), implementing Devriendt and Lambiotte’s conductance-weighted node curvature component by component. Raw scores can be negative; isolates score one. Directed projection, zero conductances, normalization and numerical limits are documented. - Independent checks use NetworkX effective resistance and exhaustive weighted spanning-tree degree expectations on small graphs. Dense electrical solves make this an explicitly requested costly measure.
Centrality Batch 15 — dynamics-sensitive and Malatya centrality
- Added
centrality_dynamics_sensitive()withds_beta,ds_muandds_steps, including the full recovery-rate family from Liu et al. Recovery rate one recovers the finite-diffusion formula listed by Zoo; recovery zero supports the paper’s SI case. These are linearized scores, with their interpretation and numerical limits documented explicitly. - Added
centrality_malatya(), the static degree-ratio sum. On nonisolated nodes it is exactly the reciprocal of the bridging coefficient. Both additions use the simple undirected skeleton and assign isolates zero. - Verification uses independently expanded matrix/walk formulas and NetworkX neighbour-degree arithmetic, with documented parameter limits.
Centrality Batch 14 — finite diffusion and dynamical importance
- Added
centrality_diffusion_centrality()withdiffusion_qanddiffusion_steps. It evaluates Banerjee et al.’s finite weighted walk sum, separate from existing diffusion degree and the TNA power series. Directed edges follow their outgoing orientation; weights, loops and repeated walks are supported. Probability interpretation and default parameter choices are documented explicitly. - Added
centrality_dynamical_importance(): relative spectral-radius loss on node deletion, recomputed directly. It supports nonnegative directed weighted graphs, removes loops and returns NaN when the original radius is zero. This costly measure is held back from the default all tier. - Independent numerical checks use explicit walk enumeration, NumPy matrix powers and eigenspectra, and SymPy exact characteristic polynomials.
- Fixed the existing TNA power-series method’s missing binary fallback for unweighted igraph inputs. Normalization now preserves entirely undefined or empty result vectors without an empty-maximum warning.
Centrality Batch 13 — volume and maximal cliques
- Added
centrality_volume()withvolume_radiusandcentrality_mcc(), also available throughcentrality(measures = ). Both use the simple undirected skeleton. Volume sums original degrees over closed hop neighbourhoods; MCC sums factorial contributions from maximal cliques. - MCC explicitly excludes singleton cliques (isolates score zero), reports numerical overflow, and is held back from the default
type = "all"tier because clique enumeration has exponential worst-case cost. - Verification uses NetworkX neighbourhoods and maximal cliques, plus exhaustive recognition of maximal cliques on graphs of up to ten nodes.
- Corrected the catalogue’s existing diffusion entry: default diffusion degree and the TNA power series are separate formulas. The Banerjee diffusion-centrality candidate remains pending.
Centrality Batch 12 — verified parameter candidates
- Added
centrality_truss(),centrality_mdd(),centrality_bridging_coefficient(),centrality_godfather()andcentrality_support(), also available throughcentrality(measures = ). They use the simple undirected skeleton.mdd_lambdacontrols the exhausted-degree weight; truss numbers use the k-2 triangle convention. - Documented LocalRank as the existing
semilocalmeasure on simple undirected graphs. Corrected parameterized Zoo lookup calls and replaced unsupported equivalence claims based solely on rank correlation. - Numerical checks cover NetworkX, centiserve and independent combinatorial references. The original 160 candidate rows remain tracked in
docs/zoo/parameter_candidate_status.csv.
Centrality Batch 7 — Centrality Zoo comparison batch
Five measures chosen from the Centrality Zoo correlation study (Shvydun 2025; 349 measures, 648 ICON networks, average Kendall tau) as the ones with the lowest rank redundancy against what centrality() already computed (maximum tau with any existing measure in parentheses). All are implemented in base R on matrices (R/kernels-batch7.R) with thin igraph glue and one exported verb each (R/centrality-batch7.R).
-
centrality_distance_entropy()(tau 0.30) — Stella & De Domenico (2018). Normalised Shannon entropy of a node’s hop-distance profile; closeness is the mean of that profile, this is its spread. The normaliser islog(M - m + 1)so a uniform profile scores exactly 1 (the printed formula’slog(M - m)is undefined for two distances). -
centrality_local_dimension()(tau 0.50) — Pu et al. (2014). OLS slope ofln B(r)onln r, ball including the centre. Reproduces the worked example of Wen & Deng (2019) exactly (0.9231). Lower = more influential. -
centrality_local_information_dimension()(tau 0.38) — Wen & Deng (2020). Entropy-weighted local dimension over boxes up to half the eccentricity. Higher = more influential. Single-box nodes use the paper’s discretised derivative. -
centrality_modularity_vitality()(tau 0.40) — Magelinski, Bartulovic & Carley (2021).Q(G, C) - Q(G - i, C \ i)under a fixed partition; positive = community hub, negative = bridge. Closed-form vectorised update (one matrix product for all nodes); matches brute-forceigraph::modularity()after deletion on random directed, undirected and weighted graphs. Requiresmembership; wrong-length input raisescograph_bad_membership. -
centrality_neighborhood_connectivity()(tau 0.64) — Maslov & Sneppen (2002). Mean neighbour degree, isolates 0; equalsigraph::knn(weights = NA), withmodesupport.
The three distance-scaling measures are hop-count measures and ignore edge weights (as gravity and collective_influence already do); they share one unweighted all-pairs matrix per centrality() call.
Centrality Batch 8 — the Zoo’s “on the way” measures
The twelve measures the batch 7 lookup listed as “on the way” are now implemented (thirteen centrality() measures), each verified against an exact brute-force definition or the source paper’s own numbers, plus an independent Python reference written from the paper (kept in local_testing_and_equivalence/batch8/, not shipped). Base-R kernels in R/kernels-batch8.R, verbs in R/centrality-batch8.R.
-
centrality_shapley_game1(),_game2(),_game3()— Michalak et al.- closed-form Shapley values of the one-hop,
shapley_k-neighbour andshapley_cutoff-hop coverage games. Equal to exact Shapley values from full coalition enumeration on random graphs of up to 8 nodes (with isolates, loops, several components), directed extension included.
- closed-form Shapley values of the one-hop,
-
centrality_access_information(),centrality_hide_information()— Rosvall et al. (2005) / Sneppen et al. (2005) search information, averaged from and to each node; shortest-path DAG accumulation, no path enumeration. Equal to explicit all-shortest-paths enumeration; reproduces the papers’ star and complete-bipartite values. Disconnected graphs average over each node’s reachable set. -
centrality_rumor()— Shah & Zaman (2011) rumor centrality on each node’s BFS tree, log scale.exp()of it equals brute-force spreading-order counts on trees; reproduces the paper’s Fig. 5 (8, 12, 2, 3, 3). -
centrality_community_hub_bridge()— Ghalmane, El Hassouni & Cherifi- raw hub-bridge score (needs
membership;cograph_bad_membershipon bad input).
- raw hub-bridge score (needs
-
centrality_entropy_variation(of = "degree" | "betweenness")— Ai (2017) signed entropy drop on node deletion; degree variant in closed form. Equal to the author’s own R code path to 1e-15 and to the paper’s Table 2 quantiles on its 4234-node network. -
centrality_s_shell()— Liu, Tang, Do & Hui (2017) strength-based shell index with asymmetric topological weights (s_shell_a, default 0.5). The Zoo’s “s-shell index” is this measure, not the Eidsaa-Almaas s-core. Shells verified against the maximal-subgraph definition;a = 0gives k-core dense ranks. -
centrality_degree_discount(),centrality_single_discount()— Chen, Wang & Yang (2009) greedy seed-selection orders (discount_p), scored likevoterank(first selected = 1). -
centrality_ncvoterank()— Kumar & Panda (2020) neighbourhood-coreness VoteRank (ncvote_theta). The original article could not be obtained; the definition follows the Zoo encyclopedia and three restatements, and the coreness normalisation (by its maximum) is a documented choice. Its VoteRank limit reproducesnetworkx.voterank.
The Centrality Zoo lookup article and coverage document were regenerated: 82 Zoo measures are now available in cograph and nothing is “on the way”.
centrality() tiers, and a catalogue of the measures
-
type = "all"no longer runs the measures whose cost grows steeply with network size. Four are held back:infection,two_way_rw,node_contraction_improvedandentropy_variation_betweenness. On an 81-node graphinfectionalone took 611 seconds while every other measure together took about five, so a singletype = "all"call could take minutes by accident.type = "basic"(the default) andtype = "extended"are unchanged. -
New
includeargument puts them back:include = "costly"for all four, or name the ones you want. Naming a measure inmeasures =always computes it whatever its cost, so nothing became unreachable. -
list_centralities()(new export) is a tidy table of every measure with the facts you need before reading a column:orientation(which end of the scale marks a prominent node),mode_aware,needs_membership,uses_weightsandcostly. Twelve measures are oriented so that a low value marks the more central node, includingeccentricity,constraint,heatmapand the local-dimension family; sorting their column the usual way puts the periphery on top.list_centralities(orientation = "lower")lists them. The measure lists now live in one place that bothcentrality()andlist_centralities()read, and a test asserts the two agree.
Bug fixes
-
katznow warns with acograph_katz_divergedcondition whenkatz_alphais too large for the graph. Katz converges only foralpha < 1 / rho(A); above it the linear solve still returns numbers, but they are not Katz scores and can be negative. The default 0.1 is invalid on any graph whose spectral radius exceeds 10, which includes many weighted networks. The check costs nothing on the happy path and the warning names the valid bound. -
alphaandpowernow raise a classedcograph_singular_systemerror instead of surfacing a bare LU factorization message from igraph whenI - alpha Ais singular. -
?centrality_dmncgains a “Divergence from centiserve” section.centiserve::dmnc()counts the largest component’s edges in the wrong index space: its membership vector indexes the neighbourhood subgraph but is used to subset the original graph. The two disagree on 14 of the 34 karate nodes even at a matched epsilon, and reproducing that indexing exactly reproduces centiserve’s output. cograph counts the edges of the component it actually found. The catalogue’s equivalence claim was corrected.
Centrality Batch 9 — the remaining Zoo measures with a pinned definition
Twenty more measures (R/kernels-batch9.R, R/centrality-batch9.R), each researched from its source paper by a dedicated agent and verified against published tables, brute-force definitions and an independent Python reference (kept in local_testing_and_equivalence/batch9/, not shipped).
- Community-aware (need
membership):centrality_community_based()(Zhao et al. 2015; reproduces the paper’s Table 1 and Tulu et al.’s Table 1),centrality_comm_centrality()(Gupta, Singh & Cherifi 2016;comm_r),centrality_community_mediator()(Tulu, Hou & Younas 2018; base-2 entropy reproduces its Table 1). - Dimension family:
centrality_local_dimension_fixed()(Silva & Costa 2013;ld_radius),centrality_fuzzy_local_dimension()(Wen & Jiang 2019; reproduces its kite Table 1 and karate top ten in order),centrality_local_volume_dimension()(Li & Deng 2021; definition from the authors’ later preprint, flagged). - VoteRank family:
centrality_wvoterank()(Sun et al. 2019; reproduces all sixty numbers of its Figure 1),centrality_enrenew()(Guo et al. 2020; reproduces its Figure 1;enrenew_depth),centrality_voterank_plus()(Liu et al. 2021; matches the authors’ code;voterank_lambda). -
centrality_node_contraction()andcentrality_node_contraction_improved()(Tan, Wu & Deng 2006; Wang et al. 2011; reproduce Table 1 and the path closed forms;contraction_rho). The Zoo entry’s “removal” wording is wrong; the sources contract. -
centrality_two_way_rw()(Curado et al. 2022; reproduces the paper’s toy example including every fraction; O(n^4)). - Local measures:
centrality_heatmap()(Duron 2020; reproduces Table 1; lower = more central),centrality_flow_coefficient()(Honey et al. 2007, BCT form; equals one minus clustering on undirected graphs),centrality_local_entropy()(Nie et al. 2016),centrality_weighted_h_index()(Gao et al. 2019),centrality_redundancy()(Burt 1992; Borgatti’s worked example). -
centrality_weighted_kshell()(Garas, Schweitzer & Havlin 2012;wks_alpha,wks_beta; Figure 1 example and Table 2 core size),centrality_renewed_coreness()(Liu, Tang, Zhou & Do 2015; Figure 1 and all twelve percentages of its Table S1; the Zoo’s transcription is off by one),centrality_geodesic_kpath()(Borgatti & Everett 2006; paths counted with multiplicity;centiserve::geokpathcounts nodes instead).
Not shipped, with reasons recorded in the coverage document: DegreePunishment, improved WVoteRank and local degree dimension (source articles unobtainable, definitions rest on the Zoo alone) and multi-local dimension (a rescaling of local dimension for every q outside (0, 1)).
New pkgdown article Centrality Zoo lookup answers “is the Zoo measure I want in cograph?”: every Zoo measure listed once under Available, Almost identical (tau >= 0.99, with the cograph measure to use), Near-duplicate (0.90 <= tau < 0.99), On the way, or Not available. docs/CENTRALITY-ZOO-COVERAGE.md records the full intersection of the Zoo matrix with cograph: which Zoo measures are rank-identical to an existing cograph measure (and therefore not worth adding), which are near-duplicates, and the remaining ranked candidates.
Centrality Batch 10 — the gaps against the other centrality packages
docs/CENTRALITY-CROSS-COVERAGE.md counted, in both directions, what each R and Python centrality package reaches of the Zoo. The five node measures that other packages offered and centrality() did not are now implemented (R/kernels-batch10.R, R/centrality-batch10.R), each verified against the package whose gap it closes:
-
centrality_local_efficiency()— Latora & Marchiori (2001). Global efficiency of the subgraph induced on a node’s neighbours, the node removed. MatchesbrainGraph::efficiency(type = "local")and the networkx induced-subgraph form on 25 random graphs. Note thatigraph::local_efficiency()measures those distances through the rest of the network and so reports larger values;network_local_efficiency()keeps its igraph parity and its help page now says so. -
centrality_s_core()— Eidsaa & Almaas (2013). The weighted k-core: the largest strength threshold whose core still contains the node. Matchesigraph::coreness()on unweighted graphs and a brute-force reading of the definition on weighted ones.brainGraph::s_core()returns the peeling round instead, which is documented as a divergence. -
centrality_fragmentation()— Borgatti (2006). Distance-weighted fragmentation after deleting the node; matcheskeyplayer::fragment(). This is the Zoo’s “Distance-weighted fragmentation”, taking cograph’s Zoo coverage to 103 of 349. -
centrality_kpath()— Sade (1989). Simple paths of length at mostkpath_lenthat the node lies on; matches the per-vertex counts ofsna::kpath.census()for k = 2 and 3, directed and undirected. -
centrality_epc()— Lin et al. (2008), the cytoHubba edge percolated component. Recovers the exact bond-percolation mean on small graphs and matches the normalisation ofcentiserve::epc(). Monte Carlo: passepc_seedfor a reproducible value; the caller’s random stream is restored.
fragmentation and epc join the costly list, so type = "all" holds them back (include = "costly" or naming them restores them).
Three of the reported gaps turned out not to be gaps at all, and the document now says so with the evidence: centiserve::closeness.latora() is cograph’s harmonic exactly, centiserve::communibet() is communicability_betweenness exactly, and brainGraph::efficiency(type = "nodal") is harmonic over n - 1. Two remain unimplemented and are listed with the reason: the link-community centrality of Kalinka & Tomancak (its reference package linkcomm is archived, so no equivalence check is possible) and keyplayer::kpset() (a set search, not a node measure).
Centrality Batch 11 — tuning the families cograph already had
160 of the Zoo’s measures sit at a rank correlation of 0.90 or better with something centrality() already computes (docs/zoo/parameter_candidates.csv), which suggests many are the same family at a different setting rather than different ideas. The first five investigated bear that out, and four new measures plus two arguments cover seven more Zoo labels (coverage 103 -> 110 of 349):
-
centrality_length_scaled_betweenness()— Borgatti & Everett (2006), Brandes (2008) Algorithm 5. Betweenness with each separated pair weighted by1 / d(s,t). -
centrality_delta_betweenness()— Agneessens, Borgatti & Everett (2017). Betweenness with the pair weight(d - 1)^-delta(betweenness_delta, default 1);delta = 0is ordinary betweenness. -
centrality_ego_betweenness()— Everett & Borgatti (2005). Betweenness inside the node’s own ego network. Close toeffective_size, and a test pins that it is not a function of it. -
centrality_delta_closeness()— Agneessens et al. (2017) eq. 2.sum_j d_ij^-delta / (n-1)(closeness_delta, default 1). One exponent spans the family:delta = 1isharmonicovern-1,delta = 2ishararyovern-1, a largedeltaapproaches degree,delta = 0counts the reachable set. -
Bounded-distance (“k-”) betweenness needs no new measure:
centrality(x, measures = "betweenness", cutoff = k)already computes it, verified against a brute-force reading of the definition on directed and undirected graphs. It is now mapped as covered.
All four are exact under a brute-force enumeration of weighted geodesic pairs (local_testing_and_equivalence/batch11/run_equivalence.R, 6 blocks, 6 PASS).
Bug fixes
-
gravitycomputed a formula that appears in no paper. It summeddeg(j) * kshell(j) / d(i,j)^2over every reachablej: the product of two masses on the partner, none on the focal node, and no truncation. Its help page cited Li et al. (2019), whose formula isk_i k_j / d^2. Dropping the focal mass changes the ranking, not just the scale. The measure now computesm_i m_j / d^exponentand gains two arguments:gravity_mass("kshell"default,"degree", or"legacy") andgravity_radius(a number, default 3,"auto"for half the mean distance, orNULL). The default is now Ma, Ma, Zhang & Wang (2016);gravity_mass = "degree", gravity_radius = NULLis Li et al.’s gravity model andgravity_radius = "auto"their local gravity model, so one measure covers three Zoo labels.gravityreturns different values than in 2.4.7 and earlier;gravity_mass = "legacy"withgravity_radius = NULLreproduces the old numbers exactly, and a test pins that.
cograph 2.4.6
New features
splot()on anetobjectwithmethod = "entropy"(Nestimate’sentropy_network()) now receives TNA styling — oval layout, TNA palette, initial-probability donuts — instead of falling through to psych styling, so the entropy re-weighting of a transition network renders comparably with its source.splot()now acceptslabel_abbrev, matchingmcml: use an integer for a fixed maximum label length,"auto"for node-count-aware abbreviation, orNULLto retain full labels.Producer-supplied splot metadata (
x$meta$splot): packages that create cograph-plottable objects can now attach a small rendering contract —renderer(resolved through a cograph-maintained whitelist of existing renderers; arbitrary function names are never evaluated),weight(which stored edge quantity to render: an edge column keeps the producer’s edge set, a matrix redefines the drawn network from its nonzero cells, aligned by dimnames), anddefaults(renderer arguments). Precedence is alwaysuser arguments > meta$splot$defaults > cograph defaults; on the regular network path this includes deprecated argument aliases (a user-suppliedpositive_colorstill beats a metadataedge_positive_colordefault). See?splot, section “Producer-Supplied splot Metadata”.
Bug fixes / changes
-
Motif subsystem overhaul following an adversarial review (13 findings, each verified against igraph before fixing):
-
motif_census()mislabeled 13 of the 16 directed triad classes: it attached MAN-order names toigraph::motifs()output, which is in igraph’s isomorphism-class order (a pure 021U triad was reported as102). The directed 3-node census now usesigraph::triad_census(), whose ordering is MAN order. Counts were internally consistent — z-scores compared like with like — but carried the wrong names.motifs(),subgraphs()andtriad_census()were never affected. - The undirected census gains the missing one-edge class (
empty/edge/wedge/triangle), and self-loops are stripped before counting (they are not part of any 3-node class). - The
"configuration"null model now uses exact degree-preserving edge rewiring. The old stub-matching +simplify()silently changed degrees, and the undirected"vl"sampler errored on graphs with isolates and restricted the ensemble to connected graphs (two disconnected triangles gotsd = 0, z = 0, p = 1for an observation the null could never produce). - All motif p-values are now empirical permutation p-values (add-one corrected) instead of Gaussian approximations, and a degenerate null (sd = 0) yields
z = NAwhen the observation differs from it — never a silentz = 0. Zero-variance handling was previously inconsistent across the three engines (forced 0 / sd := 1 / sd := 0.1).n_random/n_permbelow 2 is now an error. -
motifs(pattern = "all")now actually includes the003class; a full census sums tochoose(n, 3)and matchesigraph::triad_census()class by class. - Instance-mode significance (
subgraphs()) now tests the null probability that a triple instantiates the row’s own MAN type; the old null counted “any of the six edges exists” (for ten subjects each with a 3-cycle, expected was 8.33 instead of the correct 3.33). - Instance mode reports one row per (triple, MAN type) instead of collapsing to a dominant type, so per-type totals now agree with census mode on identical data.
- Instance-mode significance on aggregate input (a single matrix) now warns and reports
params$significance = FALSEinstead of silently returning results without the promisedz/pcolumns. - Census significance on a symmetric directed matrix now runs a directed null (previously the undirected
empty/wedge/trianglenames never matched a MAN row, yielding all-NA statistics). -
extract_motifs(level = "aggregate")now actually pools the per-individual transition matrices (previously only metadata changed), andmin_transitionsapplies per-triad at aggregate level as documented. -
motif_census(x, directed = ...)conflicting with an igraph input’s own directedness is now an error instead of relabeling without converting. -
edge_method = "percent"thresholds above 1 are percentages and the comparison is>=as documented (the old> total * 1.5default could never classify anything); fractional edge weights are rounded, not truncated, when building permutation stubs. -
plot.cograph_motifs(type = "network")forwards...to the per-motif igraph plots as documented, and pattern-panel significance decoration is suppressed for legacy per-triple results where a per-type lookup would be ambiguous. - A Monte Carlo equivalence suite now validates the whole subsystem on 1000 generated datasets per run — 18 topologies (random, scale-free, small-world, rings, stars, tournaments, DAGs, bipartite blocks, disconnected, isolates, empty), 3-60 nodes, directed / weighted / undirected / multi-actor inputs — against
igraph::triad_census(), brute-force triple enumeration, and per-actor reference censuses. - The legacy
extract_motifs()individual-level path now retains one row per(node triple, MAN type)and tests that exact type under the same weighted stub-matching null as individual-levelsubgraphs(). It no longer collapses mixed-type triples to a dominant label or counts any permuted type as a match. - Fractional transition data now produce balanced integer in/out stubs; low-activity units (including one-transition and empty units when allowed) participate correctly in instance nulls, and one-element stub vectors are shuffled without R’s
sample(x)length-one ambiguity. Unit eligibility is frozen from the original loopless weighted activity, and every positive edge retains support during integerization. -
sig, printing, and motif plot colors now consistently follow the empirical permutation decision (p < .05) instead of mixing it with|z| > 1.96or|z| > 2cutoffs. Parallel edges are simplified beforemotif_census()so observed and null graphs use the same simple-graph projection. - Instance results now include structured
node1/node2/node3columns in addition to the existingtriaddisplay label, so node names containing" - "remain unambiguous in statistics and plots. - Directed size-4 motif rows now use one consistent
M1–M218naming sequence covering all igraph isomorphism slots (199 are connected), anddirected=conflicts are rejected consistently for both igraph and cograph-network inputs. - Degenerate-null rows (
z = NAwith the smallest possible empirical p, emitted when the observation lies outside a zero-variance null) are now treated as the strongest findings everywhere: they rank first in sorted results, survivetop = ncuts instead of being silently truncated, and the significance plots report them with a message instead of silently dropping them (a z bar cannot be drawn for them). -
plot(x, type = "triads")no longer errors on fractional weighted counts (e.g. probability matrices at aggregate level); whole numbers keep the plainn=3caption. - Weight validation for permutation nulls applies only to null-eligible units, so a malformed cell in a unit excluded by
min_transitionsno longer aborts the whole significance run (malformed cells in eligible units still error loudly).
-
plot_transitions()with a multi-column data frame (the consecutive multi-step branch) no longer silently drops styling arguments:value_min,label_color,label_fontface,label_nudge,title_color,title_fontface,value_halo,value_fontface,value_nudge, andtotal_fontfaceare now forwarded, so both multi-step input forms (list of matrices, data frame) respond to the same arguments identically.Motif pattern plots (
plot(motifs(x), type = "network"), triad glyph panels) drew the wrong structure for five of the sixteen MAN triad classes: the021Dand021Uglyphs were swapped (transposed matrices), the120Dand120Uglyphs both drew a120C-isomorphic triad, and the210glyph drew a120-class triad — so the120Uand210structures were never drawn at all. Only the drawn glyphs were wrong: motif counts, significance tests, andtriad_census()were always computed from a separate, correct canonical pattern set (verified againstigraph::triad_census()). All sixteen visual patterns are now verified against igraph by a regression test that also pins the visual and canonical sets to each other. Thanks to Mengli Zhang for reporting (reconstructing the structures from the package source and spotting that three “distinct” glyphs were isomorphic).plot_bootstrap_forest(),extract_motifs(),motif_census(),triad_census()andextract_triads()are now listed in the package index and the reference site. All five are exported and user-facing, but carried@keywords internal, which hid them fromhelp(package = "cograph")— you could only find them if you already knew the name.mcml()remains hidden; it is a deprecated alias ofcsum(). Thenand...arguments ofprint.cograph_motif_analysis()andprint.cograph_motifs()are now documented (previously exempt from checking by theinternalkeyword).plot_difference()’s newdifferenceargument moved to the end of the signature, aftercombined. It had been inserted beforecombined, which shifted the positional argument order relative to the released 2.3.6 signature. Becauseplot_compare()isfunction(x, ...)and forwards toplot_difference(), a caller passing 14 positional arguments had their 14th silently rebound fromcombinedtodifference— making the function treatxas an already-subtracted matrix, discardy, and draw the wrong network with no error.differencewas introduced after the last CRAN release, so no released behaviour changes. Named calls were never affected.plot_bootstrap_forest()andplot_edge_diff_forest()no longer emit ageom_errorbarh()deprecation warning under ggplot2 4.0.0. The four horizontal error-bar layers now usegeom_errorbar(orientation = "y"); the rendered output is unchanged.DESCRIPTIONnow declares theggplot2 (>= 3.4.0)requirement the package already had (it uses thelinewidthaesthetic throughout).plot_edge_diff_forest(layout = "chord")no longer emits a spurious “row names were found from a short variable and have been discarded” warning for every node arc it draws.aggregate_layers(),supra_adjacency(),layer_similarity_matrix()andplot_motifs()now ship runnable examples. Their\examplessections were previously commented out (or entirely\dontrun), so they demonstrated nothing and were never checked. The remaining\dontrunblocks inmotifs()andextract_motifs()are now\donttest, so they are executed underR CMD check --run-donttest.detect_communities()with the"louvain"(the default) or"leiden"method no longer errors on a directed graph. These igraph algorithms are undirected-only, sodetect_communities(tna_object)— a tna model is always directed — aborted with “Multi-level community detection works for undirected graphs only”. It now collapses the directed edges to undirected (mean, as the"fast_greedy"method already did) with a message, so the package’s primary object type works with the default algorithm. This also fixesplot_htna(x, community = "louvain")and other internal callers that ran community detection on a directed model.splot()on a Nestimatenetdifference(fromsubtract_networks()/as_netdifference()) now routes toplot_difference(). Previously it fell through to thenetobjectpath, which styles by$method— “difference” is not a TNA-family method, so the asymmetric difference matrix was drawn with undirected psych styling: no arrowheads and one triangle of each asymmetric edge pair silently dropped.splot(d, minimum = 3)is now the straightforward call for a signed difference network.The
netdifferencerouting excludesnet_permutation-family objects:net_bayescarries both classes and must keep reachingsplot.net_permutation, whose per-edge CI/star arrays are aligned byNestimate::plot.net_bayesto that renderer’s edge ordering.plot_difference()on anetdifferencenow draws the display matrix ($weights— e.g. only the credible differences when coerced withas_netdifference(b, significant_only = TRUE)), falling back to$difference_matrix. Forsubtract_networks()results the two are identical, so nothing changes there.plot_permutation()/splot.net_permutation(): thetitleandlayoutdefaults now use exact[[indexing.args$titleon a dots-list holdingtitle_size(but notitle) partially matchedtitle_size, so the default title was silently skipped and no title was drawn — this is whyNestimate::plot.net_bayes()output had no title. Same latent hazard fixed forlayout/layout_scale.Edge label templates gain a
{p_diff}placeholder (probability of the difference, for Bayesian comparisons), fed by the newedge_label_p_diffargument — a per-edge vector or a full node-by-node matrix (the matrix is indexed at each drawn edge, so it survivesminimum/thresholdfiltering, and is aligned by dimnames so it may be supplied in any node order). Filled automatically from$p_differencebysplot.net_permutationand byplot_difference()on Bayesiannetdifferencecoercions. Template example:edge_label_template = "{est} (P={p_diff})".splot.netobject()styling classifier:"edge_betweenness"networks are now styled by their directedness. A directed edge-betweenness network previously fell into psych styling — drawn undirected, silently losing one direction of each asymmetric pair; it now gets the TNA presets with arrows. An undirected one (from a correlation-family source — Nestimate preserves the source’s directedness) keeps the psych look.Nestimate producers now use the
meta$splotcontract:netdifferenceobjects carryrenderer = "difference"andnet_bayescarriesrenderer = "permutation", so metadata routing (which runs before class dispatch) selects the renderer; thenetdifferenceclass branch remains as a fallback for objects built without metadata.
cograph 2.4.3
Bug fixes / changes
plot_difference()no longer hides small difference edges: it defaultsminimum = 0(the style presets otherwise injectedminimum = 0.01, silently dropping edges with|x - y| < 0.01). An explicitminimumstill wins.plot_difference(x, y, difference = TRUE)now warns thatyis ignored and usesxas the difference network, instead of silently computingx - y.
cograph 2.4.2
Bug fixes / changes
plot_compare()is no longer deprecated — it is a plain alias ofplot_difference().tna::plot_compare()delegates to it by name (cograph::plot_compare(x, y, ...)), so deprecating it wrongly made everytna::plot_compare()call emit a warning; the warning is removed. Both names call the same implementation;plot_difference()is the preferred spelling for new cograph code.plot_difference()also auto-detects a Nestimatenetdifferenceobject (or any object exposing$difference_matrix), alongsidetna_comparison.
cograph 2.4.1
New features
-
plot_difference()can now consume a pre-computed difference network: atna_comparisonobject (fromtna::compare()) is detected automatically and its$difference_matrixis plotted, anddifference = TRUEtreatsxas an already-subtracted matrix/network (noyneeded). The two-networkplot_difference(x, y)path is unchanged.
cograph 2.4.0
New features
- Two focal-node flow layouts, usable anywhere a layout name is accepted (
splot(x, layout = "target")/layout = "saqr"):-
layout_target()ports qgraph’sflow()— places one node of interest (target =) on the left and every other node in columns by unweighted BFS distance (hops). Unlike qgraph it handles disconnected graphs (isolated nodes go to a trailing column) instead of erroring. -
layout_saqr()ports the Dynalytics Desktop “saqr” transition layout (Saqr et al., LAK25): Start on top, End on bottom, middle nodes ranked by outgoing weight from Start and split into 2–3 sine-enveloped rows with a zig-zag first row (start =,end =,jitter =).
-
Bug fixes / changes
plot_difference()now styles the difference network automatically instead of drawing bare default-blue nodes: an undirected difference gets the psychometric look (Okabe-Ito node palette, no arrows, thin edges), a directed difference gets the TNA look (TNA palette, arrows). Node size uses the calibrated preset (previously nodes could render near-invisible), and edges stay coloured by the sign of the difference. Explicitnode_*arguments still override the preset.plot_difference()is added as the preferred name for the difference-network plotter.plot_compare()remains a first-class alias of it (tna::plot_compare()delegates tocograph::plot_compare()by name, so the name must keep working).plot_difference()(the renamed difference-network plotter) now treats an S3cograph_network(which is itself a list — e.g. apsychnetfit, a Nestimatenetobject, or anyas_cograph()result) as a single network. Previously such an object fell into the “plain list of networks” branch and was misread as a list of sub-networks, failing with “x must be a matrix, cograph_network, tna, or igraph object”. Comparing two psychnet/netobject networks withplot_difference(net1, net2)now works.
cograph 2.3.11
New features
dyad_census()classifies every dyad of a directed network into mutual (M), asymmetric (A), or null (N), returning a tidy one-row-per-type data.frame with counts and proportions and a dyad-based reciprocity (2M / (2M + A)) attribute. It is the dyad-level companion totriad_census(). Undirected input counts every edge as a mutual dyad.ego_networks()reports tidy per-ego personal-network metrics — size, ego/alter tie counts and densities, and Burt’s structural-hole measures (effective_size,constraint,order = 1only) — with one row per ego. The structural-hole columns reuse the same implementations ascentrality(), so they matchcentrality(x, measures = c("effective_size", "constraint"))exactly.
cograph 2.3.10
Bug fixes / changes
Bootstrap plots of undirected co-occurrence networks (
splot.net_bootstrap) now default to the"oval"layout instead of the force-directed"spring"layout, matchingsplot.tna_bootstrap. Passlayout = "spring"to restore the previous behavior.Bootstrap plots now auto-suppress the
".00"decimal tail on integer-valued weight matrices (co-occurrence counts, raw frequencies):266.00**renders as266**. Detection mirrorssplot.netobject— when every nonzero weight is a whole number and the user has not setweight_digits, bothweight_digitsandedge_label_digitsdefault to0. Applies to bothsplot.net_bootstrapandsplot.tna_bootstrap. Non-integer (correlation/GLASSO) networks are unaffected, and an explicitweight_digitsalways wins.
cograph 2.3.9
New features
plot_mcml()gains athemeargument:"classic"(default — the established pie-node / straight-edge look, now with thinner node and shell borders and slightly larger detail nodes),"rich"(donut nodes on both layers plus curved summary edges and splot self-loops), and"light"("rich"with no shell outline and a softer fill). Granular overridesnode_donut,node_donut_inner_ratio,summary_donut_inner_ratio,summary_donut_show_value,curved_edges, andsummary_curvewin over the preset.plot_mcml()now colors edges by weight sign on every layer (within-cluster, between-cluster, summary, and self-loops) viaedge_color_by:"auto"(default) keeps cluster coloring for non-negative transition networks but switches to sign coloring when any negative weight is present (correlation / association networks),"cluster"and"sign"force either mode. Positive edges useedge_positive_color("#2E7D32", green) and negative edgesedge_negative_color("#C62828", red), matchingsplot(). Edge visibility thresholding and width scaling now use the absolute weight, so negative edges are drawn rather than silently dropped, and a positive/negative key is added to the legend when sign coloring is active.plot_mcml()summary-node labels are now placed “on the clock”: each label sits just outside its node in the cardinal direction the node points from the arrangement center (top at 12, bottom at 6, left at 9, right at 3), anchored at the node boundary so it always clears the node regardless ofsummary_size. An explicitsummary_label_positionstill overrides this.
cograph 2.3.8
New features
-
plot_mcml()andsplot()acceptmcml_pcobjects (Nestimate::build_mcml_pc(), experimental psychometric MCML) and render them undirected via theirmeta$directedflag.
cograph 2.3.7
Breaking changes
- The exported names
cluster_summary()andbuild_mcml()are removed to end, permanently, the collision withNestimate::cluster_summary()andNestimate::build_mcml()— different functions that silently masked each other depending on package attach order (the same disease as thecluster_network()alias removed in 2.3.6, where load order silently flipped results between raw counts and row-normalized probabilities). Migration is name-for-name with identical behavior:-
cluster_summary(...)→csum(...)(the existing short alias is now the canonical exported name; same arguments, samecluster_summaryreturn object). -
build_mcml(...)→summarize_clusters(...)(same arguments, samemcmlreturn object). In sessions where both packages are attached, the bare namescluster_summary()/build_mcml()now always refer to Nestimate’s data-layer verbs, regardless of attach order. Theas_tna()generic is intentionally exported by both packages: the definitions are identical (function(x) UseMethod("as_tna")), so masking is harmless and S3 methods from both packages dispatch correctly.
-
New features
-
plot_mcml()gains adirectedargument (defaultNULL= auto-detect). Undirected rendering suppresses arrowheads on all three edge layers (within-cluster, between-cluster, summary), draws each symmetric edge pair once instead of twice (previously a symmetric matrix produced overplotted reciprocal arrows), and moves edge labels to the edge midpoint. Auto-detection reads$meta$directedfromcluster_summary/mcmlinput (e.g., co-occurrence aggregations such asNestimate::build_mcml(type = "cooccurrence")now render undirected with no extra flag), the$directedfield of network objects, or matrix symmetry for plain matrices — the same contract assplot(), which forwardsdirectedwhen dispatchingmcml/cluster_summaryobjects. -
plot_mcml()undirected matrix input is aggregated withcluster_summary(type = "cooccurrence")(symmetrized counts) instead of the row-normalizedtype = "tna", whose output is asymmetric even for symmetric input and cannot be represented by undirected drawing. Whendirected = FALSEis forced on weights that are not symmetric,plot_mcml()now warns that only the upper triangle is drawn.
Bug fixes
-
cluster_summary()and the sequence path ofbuild_mcml()now record the effective directedness in$meta$directed:FALSEwhentype = "cooccurrence"(which symmetrizes the weights), instead of echoing thedirectedargument unchanged.
cograph 2.3.6
CRAN release: 2026-05-31
Bug fixes
- Removed the
cluster_network()alias forsummarize_network(). It collided withNestimate::cluster_network()— a completely different function (PAM clustering on sequence data, one network per cluster) — and the two silently masked each other depending on package attach order, producing confusingunused arguments (k = ..., cluster_by = ...)errors. Usesummarize_network()(or its remaining short formcnet()) for matrix-to-cluster aggregation in cograph.
cograph 2.3.4
Bug fixes
-
.smooth_blob()(used byplot_simplicial()andoverlay_communities()) now guardsgrDevices::chull()against non-finite anchor coordinates. Previously a node lacking layout coordinates (NA/Inf) aborted the blob with “finite coordinates are needed”; such anchors are now dropped before the convex-hull step.
cograph 2.3.3
Documentation
- Aligned the
motifs()/subgraphs()roxygen documentation with the post-audit behavior shipped in 2.3.2 (censustype_summarycounts,min_counthandling, and corrected plot legend descriptions).
cograph 2.3.2
Bug fixes
- Full audit pass over the motifs subsystem:
type_summarynow holds real MAN-type counts in census mode,min_countis honored in census mode, and the swapped source/target color description inplot.cograph_motif_result()is corrected. - Unbroke
motifs()andplot_simplicial()on Nestimate-backed workflows (HON / HYPA sequence inputs). -
panel_layout(): tightened dimension validation and made the restoration claim honest — it now restores only thepar()settings it actually changed.
cograph 2.3.1
Multi-panel layout control
- New
combinedargument (defaultTRUE) on every multi-panel plot function:splot()group-cascade,plot_netobject_group(),plot_netobject_ml(),plot_net_bootstrap_group(),plot_group_permutation(),plot_compare(),splot.net_mlvar(),plot_network_evolution(),plot.cograph_motifs(),plot.cograph_motif_result(),plot.cograph_motif_analysis(), andplot.tna_disparity(). Withcombined = FALSEthese functions draw panels into the active device without callinggraphics::par(mfrow=...), so callers can drive their own layout (e.g.graphics::layout()or the newpanel_layout()helper). DefaultTRUEpreserves prior behavior — every existing call site renders identically. - New
panel_layout()helper sets up a custom multi-panel device layout for use withcombined = FALSE. Accepts either a uniform-gridc(nrow, ncol)or agraphics::layout()matrix for non-uniform layouts (e.g. one wide panel + two narrow ones). Returns apar()snapshot for restoration viaon.exit().
Test suite hygiene
-
test-coverage-splot-{41,42}.R: bumpedn_nodesfrom 4 to 10 in seven per-edge attribute tests so the seed=42 sampler does not produce duplicate (1,2) pairs that trip cograph’s undirected-duplicate-edge detector. -
test-coverage-class-network-41.R: aligned theset_layout_coords()mismatched-row-count test with the strict input validation already enforced byR/class-network.R. -
test-overlay-communities.R: prefixed twocommunities()calls withcograph::to avoidtnamasking when both packages are loaded in the suite (per CLAUDE.md “namespace masking” gotcha).
cograph 2.3.0
Documentation
- Audited every R/*.R function file for roxygen/Rd accuracy. Corrected stale defaults (
cr_color#D4820A->#D4829Ainplot-forest.R;show_valuedefaultFALSE->TRUEinsplot-nodes.R), corrected dataset dimensions indata-hai.R(302->429 x 287), corrected a reference to the nonexistentigraph::is_bipartite()(nowbipartite_mapping()), expandedcentrality()@parammeasure lists formode,cutoff,invert_weights, andmembershipto match the implementation, dropped baked-in measure counts that rot on each addition, and removed nonexistent themes fromsn_themedocumentation. No runtime behavior changes from the documentation pass itself.
Bug fixes
-
plot_simplicial()now warns whenanomalyis set on an input that has no anomaly concept (HON, association rules, link prediction, character pathways,method = "hon"/"rules"). Previously the argument was silently dropped, so calls likeplot_simplicial(hon, anomaly = "over")andplot_simplicial(hon, anomaly = "under")produced byte-identical plots.anomalyis honored only fornet_hypainputs andmethod = "hypa"auto-builds.
Centrality
-
centrality()gains an umbrella argumenttna_network(logical or NULL). WhenTRUE(or auto-detected from atna/group_tna/ctna/ftna/atnainput), all measures shared withtna::centralities()match byte-for-byte:loops = FALSE,invert_weights = TRUE,diffusion_method = "power_series",transitivity_type = "onnela". Side-by-side audit confirms zero divergence onOutStrength,InStrength,ClosenessIn/Out/All,Betweenness,Diffusion,Clustering(max|diff| = 0). Any per-argument override the user passes explicitly always wins over the umbrella. -
centrality()(andcentrality_diffusion()) gain adiffusion_method = c("kandhway_kuri", "power_series")argument. The defaultNULLauto-detects:"power_series"for tna inputs (matchestna::centralities(., measures = "Diffusion")byte-for-byte whenloops = FALSE),"kandhway_kuri"(the existing 1-hop binary-degree formula, Kandhway & Kuri 2014) for everything else. Previously cograph’s diffusion silently disagreed with tna’s because cograph used an unweighted neighborhood-degree sum while tna usesrowSums(P + P^2 + ... + P^n)on the diagonal-zeroed weighted matrix — the same name covered two different statistics. Set explicitly to override the auto-detect.
Tests
- Added a regression test in
tests/testthat/test-validate-nestimate-bootstrap-permutation.Rasserting thatcentrality()on a Nestimatenetobjectagrees withcentrality()on its$weightsmatrix when the diagonal is non-zero. Locks in the upstream Nestimate fix to.extract_edges_from_matrix()(Nestimate >= 2026-05-02) which now preserves self-loops in$edges. Without that fix, loop-bearing netobjects (e.g.Nestimate::build_mcml() |> Nestimate::as_tna()) silently under-counted node degree by 2.
Plotting — edge-label cex coupling (Phase 2)
- Default
edge_label_sizeis now coupled to the node label cex at a fixed 0.55 fraction (edge_cex = 0.55 * mean(node_label_cex)) so the node-to-edge-label ratio stays a stable ~1.82x across canvases. This replaces the previousEDGE_LABEL_SCALE_CAP-based compensation, which let the ratio drift from 2.5x at reference to 3.6x at poster canvases because edge labels were clamped to a tighter 1.6 ceiling while node labels scaled freely to 2.3. The visible effect: edge weight annotations are now readable at poster sizes instead of shrinking relative to node labels. User-explicitedge_label_sizestill wins and receives the same (capped) visual-scale compensation as before; only the default path changed. - Edge-label visual_scale resolution moved from
render_edges_splot()intosplot.Rso the final cex is produced in a single place.
Plotting — device-aware visual scaling
-
splot()now applies device-dependent compensation to text, line, and point sizes so visual ratios (label-to-node, legend-to-plot, edge thickness) stay consistent when the output device changes. This fixes the long-standing “labels too big at high DPI” and “legend desynchronised from the plot” issues when saving PNGs atres = 300orres = 600with pixel-defaultwidth/height, and when resizing the RStudio plot pane. Implementation: a singlecompute_visual_scale()reads the active device’s canvas size (dev.size("in")) and returns multipliers keyed off a 5.9-inch reference (matching the default RStudio 7×5” pane so backward-compatible behaviour at the default canvas is preserved). Multipliers are clamped to[0.55, 1.9]to keep thumbnails and posters legible. See the newR/visual-scale.R. - New
scaling = "fixed"mode onsplot()— and corresponding global optionoptions(cograph.visual_scaling = FALSE)— disables device compensation for reproducibility-sensitive workflows that calibrated against the previous behaviour. -
splot()return value now carries two attributes for downstream tooling:cograph.visual_scale(the multiplier list) andcograph.node_diam_in(the representative node diameter in inches at the rendered device). - The splot-internal
render_legend_splot()plus the new shared.render_legend_base()(R/render-legend-shared.R) replace the ad-hoc legend cex/pt.cex handling with a single compensated path.plot_htna,plot_mtna,plot_mlna,plot_mcmlstill use their historical scale multiplier arguments; Phase 2 will migrate them to the shared helper.
Plotting
-
splot.netobjectnow routes on the Nestimate$methodslot rather than just direction. Undirected sequence-based networks frombuild_cna()andwtna(method = "cooccurrence")get oval TNA-family styling (layout, palette, donuts) with arrows and dotted edge starts automatically dropped because the matrix is symmetric. Glasso / cor / pcor / ising networks still getpsych_styling = TRUE(spring layout, Okabe-Ito palette). -
from_tna()auto-detects integer-valued weight matrices (ftna, ctna, raw counts) and setsweight_digits = edge_label_digits = 0so edge labels render as2304rather than2304.00. Fractional weights still format to two decimals. Explicit user-suppliedweight_digitsstill wins. -
psych_styling = TRUEis now exported as a first-class styling preset (undirected counterpart oftna_styling) — Okabe-Ito palette, spring layout, no arrows — applied by default tosplot.netobjecton correlation-family input and to the$contemporaneous/$betweenconstituents ofnet_mlvar. - Expanded
splot()dispatch coverage across the tna and Nestimate class hierarchies, ensuringtna,ftna,ctna,group_tna,tna_bootstrap,group_tna_bootstrap,tna_permutation,group_tna_permutation,netobject,netobject_group,netobject_ml,net_mlvar,wtna_mixed,net_bootstrap,net_permutation,boot_glasso,mcml,net_hon,net_hypa, andsimplicial_complexall reach the correct renderer. - Self-loops are now preserved in every plot function.
Correctness fixes (audit-driven)
-
detect_duplicate_edges(),aggregate_duplicate_edges(),simplify.cograph_network(), and the internalcheck_duplicate_edges()helper now respect directed vs undirected semantics. Previously the canonical (min/max) endpoint key collapsedA -> BandB -> Ainto one edge even on directed graphs, matchingigraph::simplify()ground truth. -
.compute_modularity()replaces a nested for loop with cluster-wise vectorization (sum(A[idx, idx]) - sum(k_out[idx]) * sum(k_in[idx]) / m), per the project “no for loops” rule. Results verified bit-exact againstigraph::modularity(). -
is_directed()now recognisesCographNetworkR6 objects — previously only thecograph_networklist format dispatched correctly. -
compute_layout_for_cograph()useslayout$get_type()instead of the removed$namefield onCographLayout. -
network_small_world()returns0(valid: no triangles means definitively not small-world) instead ofNA_real_when the observed clustering coefficient is zero but path length is finite. -
simplify.cograph_network()threads the directed flag through to edge aggregation so directed multigraphs collapse correctly.
Performance & documentation
-
simplify()performance refactor for large networks plus a cleaner title-composition path. -
motifs(),extract_motifs(), andplot.cograph_motif_analysisexamples reworked to usen_perm = 10L(orsignificance = FALSE) and promoted from\dontrunto CRAN-runnable (optional tna branches stay in\donttest). Retires 320 seconds of latent CRAN timing risk — every example now runs in under 4 seconds.
New tests
-
test-audit-fixes.R— ground-truth regressions for the directed edge semantics, modularity vectorization, and small-world behaviour changes. -
test-integer-weight-labels.R— locksfrom_tna()integer-weight auto-detect behaviour and precedence of explicitweight_digits. -
test-equiv-{assortativity, cluster-quality, communities, disparity, edge-centrality, network-summary, robustness, standalone-measures}.R— numerical equivalence against igraph, sna, centiserve, brainGraph, influenceR, tidygraph, and NetworkX. Gated byskip_coverage_tests() + skip_on_cran(), so they do not run on the CRAN pipeline.
cograph 2.1.0
New Features
Batch 6 — new-API graph-level / set-level / pair-level measures
These measures don’t fit the per-node centrality() data frame, so they live as standalone functions:
-
estrada_index()— graph-level spectral invariant: , equal to the trace of the matrix exponential of the adjacency. Equivalently, the sum ofsubgraph_centrality()across all nodes. Matchesnetworkx.estrada_indexat machine epsilon (max relative diff ~5e-15 across random test graphs). -
trophic_incoherence()— graph-level food-web stability measure (Johnson et al. 2014). Defined as the population standard deviation of per-edge trophic differences where is the trophic level of node . Zero for perfectly coherent DAGs (e.g., a pure chain). Matchesnetworkx.trophic_incoherence_parameterat machine epsilon. Directed-only; reuses the existingtrophic_levelcalculator. -
group_centrality(x, nodes, measure = c("betweenness", "closeness", "degree"))— Everett-Borgatti (1999) group centrality for a set of nodes. Returns a scalar. Supportsmode = "in"/"out"for directed-degree variants. Group closeness and group degree matchnetworkx.group_*_centralitybit-exact. Group betweenness implements the textbook Everett-Borgatti / Puzis 2008 definition (fraction of shortest paths passing through at least one node in the group), which diverges fromnetworkx.group_betweenness_centralityon some graphs due to a known quirk in NetworkX’s Puzis-Yahalom-Elovici iterative algorithm. Verified via an independent Python brute-force: cograph matches the textbook definition; NX produces larger values on graphs with many overlapping shortest paths. Documented in the roxygen “Divergence from NetworkX” section. -
dispersion(x, u = NULL, v = NULL, normalized = TRUE, alpha = 1, b = 0, c = 0)— Backstrom-Kleinberg (2014 Facebook) pair-level measure of tie strength. Counts the number of “well-dispersed” mutual friends ofuandv(pairs of common neighbors that are not directly connected and share no common neighbor insideu’s ego network other thanuandv). Matchesnetworkx.dispersionbit-exact across all 156 edges on the karate club graph. Returns a scalar, named vector, or data frame depending on which ofu,vare specified.
Centrality Batch 5 — Gould-Fernandez brokerage (5 roles)
Added the five Gould-Fernandez (1989) brokerage role counts, a foundational measure in social network analysis (~1500 citations). Each role is a separate per-node measure requiring a membership argument (following the same pattern as participation, within_module_z, gateway), and counts open directed 2-paths a -> v -> c through broker v:
-
centrality_brokerage_coordinator()— all three in broker’s group (w_I) -
centrality_brokerage_itinerant()— endpoints same group, broker different (w_O, “consultant”) -
centrality_brokerage_representative()— broker + source same, target different (b_IO) -
centrality_brokerage_gatekeeper()— broker + target same, source different (b_OI) -
centrality_brokerage_liaison()— all three in different groups (b_O)
Bit-exact match against sna::brokerage$raw.nli for all five roles across 20 random directed graphs. Implemented natively (no runtime dependency on sna). Key implementation detail: the Gould-Fernandez counting rule requires open 2-paths only — triads where a direct edge a -> c already exists are excluded. This matches sna’s C implementation exactly and was derived empirically (sna’s .C("brokerage_R", ...) has no R-level source).
Directed-only; warns and returns NA on undirected input.
Centrality Batch 4 — directed prestige family (Wasserman-Faust / sna)
-
centrality_prestige_domain()— directed-graph prestige measure: for each node , the number of other nodes that can reach via a directed path. Classical Wasserman-Faust (1994) measure fromsna::prestige(cmode = "domain"). Bit-exact match against sna, implemented natively viaigraph::distances(mode = "out")+colSums(is.finite(D)) - 1(no runtime dependency on sna). Directed-only; returns NA with a warning on undirected input. -
centrality_prestige_domain_proximity()— distance-weighted variant:R_v^2 / (D_v * (n - 1))whereR_vis the number of reachers andD_vis the sum of their geodesic distances tov. Bit-exact match againstsna::prestige(cmode = "domain.proximity")on strongly connected directed graphs. On graphs with any unreachable pair, sna has a known bug (FALSE * Inf = NaNcollapses the denominator, producing all-zero output); cograph’sis.finite()-masked formula produces mathematically correct values on any directed graph. Directed-only.
Centrality Batch 3 — classical measures with reference-package validation
-
centrality_katz()— Katz (1953) status index. Bit-exact match againstcentiserve::katzcent(cograph mirrors centiserve’s exact LAPACK call sequence). Also matchesigraph::alpha_centrality(exo = 1)andnetworkx.katz_centrality_numpyat machine epsilon. Newkatz_alphaparameter (default 0.1). -
centrality_hubbell()— Hubbell (1965) input-output centrality. Bit-exact match againstcentiserve::hubbell(cograph mirrors centiserve’s full-inverse LAPACK call path). Note: centiserve’s default (weights = NULL) silently ignoresE(g)$weight; to reproduce cograph’s behavior with centiserve on weighted graphs, passweights = igraph::E(g)$weightexplicitly. Newhubbell_weightparameter (default 0.5). -
centrality_information()— Stephenson-Zelen (1989) information centrality. Bit-exact match againstsna::infocenton connected undirected graphs (cograph mirrors sna’s exact construction andsolve()call sequence). -
centrality_pairwisedis()— Pairwise disconnectivity (Potapov et al. 2008). Directed-only; fraction of reachable ordered pairs that become unreachable when each node is removed. Bit-exact match againstcentiserve::pairwisedis. Warns and returnsNAon undirected input, matching the convention used bysalsa,leaderrank, andtrophic_level. -
centrality_reaching_local()/reaching_global()— Local and global reaching centrality (Mones, Vicsek & Vicsek 2012). Bit-exact match againstnetworkx.local_reaching_centralityacross the directed unweighted, undirected unweighted, and weighted branches. Undirected unweighted LRC coincides withigraph::harmonic_centrality(normalized = TRUE)(documented).reaching_global()is a graph-level hierarchy statistic in [0, 1].
cograph 1.8.2
New Features
-
plot_simplicial()now acceptstna,netobject,net_hon, andnet_hypaobjects directly — higher-order pathways are auto-built and visualized with proper state labels, no manual extraction needed. New parameters:method("hon"/"hypa"),max_pathways,ncol. Dismantled mode usesgridExtragrid layout with scaled nodes -
print.cograph_network()now shows a structured summary: node/edge counts, density, reciprocity, weight range, and top-degree nodes — replacing the minimal R6 default output - Added
mcmlS3 class withas_mcml()generic for type-safe handling of Markov Chain Multi-Level models — enablesprint(),plot(), and method dispatch on MCML objects - Added local
%||%operator for R 4.1 compatibility (no longer requires R 4.4+)
Breaking Changes
- MCML field names renamed for clarity:
$between→$macro,$within→$clusters -
as_tna()on MCML objects now returns a flatgroup_tnalist instead of a nested structure
Bug Fixes
-
plot_mcml()now suppresses zero-weight edges instead of drawing invisible lines, and strips leading zeros from edge labels (.32instead of0.32) - Self-loops in
cluster_summary()are now preserved in the macro diagonal, reflecting intra-cluster retention rates - Sequence data is properly propagated through the full tna → macro → cluster pipeline, so downstream models can use bootstrap and permutation tests
cograph 1.8.0
New Features
- Added
overlay_communities()for drawing community blob overlays on any network plot — accepts method names, membership vectors, or pre-computed community objects - Added
plot_simplicial()for higher-order pathway visualization, rendering simplicial complexes as smooth blobs with flexible separators and a dismantled view option - Added
value_nudgeparameter toplot_transitions()for controlling the distance between flow labels and nodes - Added bundle legend label controls:
bundle_legend_size,bundle_legend_color,bundle_legend_fontface,bundle_legend_position - Added per-function label controls (
label_size,label_color,label_fontface,label_hjust) toplot_transitions(),plot_trajectories(), andplot_alluvial()
cograph 1.7.0
New Features
Cluster Analysis
- Added
cluster_summary()for aggregating network weights at the cluster level, producing between-cluster and within-cluster matrices from raw transition data - Added
build_mcml()for constructing Markov Chain Multi-Level models from edge lists or sequence data with automatic cluster detection - Added
cluster_quality()for modularity-based cluster quality metrics andcluster_significance()for permutation-based significance testing - Added
as_tna()to convert cluster summaries to TNA objects for bootstrapping, permutation testing, and plotting withsplot()
Network Operations
- Added
simplify()for pruning weak edges from networks, with configurable weight threshold and aggregation method - Added
disparity_filter()for backbone extraction (Serrano et al. 2009), with methods for matrices, tna, igraph, and cograph_network objects - Added
robustness()for network robustness analysis with targeted (betweenness, degree) and random attack strategies, plusggplot_robustness()for faceted ggplot2 output - Added
temporal_edge_list()for converting sequence data to timestamped edge lists - Added
supra_adjacency(),supra_layer(),supra_interlayer()for multilayer supra-adjacency matrix construction - Added
layer_similarity(),layer_similarity_matrix(), andlayer_degree_correlation()for comparing layers in multilayer networks - Added
aggregate_weights()andaggregate_layers()for weight aggregation across layers - Added
verify_with_igraph()for cross-validating cograph centrality and network metrics against igraph
Motif Analysis
- Added
motifs()/subgraphs()as a unified API for triad census (node-exchangeable counts) and instance extraction (named node triples), with auto-detection of actor/session columns, rolling/tumbling window support, and exact configuration model significance testing
Visualization
- Added
plot_mcml()for Markov Chain Multi-Level visualization showing between-cluster summary edges alongside within-cluster detail, with pie charts, self-loops, and 22 customization parameters - Added
plot_chord()for native chord diagrams with automatic weight-based arc sizing - Added
plot_time_line()for cluster membership timeline visualization - Added
plot_htna()orientations:"facing"(tip-to-tip columns) and"circular"(two semicircles), plusintra_curvaturefor drawing intra-group edges as dotted bezier arcs - Added
thresholdparameter to all plot functions for filtering edges/cells below a minimum absolute weight - Added
value_fontface,value_fontfamily, andvalue_haloparameters toplot_heatmap()for text styling control - Added directional shorthands for
scale_nodes_by:indegree,outdegree,instrength,outstrength,incloseness,outcloseness,inharmonic,outharmonic,ineccentricity,outeccentricity - Added
scale_nodes_scaleparameter tosplot()for dampening (< 1) or exaggerating (> 1) centrality-based node sizing differences - Added qgraph argument translation in
splot(): when plotting tna objects, qgraph-style parameters (vsize,asize,edge.color,lty,shape) are automatically mapped to cograph equivalents
Transition Plot Enhancements
- Added intermediate labels with
node_label_format(e.g.,"{state} (n={count})") for showing counts on transition plot nodes - Added line bundling via
bundle_sizefor aggregating individual trajectories into weighted summary lines in large datasets - Added flow value labels via
show_values/value_positionfor displaying transition counts on flow lines - Added
label_positionconsistency across ALL columns (first, middle, last) in trajectory plots
Data & Infrastructure
- Added example datasets:
gamer_data,group_engagement,srl_data - Added
set_node_groups()/get_node_groups()for managing cluster assignments on cograph_network objects - Consolidated cograph_network metadata under
$metawith getter/setter functions - Added
group_tnasupport tosplot()for direct plotting of grouped TNA models - Gave each
centrality_*wrapper its own focused help page
Bug Fixes
- Fixed load and percolation centrality computation: the BFS assumed unit edge weights, causing infinite loops on weighted graphs; directed graphs now transpose correctly (matching sna convention); disconnected nodes no longer contribute spurious centrality
- Fixed self-loop and edge clipping in
splot()viewport calculation - Fixed argument forwarding in splot dispatch for bootstrap/permutation objects — named parameters (minimum, threshold, layout, title) were consumed by
splot()’s signature and silently dropped when dispatching - Fixed overlapping flow value labels in multi-step alluvial plots
- Fixed alluvial label halo rendering producing spike artifacts (8 → 16 offset directions)
- Fixed viridis palette direction in
plot_heatmap()so high values get dark colors - Fixed
build_mcml()density method crash when weight vector had no names - Fixed display label priority resolution (labels > label > identifier)
- Removed zero-value labels that appeared after rounding in transition plots
Improvements
- Simplified splot dispatch: extracted
.collect_dispatch_args()helper to replace 6 copy-paste dispatch blocks, usingmatch.call()+mget()for reliable argument capture
cograph 1.6.0
New Features
Centrality
- Added
centrality()with 23 measures and individual wrappers: degree, strength, betweenness, closeness, eigenvector, pagerank, harmonic, authority, hub, alpha, power, kreach, diffusion, percolation, eccentricity, transitivity, constraint, coreness, load, subgraph, leverage, laplacian, current-flow betweenness, current-flow closeness, voterank - Added
edge_betweenness()for edge-level centrality - Added automatic weight inversion for path-based measures when working with tna transition matrices (where higher weight = stronger connection, not shorter distance)
Community Detection
- Added
detect_communities()with 11 algorithms: louvain, walktrap, fast_greedy, label_propagation, leading_eigenvector, infomap, spinglass, leiden, optimal, edge_betweenness, multilevel — pluscom_*shorthand aliases - Added consensus clustering and
cluster_significance()for permutation-based validation
Network Metrics
- Added
network_summary()andsummarize_network()for computing comprehensive network-level statistics (density, reciprocity, transitivity, diameter, components, degree distribution)
Visualization
- Added
plot_transitions()for alluvial/Sankey flow diagrams, withplot_alluvial()andplot_trajectories()wrappers - Added
plot_bootstrap()andplot_permutation()for significance-styled visualization of bootstrap and permutation test results — significant edges rendered solid on top, non-significant edges dashed behind - Added
plot_mixed_network()for overlaying symmetric (undirected, straight) and asymmetric (directed, curved) edges on the same network - Added
plot_heatmap()for adjacency matrix heatmaps with optional hierarchical clustering andplot_ml_heatmap()for multilayer 3D perspective heatmaps - Added
plot_compare()for difference network visualization showing edge-weight changes between two networks - Added
splot()S3 methods fortna_bootstrapandtna_permutationobjects
Motif Analysis
- Added
motif_census(),triad_census(), andextract_motifs()for triad motif analysis with pattern filtering, significance testing, and network diagram visualization
Network Utilities
- Added
filter_edges(),subset_edges(),select_nodes(),select_edges()for flexible network subsetting - Added
set_groups()for storing cluster assignments on cograph_network objects with automatic dispatch toplot_htna()/plot_mtna()
Infrastructure
- All plot functions now accept
cograph_networkobjects as input, in addition to matrices, igraph objects, and tna objects - Layout computation is now lazy — coordinates are only calculated when first needed
- Improved
layout_springandlayout_gephi_fralgorithms: vectorized attraction forces, edge aggregation for dense networks - Renamed package from Sonnet to cograph
cograph 1.5.2
CRAN release: 2026-03-02
Breaking Changes
- Standardized first parameter name to
xacross all plotting functions:-
plot_tna():input→x -
plot_htna():input→x(wasmodel) -
plot_mtna():input→x(wasmodel) -
splot()already usedx
-
cograph 1.5.1
Breaking Changes (with backwards compatibility)
The following parameters have been renamed for consistency. The old names still work but emit deprecation warnings:
| Old Name | New Name | Reason |
|---|---|---|
esize |
edge_size |
Add edge_ prefix, expand abbreviation |
cut |
edge_cutoff |
Add edge_ prefix, clarify meaning |
usePCH |
use_pch |
Fix camelCase to snake_case |
positive_color |
edge_positive_color |
Add edge_ prefix (matches theme storage) |
negative_color |
edge_negative_color |
Add edge_ prefix (matches theme storage) |
donut_border_lty |
donut_line_type |
Expand lty abbreviation |
cograph 1.4.0
New Features
- Added
mlna()for multilevel network visualization with 3D perspective - Added
mtna()for multi-cluster network visualization with shape-based cluster containers - Added
plot_htna()for hierarchical multi-group network layouts with polygon and circular arrangements - Added
tplot()as a qgraph drop-in replacement with automatic parameter translation - Added
arrow_angleparameter for customizable arrowhead geometry
cograph 1.3.1
New Features
- Added
edge_start_dot_densityparameter for TNA-style dotted edge starts indicating direction - Added direct support for tna objects via
from_tna()— no manual matrix extraction needed - Added direct support for statnet
networkandqgraphobjects as input - Added auto-conversion of
pie_valuesvector todonut_fillwhen all values are in [0,1]
Bug Fixes
- Fixed TNA visual defaults being silently overwritten in
splot()when other parameters were specified - Fixed self-loop edge labels overlapping the loop arc
- Fixed
donut_shapevalidation rejecting custom SVG shapes - Fixed title clipping when title text exceeded plot margins
- Fixed edge rendering crash on certain edge/node configurations
- Removed underscore prefix requirement for custom SVG shape names
cograph 1.2.7
Bug Fixes
- Fixed oversized nodes in
from_qgraph()when a layout override was provided - Fixed oval layout using independent axis scaling, which distorted aspect ratios — now uses uniform scaling via
normalize_coords() - Fixed edge label alignment in
from_qgraph()by using a matrix intermediary for per-edge vector reordering - Fixed
nrow(el)crash: qgraph’s Edgelist is a list, not a data.frame - Fixed oval layout node distortion and donut fill values when converting from qgraph
cograph 1.2.6
New Features
- Added
donut_emptyparameter for rendering unfilled donut nodes - Added
from_qgraph()for converting qgraph objects to cograph format, reading resolvedgraphAttributesfor accurate parameter extraction
cograph 1.2.0
New Features
- Added
soplot()for grid/ggplot2-based network plotting — full feature parity withsplot()using a different rendering backend - Added
layout_oval()for oval/elliptical node arrangements - Added
layout_scaleparameter to expand or contract the network layout, with"auto"mode for node-count-based scaling - Added Gephi-style Fruchterman-Reingold layout algorithm
- Added
edge_start_styleparameter for visually indicating edge direction via styled start segments (dashed, dotted)
cograph 1.1.0
New Features
- Added
splot()— a base R graphics engine for network visualization usingpolygon(),lines(), andxspline(), providing better performance than grid-based rendering for large networks - Added polygon-shaped donut nodes, custom SVG node shapes, and AI-generated shape support
- Added shadow/halo labels and fine-grained text control (fontface, fontfamily, hjust, vjust, angle)
- Added double donut nodes with separate inner/outer border controls
- Added edge CI (confidence interval) underlays and template-based edge labels
- Added comprehensive legend support: groups, edge color scales, and node size scales
- Added high-resolution output via
sn_save()with configurable DPI - Added edge curve modes, bidirectional arrows, self-loop rotation, and per-edge curve control
Bug Fixes
- Fixed donut rendering producing artifacts and simplified the
donut_colorAPI to accept 1 color (fill), 2 colors (fill + background), or n colors (per-node) - Fixed arrow positioning and curve direction for qgraph-style edges
- Fixed edge label positioning to avoid overlap with edge lines
- Fixed self-loop rendering to use qgraph-style circular arcs
- Fixed arrow placement on non-square viewports
- Fixed reciprocal edge auto-separation causing edge crossings
