This guide shows how to compare multiscale GRIP with layout engines from igraph and graphlayouts using common diagnostics on unweighted graphs. The examples use precomputed fits with the settings described below. They do not compare SGD metric MDS; see Metric MDS for SGD and SMACOF, and Choosing and evaluating for selecting layouts for your data.
For this track the comparison remains package-to-package:
igraphgraphlayoutsgripTo keep the article lightweight, the cross-package results are precomputed and bundled as an RDS file. The reproduction script is still:
inst/scripts/precompute-vs-alternatives.Rres.path <- system.file(
"extdata", "vs_alternatives", "benchmark_results.rds",
package = "grip"
)
if (!nzchar(res.path)) {
stop(
"Precomputed benchmark results not found. ",
"Run inst/scripts/precompute-vs-alternatives.R first."
)
}
res <- readRDS(res.path)The provenance record and reproduction script provide the computation environment, settings and timing boundaries.
The karate graph has 34 vertices and 78 edges; the
12 by 12 mesh has 144 vertices and 264 edges. Each
table below scores a saved layout with hop-distance stress, scoring
seeds 42, 2,000 requested stress pairs, and up to 5,000 sampled
nonedges. Exact crossings are enabled for these small graphs. The stress
score fits a common scale; it is not the raw stress objective of
metric.mds(). Preset/tuned rows are labeled separately from
ordinary defaults. Keep the results where another method scores better
when interpreting the comparison.
This is still a useful small-graph sanity check. The graph is small enough that single-scale methods can compete directly.
karate.display <- res$karate$scores[, c(
"method",
"sampled.stress",
"edge.length.cv",
"edge.crossings",
"sampled.nonedge.sep.ratio",
"cluster.separation"
)]
knitr::kable(
karate.display,
digits = 4,
row.names = FALSE,
caption = "Karate club: precomputed comparison; one seeded layout per method."
)| method | sampled.stress | edge.length.cv | edge.crossings | sampled.nonedge.sep.ratio | cluster.separation |
|---|---|---|---|---|---|
| FR (igraph) | 0.2477 | 0.3581 | 68 | 0.3670 | 2.7566 |
| KK (igraph) | 0.2102 | 0.2806 | 73 | 0.3021 | 2.2528 |
| DrL (igraph) | 0.3320 | 0.4680 | 120 | 0.2372 | 1.6584 |
| Stress (graphlayouts) | 0.2075 | 0.2611 | 78 | 0.2747 | 2.2240 |
| grip default | 0.2221 | 0.2941 | 75 | 0.2547 | 2.1364 |
| grip tuned | 0.3254 | 0.2836 | 124 | 0.0570 | 1.7389 |
On graphs this small, grip does not have a special
structural advantage. That is an honest result and a useful reminder:
multiscale methods are not automatically best on every graph.
Meshes are a good structured topological benchmark because a strong layout should recover an orderly grid with uniform edge lengths and few or no crossings.
mesh.display <- res$mesh$scores[, c(
"method",
"sampled.stress",
"edge.length.cv",
"edge.crossings",
"sampled.nonedge.sep.ratio"
)]
knitr::kable(
mesh.display,
digits = 4,
row.names = FALSE,
caption = "12x12 mesh: precomputed comparison; one seeded layout per method."
)| method | sampled.stress | edge.length.cv | edge.crossings | sampled.nonedge.sep.ratio |
|---|---|---|---|---|
| FR (igraph) | 0.1159 | 0.1735 | 0 | 0.9731 |
| KK (igraph) | 0.1039 | 0.0135 | 0 | 1.3356 |
| DrL (igraph) | 0.1276 | 0.3580 | 0 | 0.5613 |
| Stress (graphlayouts) | 0.1040 | 0.0138 | 0 | 1.3367 |
| grip default | 0.1089 | 0.0490 | 0 | 1.1589 |
| grip mesh preset | 0.1056 | 0.0497 | 0 | 1.2197 |
This benchmark shows a different pattern from the karate graph: on a
canonical structured family like a mesh, the default GRIP result shows a
clean uncrossed layout. The saved mesh preset result has
slightly lower sampled stress. These are particular seeded results, not
an estimate of variation across layout seeds. At the same time, stress
and KK remain very strong on a regular lattice.
The same artifact includes an unweighted symmetric-neighbor graph of HMP microbiome samples: 4,391 vertices and 9,067 edges. PCA-space edge lengths were not supplied to any layout engine. The recorded machine was an Apple M4 Max with 64 GiB memory, macOS/Darwin 25.6.0, and R 4.6.1.
knitr::kable(res$hmp$timing, digits = 3, row.names = FALSE,
caption = "HMP graph: elapsed seconds for the precomputed layout runs.")| method | n.runs | elapsed.sec.median | elapsed.sec.iqr |
|---|---|---|---|
| FR (igraph) | 5 | 0.115 | 0.001 |
| DrL (igraph) | 5 | 2.840 | 0.001 |
| Stress (graphlayouts) | 5 | 24.530 | 0.142 |
| grip default (hop) | 5 | 0.531 | 0.002 |
knitr::kable(res$hmp$scores[, c("method", "sampled.stress", "edge.length.cv")],
digits = 4, row.names = FALSE,
caption = "HMP graph: hop-distance quality of the first timed layout per method.")| method | sampled.stress | edge.length.cv |
|---|---|---|
| FR (igraph) | 0.6010 | 0.7438 |
| DrL (igraph) | 0.5122 | 2.5957 |
| Stress (graphlayouts) | 0.1640 | 0.3587 |
| grip default (hop) | 0.2286 | 0.3464 |
The five timing repeats reset seed 1 before each layout call. Their medians and interquartile ranges measure timing variation for that seeded setup, not variation across independent stochastic layouts. Timing includes only the layout call; graph construction, garbage collection, and scoring are excluded. Scores use the first repeat, seeds 42, 2,000 stress pairs, and up to 5,000 nonedges; exact crossings are disabled for this graph. These hardware-specific times and single-seed quality results do not establish general speed or accuracy rankings.
The saved external-package benchmark has a limited scope:
That second question needs a different benchmark track.