Skip to content

Fidelity

Commit
2a743bf · 2026-10-19
Generated
2026-10-19 13:58:24 UTC
Source
Measured: gv-diff e2e run under Apple and portable math on Darwin arm64, matching the committed baselines
Reference
C Graphviz 16.1.0, no Pango plugin

The port is checked against C Graphviz 16.1.0 in four ways, from the strictest to the broadest. All the corpus checks run in cargo test --workspace as ratchets: each has a committed baseline listing the files that match, and a file that stops matching fails the build. The numbers on this page are read from those runs and baselines by scripts/site-data.py; none are typed in by hand, except where marked.

Two math profiles are reported. macOS uses Apple’s libm, as C does on the reference machine. Linux and WASM use a portable math library, because Apple’s pow, sin, cos and atan2 are closed source and not reproduced bit for bit there; Linux on aarch64, Linux on x86_64 and the WASM build give the same bytes as each other (why).

gv-diff e2e runs the whole port, parse to layout to render, on every input of nine corpora and compares its -Tsvg output with C’s. Identical means equal bytes, after dropping the one line that starts with <!-- Generated by , which names the program and its version. Nothing else is normalised.

Only hashes of C’s output are committed (tests/golden/e2e.json, 64-bit FNV-1a of the normalised SVG), so the check runs without C installed. Each C reference was produced twice; inputs whose C output differed between the two runs are recorded as unstable and skipped.

CorpusInputsFilesmacOSLinux, WASMC unstable, skipped
maintests/wild, tests/fixtures565565564·
neatotests/neato442442442·
sfdptests/sfdp297297183·
circotests/circo280280279·
twopitests/twopi2812812818
fdptests/fdp3823823794
osagetests/osage180180180·
patchworktests/patchwork141141141·
rendertests/render212121·
Total2,5892,5892,47012

Each patch below is one file. The macOS sheet is solid; switch to the portable profile to see where Apple’s libm matters: almost all of it in sfdp, whose multilevel layout amplifies a last-bit difference in pow into a different drawing.

identical differs C unstable, skipped

mainneatosfdpcircotwopifdposagepatchworkrendertwopi/fx_arrows: C unstable, skippedtwopi/wild_gv16_directed_oldarrows_f70da3: C unstable, skippedtwopi/wild_gv16_graphs_arrows_148b6b: C unstable, skippedtwopi/wild_gv16_graphs_arrowsize_46ccd8: C unstable, skippedtwopi/wild_gv16_graphs_newarrows_769825: C unstable, skippedtwopi/wild_gv16_linux_x86_arrows_dot_1ce23f: C unstable, skippedtwopi/wild_wild_graphs_arrows_a4826e: C unstable, skippedtwopi/wild_wild_graphs_arrowsize_ccef6e: C unstable, skippedfdp/wild_gv16_graphs_badvoro_2e8cb8: C unstable, skippedfdp/wild_gv16_graphs_ngk10_4_6aab96: C unstable, skippedfdp/wild_gv16_infosrc_plugins_1a797f: C unstable, skippedfdp/wild_gv16_undirected_ngk10_4_793340: C unstable, skipped
2,589 identical · 0 differ · 12 skipped
The 119 files that differ under portable math
  • circo/wild_gv16_undirected_ngk10_4_793340
  • fdp/wild_gv16_graphs_b15_8c212e
  • fdp/wild_gv16_graphs_b51_33bcb0
  • fdp/wild_gv16_graphs_ldbxtried_9e83ae
  • main/gallery_beautify_1_1cfb90
  • sfdp/attr_beautify_tree
  • sfdp/attr_label_scheme_1
  • sfdp/attr_label_scheme_2
  • sfdp/attr_label_scheme_3
  • sfdp/attr_label_scheme_4
  • sfdp/attr_label_scheme_4_overlap
  • sfdp/attr_levels_0
  • sfdp/attr_levels_1
  • sfdp/attr_levels_2
  • sfdp/attr_normalize
  • sfdp/attr_overlap_false_grid
  • sfdp/attr_overlap_false_labels
  • sfdp/attr_quadtree_none
  • sfdp/attr_quadtree_normal
  • sfdp/attr_ratio_fill
  • sfdp/attr_repulsiveforce_2
  • sfdp/attr_repulsiveforce_2_grid
  • sfdp/attr_repulsiveforce_3
  • sfdp/attr_rotation_-45
  • sfdp/attr_rotation_30
  • sfdp/attr_rotation_90
  • sfdp/attr_size
  • sfdp/attr_start_7
  • sfdp/attr_start_random42
  • sfdp/fx_ER
  • sfdp/fx_Heawood
  • sfdp/fx_NaN
  • sfdp/fx_Petersen
  • sfdp/fx_abstract
  • sfdp/fx_alf
  • sfdp/fx_arrows
  • sfdp/fx_clust3
  • sfdp/fx_pack
  • sfdp/fx_record
  • sfdp/fx_unix
  • sfdp/fx_world
  • sfdp/gen_grid10x10
  • sfdp/gen_grid20x20
  • sfdp/gen_grid8x40
  • sfdp/gen_powerlaw1000
  • sfdp/gen_powerlaw300
  • sfdp/gen_rnd100
  • sfdp/gen_rnd2000
  • sfdp/gen_rnd300
  • sfdp/gen_rnd800
  • sfdp/gen_tree4_ternary
  • sfdp/gen_tree7
  • sfdp/gen_tree9
  • sfdp/hw_C60
  • sfdp/hw_K5
  • sfdp/hw_K8_box
  • sfdp/hw_disconnected
  • sfdp/hw_disconnected_pack20
  • sfdp/hw_disconnected_packarray
  • sfdp/hw_labels
  • sfdp/hw_loops
  • sfdp/hw_petersen
  • sfdp/hw_strict_multi
  • sfdp/hw_weights
  • sfdp/wild_gv16_directed_arrows_54d1cf
  • sfdp/wild_gv16_directed_crazy_982d4d
  • sfdp/wild_gv16_directed_oldarrows_f70da3
  • sfdp/wild_gv16_graphs_ER_1359e3
  • sfdp/wild_gv16_graphs_Heawood_771643
  • sfdp/wild_gv16_graphs_Petersen_8a008f
  • sfdp/wild_gv16_graphs_arrows_148b6b
  • sfdp/wild_gv16_graphs_arrowsize_46ccd8
  • sfdp/wild_gv16_graphs_b34_ca01c7
  • sfdp/wild_gv16_graphs_b56_36f937
  • sfdp/wild_gv16_graphs_b69_4b5b48
  • sfdp/wild_gv16_graphs_badvoro_2e8cb8
  • sfdp/wild_gv16_graphs_clover_ae8650
  • sfdp/wild_gv16_graphs_crazy_cfdc7a
  • sfdp/wild_gv16_graphs_d_3a4a40
  • sfdp/wild_gv16_graphs_dpd_223cb3
  • sfdp/wild_gv16_graphs_ldbxtried_9e83ae
  • sfdp/wild_gv16_graphs_newarrows_769825
  • sfdp/wild_gv16_graphs_ngk10_4_6aab96
  • sfdp/wild_gv16_graphs_p2_99819c
  • sfdp/wild_gv16_graphs_p4_849964
  • sfdp/wild_gv16_graphs_pack_1c2410
  • sfdp/wild_gv16_graphs_pm2way_85dfc3
  • sfdp/wild_gv16_graphs_pmpipe_2cd2b2
  • sfdp/wild_gv16_graphs_proc3d_a79c48
  • sfdp/wild_gv16_graphs_triedds_2395ab
  • sfdp/wild_gv16_graphs_viewport_651d5b
  • sfdp/wild_gv16_linux_x86_arrows_dot_1ce23f
  • sfdp/wild_gv16_linux_x86_edgeclip_dot_17e57b
  • sfdp/wild_gv16_linux_x86_html2_dot_3ee3fc
  • sfdp/wild_gv16_linux_x86_pack_neato1_0327de
  • sfdp/wild_gv16_linux_x86_pack_neato2_dc0918
  • sfdp/wild_gv16_linux_x86_pack_neato_c0395a
  • sfdp/wild_gv16_linux_x86_ports_dot_99eb47
  • sfdp/wild_gv16_linux_x86_rankdir_dot1_8f7d7d
  • sfdp/wild_gv16_linux_x86_rankdir_dot2_810f01
  • sfdp/wild_gv16_linux_x86_rankdir_dot_f06bd6
  • sfdp/wild_gv16_linux_x86_record_dot_dd3ea1
  • sfdp/wild_gv16_linux_x86_rowcolsep_dot1_2c201c
  • sfdp/wild_gv16_linux_x86_rowcolsep_dot_0ad7dd
  • sfdp/wild_gv16_nshare_html2_dot_7bea59
  • sfdp/wild_gv16_nshare_neatosplines_neato1_9c01b5
  • sfdp/wild_gv16_nshare_neatosplines_neato_7ac8ee
  • sfdp/wild_gv16_nshare_overlap_neato_afd9ce
  • sfdp/wild_gv16_nshare_pack_neato1_014679
  • sfdp/wild_gv16_nshare_pack_neato2_10690f
  • sfdp/wild_gv16_nshare_pack_neato_e0a2bb
  • sfdp/wild_gv16_nshare_ports_dot_25dc96
  • sfdp/wild_gv16_nshare_rankdir_dot1_228ae0
  • sfdp/wild_gv16_nshare_rankdir_dot2_920cb4
  • sfdp/wild_gv16_nshare_rankdir_dot_0e4e43
  • sfdp/wild_gv16_nshare_record_dot_f557a8
  • sfdp/wild_wild_graphs_arrows_a4826e
  • sfdp/wild_wild_graphs_arrowsize_ccef6e
  • sfdp/wild_wild_graphs_complex_edges_71191c
The 12 inputs skipped because C's output changes between two runs
  • fdp/wild_gv16_graphs_badvoro_2e8cb8
  • fdp/wild_gv16_graphs_ngk10_4_6aab96
  • fdp/wild_gv16_infosrc_plugins_1a797f
  • fdp/wild_gv16_undirected_ngk10_4_793340
  • twopi/fx_arrows
  • twopi/wild_gv16_directed_oldarrows_f70da3
  • twopi/wild_gv16_graphs_arrows_148b6b
  • twopi/wild_gv16_graphs_arrowsize_46ccd8
  • twopi/wild_gv16_graphs_newarrows_769825
  • twopi/wild_gv16_linux_x86_arrows_dot_1ce23f
  • twopi/wild_wild_graphs_arrows_a4826e
  • twopi/wild_wild_graphs_arrowsize_ccef6e

Yes. Running C itself with the port’s portable math functions (scripts/dot-portable-libm.sh loads them into the unmodified C dot) and comparing again:

vs stock C on macOS vs C with the port’s math functions
Linux and WASM build 2,470 / 2,589 2,590 / 2,590
C Graphviz built on Linux, for comparison 2,005 / 2,586 —

With the same math functions on both sides, the browser build is byte-identical to C on every stable input; a ratchet in CI keeps it that way. C itself is less consistent across platforms than the port: built on Linux the way Homebrew builds it on macOS, it matches macOS C on 77.5% of the corpus, against 95.4% for the port in the browser. The details, and what makes Linux C differ (glibc’s math, qsort and rand), are in Math off macOS.

gv-diff (without e2e) compares geometry rather than bytes: it lays out each input with the port, reads C’s -Tdot output for the same input, and checks the bounding box, every node’s centre and size, every edge’s spline, arrow endpoints and label positions, and every cluster box. A file is exact when every element is within tolerance: 1 pt for node positions, sizes and bounding boxes, 2 pt for edge splines (sampled Hausdorff distance) and labels. This is looser than byte identity, and it covers the per-engine corpora with C goldens kept as .dot files (tests/golden/<engine>/).

CorpusFilesmacOS exactLinux, WASM exactNot exact (macOS)
main565565564·
neato442442442·
sfdp297297208·
circo280280279·
twopi289282281fx_arrowswild_gv16_directed_oldarrows_f70da3wild_gv16_graphs_arrowsize_46ccd8wild_gv16_graphs_newarrows_769825wild_gv16_linux_x86_arrows_dot_1ce23fwild_wild_graphs_arrows_a4826ewild_wild_graphs_arrowsize_ccef6e
fdp386386382·
osage180180180·
patchwork141141141·

The 7 twopi files are a floating-point tie in edge routing that C itself breaks differently from run to run, so no fixed reference can match every C. The full-pipeline check skips the same inputs as unstable.

gv-diff render takes C’s own layouts (tests/golden/render/laid/*.dot) and renders them with the port in no-layout mode (-Kneato -n2), next to C’s renderings of the same layouts. Any difference comes from the output code (or node sizing, which -n2 recomputes). Identity is byte for byte, generator comment aside.

FormatFilesmacOS identicalLinux, WASM identicalSkippedDiffer
-Tsvg547547547··
-Txdot547547547··
-Tdot547547547··
-Tjson511511511··
-Tjson0547547547··
-Tplain53753553510gv16_graphs_Latin1_cd0c3dgv16_graphs_b60_bf5b3a
-Tplain-ext53753553510gv16_graphs_Latin1_cd0c3dgv16_graphs_b60_bf5b3a

The skipped -Tplain and -Tplain-ext references are ones C does not reproduce from run to run: for some converted labels C prints a string after lib/common/output.c has freed it, so the field holds whatever is left in that memory. The two that differ hit the same path; on the reference machine C prints an empty label there, while the port prints the label.

scripts/dot-broad-validate.sh lays out every graph in the Graphviz 16.1.0 source tree (graphs/, tests/graphs/, tests/regression_tests/) with C and with the port, in five variants, and gv-diff pairs compares the layouts as above.

VariantFlagsExactComparedNote
base·379379
LR-Grankdir=LR379379
ortho-Gsplines=ortho379379C needs over 60 s on 3 inputs; given longer, they match too
concentrate-Gconcentrate=true379379
newrank-Gnewrank=true379379

These figures are carried over from the README: they need the Graphviz source tree and the C reference on the machine, so scripts/site-data.py does not regenerate them.

The corpora only contain inputs someone thought of. scripts/fuzz-gen.py generates random graphs from a seed (engine, size, topology, clusters, rank sets, attributes from the whole Graphviz table, and about 8% malformed input), and scripts/fuzz-run.py runs C and the port on each and compares stdout (generator comment aside) and exit status. Every difference was minimised and fixed against the C source, round by round; one reproducer per bug went into the corpus.

Identity is the share of comparable cases whose output and exit status both match C. Cases where C’s output changes between two runs, C times out, or C crashes are excluded.

RoundSeedsCasesdotneatofdpsfdptwopicircoosagepatchworkAll
0 (pilot)1-20020050.869.254.240.071.462.585.726.355.2
11-20002,00088.592.081.994.391.395.591.299.490.9
210001-130003,00096.398.995.299.299.798.6100.0100.098.1
320001-250005,00096.898.697.399.299.699.299.8100.098.4
430001-360006,00098.598.896.199.899.599.599.8100.098.8

Read from the rounds table in the fuzzing notes, which also break down the remaining differences: mostly C nondeterminism (layouts that depend on heap addresses), C undefined behaviour on inputs it reports as invalid, and a few open differences, which are tracked in What’s left.

C Graphviz 16.1.0, run with GVBINDIR pointing at a plugin directory that holds only its core, dot and neato plugins. With no Pango text-layout plugin available, C measures text with its built-in font metric tables, as the port does. A default installation, which measures text with Pango and cairo, will not match. Set it up yourself.