Explore a structural mesh, a branching power network, a recursive carpet, and a porous volume. Every figure compares metric-MDS with metric-MDS + edge-KK using the same graph and lengths. The remaining nine examples, including dwt_307, Les Miserables, curved surfaces, and the intrinsic tree, are in the extended gallery.
The layouts use SGD, grip’s default metric-MDS backend. They use uniform stress weights; they do not reproduce the inverse-squared weighting in the Zheng et al. paper. See Metric MDS for that comparison.
Blue is metric MDS; orange is MDS followed by edge-KK. Gray, when present, shows the coordinates used to construct the graph. Each visible layout includes all its edges. Switching layouts preserves the camera and scale. Each layout is Procrustes-aligned to the generating coordinates (or to metric MDS when no generating coordinates are available). Corresponding vertices are matched by their graph indices; translation and rotation/reflection minimize their summed squared separations. No resizing is used to make the layouts agree. Generating coordinates show where a geometric graph came from, but need not realize its graph shortest-path distances as straight-line separations.
Each graph has an interactive 3D view made with ivue. Use the visible layout selector to choose metric-MDS, metric-MDS + edge-KK, or both. Gray generating coordinates remain visible where available. Drag to rotate and scroll to zoom. Without a compatible ivue installation, a static view shows the same metric-MDS coordinates and alignment as the initial interactive view.
Each table reports two errors. Graph-distance error measures the fit to all shortest-path distances; edge-length error measures the fit to supplied edge lengths. Both are root-mean-square errors divided by the root-mean-square target length, so zero is exact agreement and lower is better. Scores use the returned coordinates without fitting an extra scale. Each stage’s elapsed time is also listed; preparation is excluded, and the refinement time is additional to the MDS time. These are single-workstation timings, not a speed comparison.
#> Warning in in_dir(input_dir(), expr): You changed the working directory to
#> /tmp/RtmpovXjjF/Rbuild279a46351567/grip/vignettes (probably via setwd()). It
#> will be restored to /tmp/RtmpovXjjF/Rinst279a1a19fb33/grip/scripts. See the
#> Note section in ?knitr::knit
A larger structural mesh with 1,005 vertices. Rotate the view to inspect its folds and connections.
1005 vertices; 3808 edges. Unit edge lengths.
| Layout | Graph-distance error | Edge-length error | Stage seconds |
|---|---|---|---|
| Metric MDS | 0.073 | 0.4031 | 14.874 |
| MDS + edge-KK | 0.115 | 0.1904 | 0.029 |
The connectivity of a 1,138-bus power-system matrix, showing branching at a larger scale.
1138 vertices; 1458 edges. Unit edge lengths.
| Layout | Graph-distance error | Edge-length error | Stage seconds |
|---|---|---|---|
| Metric MDS | 0.1363 | 0.5843 | 19.290 |
| MDS + edge-KK | 0.1510 | 0.1183 | 0.019 |
A recursively perforated grid lifted onto a saddle. Its holes create detours in graph paths.
64 vertices; 88 edges. Supplied edge lengths.
g <- recursive.mask.grid.surface.graph(mask.border(3), level = 2, surface = 'saddle', amplitude = .5, normalize = 'none')| Layout | Graph-distance error | Edge-length error | Stage seconds |
|---|---|---|---|
| Metric MDS | 0.0886 | 0.3841 | 0.060 |
| MDS + edge-KK | 0.2384 | 0.0080 | 0.003 |
A twisted cubical channel network with interior vertices and several routes through the volume.
104 vertices; 212 edges. Supplied edge lengths.
g <- cube.channel.network.surface.graph(side = 5, level = 1, surface = 'twisted', twist = .2, normalize = 'none')| Layout | Graph-distance error | Edge-length error | Stage seconds |
|---|---|---|---|
| Metric MDS | 0.1116 | 0.4908 | 0.151 |
| MDS + edge-KK | 0.3466 | 0.0000 | 0.002 |
This small example creates a weighted tree and draws it in 3D with SGD. The saved gallery also compares an optional edge-KK stage; it is not a recommended automatic second step.
g <- kary.tree.weighted.graph(k = 2, depth = 5, depth.decay = .85,
branch.spread = .2, normalize = 'none')
prepared <- prepare.graph.geodesic.mds(
edges = g$edges, n = g$n, edge.weights = g$edge_weights)
mds <- suppressWarnings(metric.mds(
prepared = prepared, dim = 3, backend = 'sgd', init = 'random',
n.init = 3, max.iter = 80, seed = 2026, diagnostics = FALSE))Use mds$coords as the default result. For larger graphs,
pass raw graph inputs to
metric.mds(approximation="sparse", backend="sgd", ...)
rather than creating this dense preparation; see sparse MDS.
The following reproduces the gallery’s additional comparison. Retain it only when its independent diagnostics support the intended use.
refined <- edge.kk(
coords = mds$coords, prepared = prepared, dim = 3,
stiffness.method = 'uniform', density.mix.schedule = 1,
scale.mode = 'identity', max.iter = 100,
return.trace = FALSE, diagnostics = FALSE)MDS tries to fit shortest-path distances between all pairs of
vertices. Edge-KK adjusts the drawing to fit directly
connected vertices. Here every edge has equal influence during
refinement, and scale.mode = "identity" keeps its target
length fixed. An improvement in edge lengths can come with a worse fit
to distances between more widely separated vertices.
We use three random starts for MDS, retaining the one with the lowest
stress, and allow 80 passes per start. Reaching that limit can produce
an iteration_limit warning; the small example suppresses
it, while the saved gallery records it. Refinement allows 100
iterations. These are illustration settings, not settings chosen to make
either stage win. All fits use seed 2026.
For the six new datasets, replace the graph-construction line with:
suitesparse.graphs()
#> id name
#> 1 AG-Monien/netz4504_dual netz4504_dual
#> 2 Bai/bfwa782 bfwa782
#> 3 Bai/dw256A dw256A
#> 4 Bai/dw256B dw256B
#> 5 Bai/mhda416__LCC_409_of_416 mhda416__LCC_409_of_416
#> 6 Bai/qh768 qh768
#> 7 Bai/rdb450 rdb450
#> 8 Bai/rdb800l rdb800l
#> 9 Bai/rw496 rw496
#> 10 Boeing/bcsstm34 bcsstm34
#> 11 Boeing/msc00726 msc00726
#> 12 Bomhof/circuit_2 circuit_2
#> 13 CPM/cz628 cz628
#> 14 FEMLAB/problem1 problem1
#> 15 FIDAP/ex2 ex2
#> 16 FIDAP/ex21 ex21
#> 17 Gset/G11 G11
#> 18 Gset/G12 G12
#> 19 Gset/G13 G13
#> 20 HB/1138_bus 1138_bus
#> 21 HB/494_bus 494_bus
#> 22 HB/662_bus 662_bus
#> 23 HB/685_bus 685_bus
#> 24 HB/bcspwr05 bcspwr05
#> 25 HB/bcsstk06 bcsstk06
#> 26 HB/bcsstk20__LCC_467_of_485 bcsstk20__LCC_467_of_485
#> 27 HB/bcsstm07 bcsstm07
#> 28 HB/can_445 can_445
#> 29 HB/can_634 can_634
#> 30 HB/can_715 can_715
#> 31 HB/dwt_1005 dwt_1005
#> 32 HB/dwt_307 dwt_307
#> 33 HB/dwt_419 dwt_419
#> 34 HB/dwt_503 dwt_503
#> 35 HB/dwt_592 dwt_592
#> 36 HB/dwt_66 dwt_66
#> 37 HB/dwt_758 dwt_758
#> 38 HB/fs_760_3__LCC_745_of_760 fs_760_3__LCC_745_of_760
#> 39 HB/gre_512 gre_512
#> 40 HB/hor_131 hor_131
#> 41 HB/illc1033 illc1033
#> 42 HB/impcol_d impcol_d
#> 43 HB/lns_511__LCC_503_of_511 lns_511__LCC_503_of_511
#> 44 HB/lnsp_511__LCC_503_of_511 lnsp_511__LCC_503_of_511
#> 45 HB/lock1074 lock1074
#> 46 HB/lock_700__LCC_691_of_700 lock_700__LCC_691_of_700
#> 47 HB/lshp_406 lshp_406
#> 48 HB/lshp_577 lshp_577
#> 49 HB/lshp_778 lshp_778
#> 50 HB/mcfe__LCC_731_of_765 mcfe__LCC_731_of_765
#> 51 HB/nnc666 nnc666
#> 52 HB/nos5 nos5
#> 53 HB/nos6 nos6
#> 54 HB/nos7 nos7
#> 55 HB/shl_0 shl_0
#> 56 HB/shl_200 shl_200
#> 57 HB/shl_400 shl_400
#> 58 HB/steam2 steam2
#> 59 HB/west0497 west0497
#> 60 HB/west0655 west0655
#> 61 JGD_Trefethen/Trefethen_500 Trefethen_500
#> 62 JGD_Trefethen/Trefethen_700 Trefethen_700
#> 63 ML_Graph/Spectro_10NN Spectro_10NN
#> 64 ML_Graph/micromass_10NN micromass_10NN
#> 65 ML_Graph/umistfacesnorm_10NN umistfacesnorm_10NN
#> 66 MathWorks/Harvard500 Harvard500
#> 67 Morandini/rotor2__LCC_785_of_791 rotor2__LCC_785_of_791
#> 68 Mycielski/mycielskian10 mycielskian10
#> 69 Negre/dendrimer dendrimer
#> 70 Newman/lesmis lesmis
#> 71 PARSEC/Si2 Si2
#> 72 Rommes/M10PI_n M10PI_n
#> 73 Rommes/M10PI_n1__LCC_526_of_528 M10PI_n1__LCC_526_of_528
#> 74 Sandia/oscil_dcop_01__LCC_426_of_430 oscil_dcop_01__LCC_426_of_430
#> 75 Sandia/oscil_trans_01 oscil_trans_01
#> 76 VDOL/dynamicSoaringProblem_1 dynamicSoaringProblem_1
#> 77 VDOL/freeFlyingRobot_1__LCC_796_of_798 freeFlyingRobot_1__LCC_796_of_798
#> 78 VDOL/lowThrust_1__LCC_558_of_584 lowThrust_1__LCC_558_of_584
#> 79 VDOL/orbitRaising_1__LCC_440_of_442 orbitRaising_1__LCC_440_of_442
#> 80 VDOL/orbitRaising_2__LCC_737_of_739 orbitRaising_2__LCC_737_of_739
#> 81 VDOL/orbitRaising_3__LCC_759_of_761 orbitRaising_3__LCC_759_of_761
#> 82 VDOL/reorientation_1__LCC_675_of_677 reorientation_1__LCC_675_of_677
#> 83 VDOL/spaceShuttleEntry_1 spaceShuttleEntry_1
#> 84 VDOL/spaceStation_3__LCC_463_of_467 spaceStation_3__LCC_463_of_467
#> 85 VDOL/tumorAntiAngiogenesis_3 tumorAntiAngiogenesis_3
#> group vertices edges conversion_type components original_vertices
#> 1 AG-Monien 615 1171 square 1 615
#> 2 Bai 782 3394 square 1 782
#> 3 Bai 512 1004 square 1 512
#> 4 Bai 512 1024 square 1 512
#> 5 Bai 409 5177 largest_component 1 416
#> 6 Bai 768 1322 square 1 768
#> 7 Bai 450 1065 square 1 450
#> 8 Bai 800 1920 square 1 800
#> 9 Bai 496 1423 square 1 496
#> 10 Boeing 588 11841 square 1 588
#> 11 Boeing 726 16896 square 1 726
#> 12 Bomhof 4510 9964 square 1 4510
#> 13 CPM 628 4456 square 1 628
#> 14 FEMLAB 415 1182 square 1 415
#> 15 FIDAP 441 13199 square 1 441
#> 16 FIDAP 656 9244 square 1 656
#> 17 Gset 800 1600 square 1 800
#> 18 Gset 800 1600 square 1 800
#> 19 Gset 800 1600 square 1 800
#> 20 HB 1138 1458 square 1 1138
#> 21 HB 494 586 square 1 494
#> 22 HB 662 906 square 1 662
#> 23 HB 685 1282 square 1 685
#> 24 HB 443 590 square 1 443
#> 25 HB 420 3720 square 1 420
#> 26 HB 467 1295 largest_component 1 485
#> 27 HB 420 3416 square 1 420
#> 28 HB 445 1682 square 1 445
#> 29 HB 634 3297 square 1 634
#> 30 HB 715 2975 square 1 715
#> 31 HB 1005 3808 square 1 1005
#> 32 HB 307 1108 square 1 307
#> 33 HB 419 1572 square 1 419
#> 34 HB 503 2762 square 1 503
#> 35 HB 592 2256 square 1 592
#> 36 HB 66 127 square 1 66
#> 37 HB 758 2618 square 1 758
#> 38 HB 745 3404 largest_component 1 760
#> 39 HB 512 1680 square 1 512
#> 40 HB 434 2071 square 1 434
#> 41 HB 1353 4719 bipartite 1 1353
#> 42 HB 425 1193 square 1 425
#> 43 HB 503 1425 largest_component 1 511
#> 44 HB 503 1425 largest_component 1 511
#> 45 HB 1074 25275 square 37 1074
#> 46 HB 691 10742 largest_component 1 700
#> 47 HB 406 1155 square 1 406
#> 48 HB 577 1656 square 1 577
#> 49 HB 778 2247 square 1 778
#> 50 HB 731 15086 largest_component 1 765
#> 51 HB 666 2136 square 1 666
#> 52 HB 468 2352 square 1 468
#> 53 HB 675 1290 square 1 675
#> 54 HB 729 1944 square 1 729
#> 55 HB 663 1682 square 1 663
#> 56 HB 663 1720 square 1 663
#> 57 HB 663 1709 square 1 663
#> 58 HB 600 3670 square 1 600
#> 59 HB 497 1710 square 1 497
#> 60 HB 655 2795 square 1 655
#> 61 JGD_Trefethen 500 3989 square 1 500
#> 62 JGD_Trefethen 700 5977 square 1 700
#> 63 ML_Graph 531 3711 square 1 531
#> 64 ML_Graph 571 4834 square 1 571
#> 65 ML_Graph 575 3495 square 1 575
#> 66 MathWorks 500 2043 square 1 500
#> 67 Morandini 785 8833 largest_component 1 791
#> 68 Mycielski 767 22196 square 1 767
#> 69 Negre 730 31147 square 1 730
#> 70 Newman 77 254 square 1 77
#> 71 PARSEC 769 8516 square 1 769
#> 72 Rommes 682 689 square 1 682
#> 73 Rommes 526 533 largest_component 1 528
#> 74 Sandia 426 603 largest_component 1 430
#> 75 Sandia 430 625 square 1 430
#> 76 VDOL 647 2502 square 1 647
#> 77 VDOL 796 2384 largest_component 1 798
#> 78 VDOL 558 2819 largest_component 1 584
#> 79 VDOL 440 1332 largest_component 1 442
#> 80 VDOL 737 3061 largest_component 1 739
#> 81 VDOL 759 2841 largest_component 1 761
#> 82 VDOL 675 3464 largest_component 1 677
#> 83 VDOL 560 3286 square 1 560
#> 84 VDOL 463 2420 largest_component 1 467
#> 85 VDOL 410 1853 square 1 410
#> retained_fraction gallery original_six
#> 1 1.0000000 TRUE FALSE
#> 2 1.0000000 TRUE FALSE
#> 3 1.0000000 TRUE FALSE
#> 4 1.0000000 TRUE FALSE
#> 5 0.9831731 TRUE FALSE
#> 6 1.0000000 TRUE FALSE
#> 7 1.0000000 TRUE FALSE
#> 8 1.0000000 TRUE FALSE
#> 9 1.0000000 TRUE FALSE
#> 10 1.0000000 TRUE FALSE
#> 11 1.0000000 TRUE FALSE
#> 12 1.0000000 TRUE FALSE
#> 13 1.0000000 TRUE FALSE
#> 14 1.0000000 TRUE FALSE
#> 15 1.0000000 TRUE FALSE
#> 16 1.0000000 TRUE FALSE
#> 17 1.0000000 TRUE FALSE
#> 18 1.0000000 TRUE FALSE
#> 19 1.0000000 TRUE FALSE
#> 20 1.0000000 FALSE TRUE
#> 21 1.0000000 TRUE TRUE
#> 22 1.0000000 TRUE FALSE
#> 23 1.0000000 TRUE FALSE
#> 24 1.0000000 TRUE FALSE
#> 25 1.0000000 TRUE FALSE
#> 26 0.9628866 TRUE FALSE
#> 27 1.0000000 TRUE FALSE
#> 28 1.0000000 TRUE FALSE
#> 29 1.0000000 TRUE FALSE
#> 30 1.0000000 TRUE FALSE
#> 31 1.0000000 FALSE TRUE
#> 32 1.0000000 FALSE TRUE
#> 33 1.0000000 TRUE FALSE
#> 34 1.0000000 TRUE FALSE
#> 35 1.0000000 TRUE FALSE
#> 36 1.0000000 FALSE TRUE
#> 37 1.0000000 TRUE FALSE
#> 38 0.9802632 TRUE FALSE
#> 39 1.0000000 TRUE FALSE
#> 40 1.0000000 TRUE FALSE
#> 41 1.0000000 TRUE FALSE
#> 42 1.0000000 TRUE FALSE
#> 43 0.9843444 TRUE FALSE
#> 44 0.9843444 TRUE FALSE
#> 45 1.0000000 TRUE FALSE
#> 46 0.9871429 TRUE FALSE
#> 47 1.0000000 TRUE FALSE
#> 48 1.0000000 TRUE FALSE
#> 49 1.0000000 TRUE FALSE
#> 50 0.9555556 TRUE FALSE
#> 51 1.0000000 TRUE FALSE
#> 52 1.0000000 TRUE FALSE
#> 53 1.0000000 TRUE FALSE
#> 54 1.0000000 TRUE FALSE
#> 55 1.0000000 TRUE FALSE
#> 56 1.0000000 TRUE FALSE
#> 57 1.0000000 TRUE FALSE
#> 58 1.0000000 TRUE FALSE
#> 59 1.0000000 TRUE FALSE
#> 60 1.0000000 TRUE FALSE
#> 61 1.0000000 TRUE FALSE
#> 62 1.0000000 TRUE FALSE
#> 63 1.0000000 TRUE FALSE
#> 64 1.0000000 TRUE FALSE
#> 65 1.0000000 TRUE FALSE
#> 66 1.0000000 TRUE FALSE
#> 67 0.9924147 TRUE FALSE
#> 68 1.0000000 TRUE FALSE
#> 69 1.0000000 TRUE FALSE
#> 70 1.0000000 FALSE TRUE
#> 71 1.0000000 TRUE FALSE
#> 72 1.0000000 TRUE FALSE
#> 73 0.9962121 TRUE FALSE
#> 74 0.9906977 TRUE FALSE
#> 75 1.0000000 TRUE FALSE
#> 76 1.0000000 TRUE FALSE
#> 77 0.9974937 TRUE FALSE
#> 78 0.9554795 TRUE FALSE
#> 79 0.9954751 TRUE FALSE
#> 80 0.9972936 TRUE FALSE
#> 81 0.9973719 TRUE FALSE
#> 82 0.9970458 TRUE FALSE
#> 83 1.0000000 TRUE FALSE
#> 84 0.9914347 TRUE FALSE
#> 85 1.0000000 TRUE FALSE
g <- suitesparse.graph('lesmis')
head(g$vertex_data)
#> vertex source_index label source_partition original_vertex vertex_id
#> 1 1 1 Myriel row 1 v:1
#> 2 2 2 Napoleon row 2 v:2
#> 3 3 3 MlleBaptistine row 3 v:3
#> 4 4 4 MmeMagloire row 4 v:4
#> 5 5 5 CountessDeLo row 5 v:5
#> 6 6 6 Geborand row 6 v:6
#> source_vertex_id component
#> 1 v:1 1
#> 2 v:2 1
#> 3 v:3 1
#> 4 v:4 1
#> 5 v:5 1
#> 6 v:6 1Each dataset supplies n, edges,
edge_weights, vertex_data, and
provenance. For these six examples, vertex indices follow
the original matrix rows. The six graphs are connected; no vertices were
dropped or connecting edges added. We use unit edge lengths and preserve
the original matrix values separately. For example, Les Miserables
coappearance counts describe connection strength, so they are not passed
to the layout functions as distances.
| Need | Public entry points | Output |
|---|---|---|
| Topology alone | edges.path(), edges.mesh(),
edges.kary.tree(), the other primitive
edges.*() generators |
Two-column edge matrix; no bundled reference coordinates or lengths. |
| A finite perforated grid | keep.periodic.holes(),
keep.asymmetric.notches(),
keep.slit.channels(),
keep.staggered.windows(), then
occupied.mesh.surface.graph() |
A keep matrix followed by a complete weighted graph bundle. |
| A recursive pattern | Square mask.*() helpers, cubic
mask.cube.*() arrays, or triangle/tetrahedron masks, then
the matching recursive.*.surface.graph() |
A mask followed by a recursively generated weighted bundle. |
| A regular reference surface | mesh.surface.graph(),
cylinder.surface.graph(),
sphere.surface.graph(),
torus.surface.graph() |
Edges, lengths, 3D reference coordinates, and parameters. |
| Irregular surfaces or volumes | The irregular.*.surface.graph() and
irregular.*.solid.graph() families |
Deterministic geometric constructions; “irregular” does not imply random sampling. |
| Triangular-lattice or polyhedral topology | The three triangulated.*.surface.graph() families |
Graph edges and reference coordinates; the public bundles do not expose triangular faces. |
| Randomly sampled rectangle | sampled.rectangle.surface.graph() or plural
sampled.rectangle.surface.graphs() |
One graph or a sequence on a shared sampled point set. |
| Intrinsic lengths without an embedding | kary.tree.weighted.graph() |
Tree edges, lengths, depths, parents, and edge metadata. |
The construction recipes in the extended
gallery illustrate additional families. Use complete public bundles;
standalone embeddings and specialized edge builders used inside them are
internal functions. A name ending in surface.graph is not a
promise of a triangle mesh: occupied-cube families, for example, connect
occupied cells and can include interior vertices.
Use a constructor when you need controlled topology or generating coordinates. The extended gallery includes executable recipes and 3D views of a folded irregular annulus and a recursive Vicsek pattern. Change one feature at a time when studying its effect. The complete constructor catalog is in Finding your way around grip.
The small tree example runs when this vignette is built. The gallery
uses saved layouts so rebuilding the document does not repeat the larger
fits. To draw any graph yourself, use its construction line above
followed by the two-step example. Keep dim = 3 and the same
edge lengths in both steps.
The full recipe is installed as
scripts/graph-examples.R. Instructions for rebuilding the
saved layouts and images are in the reproduction
guide. The saved file retains the settings, software version, all
MDS starts, warnings, and any unsuccessful fits. This gallery uses dense
shortest-path distances; check graph size before increasing the
construction sizes substantially.
The six graph datasets are derived from the SuiteSparse Matrix Collection,
distributed under CC BY 4.0.
Original matrices, author credits, character labels, and descriptions of
the conversion are included in extdata/zheng-graphs/. See
help('suitesparse.graph') for details.