diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml new file mode 100644 index 0000000..99705f5 --- /dev/null +++ b/.github/workflows/python.yml @@ -0,0 +1,285 @@ +name: python wheels and release + +on: + push: + branches: [main] + tags: ["python-v*"] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + metadata: + name: Package and release version + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Check package versions and release tag + run: | + python - <<'PYTHON' + import os + import tomllib + from pathlib import Path + + package = tomllib.loads(Path('crates/hyper-viz-python/pyproject.toml').read_text()) + workspace = tomllib.loads(Path('Cargo.toml').read_text()) + desktop = tomllib.loads(Path('pyproject.toml').read_text()) + version = package['project']['version'] + assert desktop['project']['version'] == version, 'Desktop and core versions differ' + assert package['project']['optional-dependencies']['viewer'] == [f'hypergraph-viz-viewer=={version}'] + assert version == workspace['workspace']['package']['version'], 'Python and Rust versions differ' + if os.environ['GITHUB_REF_TYPE'] == 'tag': + assert os.environ['GITHUB_REF_NAME'] == f'python-v{version}', 'Tag must match package version' + print(f"{package['project']['name']} {version}: metadata matches") + PYTHON + + wheels: + name: ${{ matrix.target }} / CPython abi3 + needs: metadata + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + - os: macos-14 + target: aarch64-apple-darwin + - os: macos-15-intel + target: x86_64-apple-darwin + - os: windows-latest + target: x86_64-pc-windows-msvc + runs-on: ${{ matrix.os }} + env: + CARGO_TERM_COLOR: always + MACOSX_DEPLOYMENT_TARGET: '11.0' + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + - uses: dtolnay/rust-toolchain@1.89.0 + with: + targets: ${{ matrix.target }} + components: rustfmt, clippy + - uses: Swatinem/rust-cache@v2 + - name: Install test tools + run: python -m pip install -r crates/hyper-viz-python/requirements-test.txt + - name: Generate stubs and check drift + run: | + cargo run --locked -p hyper-viz-python --bin stub_gen + git diff --exit-code -- crates/hyper-viz-python/python/hyper_viz/_core.pyi + - name: Rust checks (no Bevy dependency) + run: | + cargo fmt --all -- --check + cargo clippy --locked -p hyper-viz -p hyper-viz-python --all-targets -- -D warnings + cargo test --locked --offline -p hyper-viz + - name: Build platform wheel + uses: PyO3/maturin-action@v1 + with: + maturin-version: '1.9.6' + target: ${{ matrix.target }} + manylinux: auto + args: --release --locked --manifest-path crates/hyper-viz-python/Cargo.toml --out dist + - name: Install built wheel + shell: bash + run: python -m pip install --no-deps dist/*.whl + - name: Test installed package + run: | + python -m pytest crates/hyper-viz-python/tests -q + python -m mypy --strict crates/hyper-viz-python/python/hyper_viz + python crates/hyper-viz-python/examples/xgi_interop.py + - name: Check wheel contents + run: python crates/hyper-viz-python/tests/check_wheel.py dist + - uses: actions/upload-artifact@v4 + with: + name: hyper-viz-${{ matrix.target }} + path: dist/*.whl + - uses: actions/setup-python@v5 + with: + python-version: '3.14' + - name: Clean-install ABI compatibility smoke test + shell: bash + run: | + python -m pip install --no-deps dist/*.whl + python -c 'from hyper_viz import HifDocument; assert "s:a" in HifDocument.from_json("{\"incidences\":[{\"node\":\"a\",\"edge\":\"e\"}]}").to_hypergraph_json()' + + sdist: + name: Source distribution + needs: metadata + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - uses: dtolnay/rust-toolchain@1.89.0 + - uses: Swatinem/rust-cache@v2 + - name: Build and unpack source distribution + shell: bash + run: | + python -m pip install maturin==1.9.6 + python scripts/releases/build_python_sdist.py --out dist + mkdir packaged-source + tar -xzf dist/*.tar.gz -C packaged-source --strip-components=1 + - name: Build wheel from packaged source + working-directory: packaged-source + run: maturin build --release --locked --out ../sdist-wheel + - name: Verify wheel built from packaged source + shell: bash + run: | + python crates/hyper-viz-python/tests/check_wheel.py sdist-wheel + python -m pip install --no-deps sdist-wheel/*.whl + python -c 'from hyper_viz import HifDocument; HifDocument.from_json("{\"incidences\":[]}").to_hypergraph_json()' + - uses: actions/upload-artifact@v4 + with: + name: hyper-viz-sdist + path: dist/*.tar.gz + if-no-files-found: error + + + desktop: + name: ${{ matrix.target }} / desktop viewer + needs: metadata + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + - os: macos-14 + target: aarch64-apple-darwin + - os: macos-15-intel + target: x86_64-apple-darwin + - os: windows-latest + target: x86_64-pc-windows-msvc + runs-on: ${{ matrix.os }} + env: + CARGO_TERM_COLOR: always + MACOSX_DEPLOYMENT_TARGET: '11.0' + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + - uses: dtolnay/rust-toolchain@1.89.0 + with: + targets: ${{ matrix.target }} + - uses: Swatinem/rust-cache@v2 + - name: Check desktop native dependencies + run: python scripts/releases/check_desktop_dependencies.py + - name: Install Windows binary inspection tool + if: runner.os == 'Windows' + run: python -m pip install pefile==2024.8.26 + - name: Build desktop wheel + uses: PyO3/maturin-action@v1 + env: + RUSTFLAGS: ${{ runner.os == 'Windows' && '-C target-feature=+crt-static' || '' }} + with: + maturin-version: '1.9.6' + rust-toolchain: '1.89.0' + target: ${{ matrix.target }} + manylinux: '2_28' + before-script-linux: yum install -y wayland-devel libxkbcommon-devel + args: --release --locked --auditwheel check --out desktop-dist + - name: Check desktop wheel contents + run: python scripts/releases/check_desktop_wheel.py desktop-dist + - name: Install and verify standalone desktop binary + shell: bash + run: | + python -m pip install --no-index --only-binary=:all: desktop-dist/*.whl + python scripts/releases/smoke_desktop.py --viewer-only + - uses: actions/upload-artifact@v4 + with: + name: hyper-viz-desktop-${{ matrix.target }} + path: desktop-dist/*.whl + if-no-files-found: error + + + desktop-integration: + name: ${{ matrix.target }} / installed viewer extra + needs: [wheels, desktop] + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + - os: macos-14 + target: aarch64-apple-darwin + - os: macos-15-intel + target: x86_64-apple-darwin + - os: windows-latest + target: x86_64-pc-windows-msvc + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + - uses: actions/download-artifact@v4 + with: + name: hyper-viz-desktop-${{ matrix.target }} + path: desktop-dist + - uses: actions/download-artifact@v4 + with: + name: hyper-viz-${{ matrix.target }} + path: core-dist + - name: Install core and viewer together without source builds + shell: bash + run: | + python -m pip install --no-index --only-binary=:all: --find-links core-dist --find-links desktop-dist 'hypergraph-viz[viewer]' + - name: Verify executable discovery and CLI startup + run: python scripts/releases/smoke_desktop.py + - name: Install Linux graphics test runtime + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y xvfb libvulkan1 mesa-vulkan-drivers libegl1 libxkbcommon-x11-0 libwayland-client0 libx11-6 libxrandr2 libxi6 libxcursor1 + - name: Verify native window on software Vulkan + if: runner.os == 'Linux' + env: + WGPU_BACKEND: vulkan + WINIT_UNIX_BACKEND: x11 + HYPER_VIZ_SESSION: 'off' + run: xvfb-run -a python scripts/releases/smoke_desktop.py --render + - uses: actions/upload-artifact@v4 + if: runner.os == 'Linux' && always() + with: + name: desktop-render-smoke + path: target/desktop-smoke/render.png + if-no-files-found: ignore + + publish: + name: Publish to PyPI + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/python-v') + needs: [wheels, sdist, desktop-integration] + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/project/hypergraph-viz/ + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v4 + with: + pattern: hyper-viz-* + merge-multiple: true + path: dist + - name: Separate companion and core distributions + run: | + mkdir desktop-release core-release + mv dist/hypergraph_viz_viewer-*.whl desktop-release/ + mv dist/* core-release/ + - name: Publish desktop companion with Trusted Publishing + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: desktop-release/ + - name: Publish core after companion is available + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: core-release/ diff --git a/.gitignore b/.gitignore index 3d62c7b..7c8e207 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,13 @@ /target .env +.venv/ +__pycache__/ +.pytest_cache/ +.mypy_cache/ +*.egg-info/ +scripts/benchmarks/browser/node_modules/ + +# Editable maturin builds place the extension in the Python source package. +/crates/hyper-viz-python/python/hyper_viz/*.so +/crates/hyper-viz-python/python/hyper_viz/*.pyd diff --git a/Cargo.lock b/Cargo.lock index 08bef2d..b75b36b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,6 +90,7 @@ dependencies = [ "cfg-if", "getrandom 0.3.4", "once_cell", + "serde", "version_check", "zerocopy", ] @@ -216,6 +217,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + [[package]] name = "approx" version = "0.5.1" @@ -851,22 +858,6 @@ dependencies = [ "smol_str", ] -[[package]] -name = "bevy_gilrs" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611827ab0ce43b88c0a695e6603901b5f34687efecaf526c861456c9d8e6fedb" -dependencies = [ - "bevy_app", - "bevy_ecs", - "bevy_input", - "bevy_platform", - "bevy_time", - "gilrs", - "thiserror 2.0.20", - "tracing", -] - [[package]] name = "bevy_gizmos" version = "0.18.1" @@ -1043,7 +1034,6 @@ dependencies = [ "bevy_diagnostic", "bevy_ecs", "bevy_feathers", - "bevy_gilrs", "bevy_gizmos", "bevy_gizmos_render", "bevy_gltf", @@ -1860,12 +1850,24 @@ dependencies = [ "piper", ] +[[package]] +name = "borrow-or-share" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" + [[package]] name = "bumpalo" version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +[[package]] +name = "bytecount" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" + [[package]] name = "bytemuck" version = "1.25.2" @@ -1969,6 +1971,19 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link 0.2.1", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -2341,6 +2356,12 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" + [[package]] name = "derive_more" version = "2.1.1" @@ -2468,6 +2489,15 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" +[[package]] +name = "email_address" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" +dependencies = [ + "serde", +] + [[package]] name = "emath" version = "0.33.3" @@ -2594,6 +2624,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fancy-regex" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "998b056554fbe42e03ae0e152895cd1a7e1002aec800fdc6635d20270260c46f" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + [[package]] name = "fastrand" version = "2.5.0" @@ -2638,10 +2679,15 @@ dependencies = [ ] [[package]] -name = "fnv" -version = "1.0.7" +name = "fluent-uri" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "1918b65d96df47d3591bed19c5cca17e3fa5d0707318e4b5ef2eae01764df7e5" +dependencies = [ + "borrow-or-share", + "ref-cast", + "serde", +] [[package]] name = "foldhash" @@ -2732,6 +2778,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fraction" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076045bb43dac435333ed5f04caf35c7463631d0dae2deb2638d94dd0a5b872" +dependencies = [ + "lazy_static", + "num", +] + [[package]] name = "futures-channel" version = "0.3.34" @@ -2806,6 +2862,15 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "getopts" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -2842,40 +2907,6 @@ dependencies = [ "r-efi 6.0.0", ] -[[package]] -name = "gilrs" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "902fb00d3f6398e635be22e5c837b303c501835cca7ac11a47bba138f7aafdd8" -dependencies = [ - "fnv", - "gilrs-core", - "log", - "uuid", - "vec_map", -] - -[[package]] -name = "gilrs-core" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc7f0ce6237abcc0523f2a5502b1e3fe5802daaae47ac14e166fe49551301ea9" -dependencies = [ - "inotify", - "js-sys", - "libc", - "libudev-sys", - "log", - "nix", - "objc2-core-foundation", - "objc2-io-kit", - "uuid", - "vec_map", - "wasm-bindgen", - "web-sys", - "windows 0.62.2", -] - [[package]] name = "gl_generator" version = "0.14.0" @@ -3133,7 +3164,7 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "hyper" -version = "0.1.0" +version = "0.1.2" dependencies = [ "clap", "hyper-viz", @@ -3144,8 +3175,10 @@ dependencies = [ [[package]] name = "hyper-viz" -version = "0.1.0" +version = "0.1.2" dependencies = [ + "jsonschema", + "num-bigint", "rand 0.8.7", "serde", "serde_json", @@ -3156,12 +3189,13 @@ dependencies = [ [[package]] name = "hyper-viz-bevy" -version = "0.1.0" +version = "0.1.2" dependencies = [ "bevy", "bevy_egui", "bevy_panorbit_camera", "hyper-viz", + "serde_json", "tempfile", "tracing", "tracing-subscriber", @@ -3169,6 +3203,40 @@ dependencies = [ "winit", ] +[[package]] +name = "hyper-viz-python" +version = "0.1.2" +dependencies = [ + "hyper-viz", + "pyo3", + "pyo3-stub-gen", + "serde_json", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "2.3.0" @@ -3300,30 +3368,19 @@ dependencies = [ ] [[package]] -name = "inflections" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" - -[[package]] -name = "inotify" -version = "0.11.5" +name = "indoc" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc00ea907cab49550b7da656f80ebb97be1b997d931fbcd28d39734e17ce592" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" dependencies = [ - "bitflags 2.13.1", - "inotify-sys", - "libc", + "rustversion", ] [[package]] -name = "inotify-sys" -version = "0.1.8" +name = "inflections" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d" -dependencies = [ - "libc", -] +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" [[package]] name = "inventory" @@ -3334,12 +3391,33 @@ dependencies = [ "rustversion", ] +[[package]] +name = "is-macro" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8267aa6001e25494f3015f9663bbd88a18240c74483afa5f0934a1b3e4c388e9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -3459,6 +3537,32 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonschema" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d46662859bc5f60a145b75f4632fbadc84e829e45df6c5de74cfc8e05acb96b5" +dependencies = [ + "ahash", + "base64", + "bytecount", + "email_address", + "fancy-regex", + "fraction", + "idna", + "itoa", + "num-cmp", + "num-traits", + "once_cell", + "percent-encoding", + "referencing", + "regex", + "regex-syntax", + "serde", + "serde_json", + "uuid-simd", +] + [[package]] name = "khronos-egl" version = "6.0.0" @@ -3486,21 +3590,16 @@ dependencies = [ ] [[package]] -name = "lazy_static" -version = "1.5.0" +name = "lalrpop-util" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" [[package]] -name = "lewton" -version = "0.10.2" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030" -dependencies = [ - "byteorder", - "ogg", - "tinyvec", -] +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" @@ -3536,16 +3635,6 @@ dependencies = [ "redox_syscall 0.9.4", ] -[[package]] -name = "libudev-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" -dependencies = [ - "libc", - "pkg-config", -] - [[package]] name = "linebender_resource_handle" version = "0.1.1" @@ -3609,6 +3698,12 @@ dependencies = [ "libc", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "matchers" version = "0.2.0" @@ -3633,6 +3728,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "metal" version = "0.32.0" @@ -3823,6 +3927,51 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + [[package]] name = "num-derive" version = "0.4.2" @@ -3834,6 +3983,36 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -4048,7 +4227,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" dependencies = [ - "bitflags 2.13.1", "libc", "objc2-core-foundation", ] @@ -4189,15 +4367,6 @@ dependencies = [ "nonmax", ] -[[package]] -name = "ogg" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e" -dependencies = [ - "byteorder", -] - [[package]] name = "once_cell" version = "1.21.4" @@ -4230,8 +4399,14 @@ dependencies = [ ] [[package]] -name = "owned_ttf_parser" -version = "0.25.1" +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + +[[package]] +name = "owned_ttf_parser" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" dependencies = [ @@ -4292,6 +4467,44 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.7", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.13" @@ -4392,6 +4605,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "pp-rs" version = "0.2.1" @@ -4446,6 +4665,104 @@ version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" +[[package]] +name = "pyo3" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab53c047fcd1a1d2a8820fe84f05d6be69e9526be40cb03b73f86b6b03e6d87d" +dependencies = [ + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b455933107de8642b4487ed26d912c2d899dec6114884214a0b3bb3be9261ea6" +dependencies = [ + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c85c9cbfaddf651b1221594209aed57e9e5cff63c4d11d1feead529b872a089" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5b10c9bf9888125d917fb4d2ca2d25c8df94c7ab5a52e13313a07e050a3b02" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b51720d314836e53327f5871d4c0cfb4fb37cc2c4a11cc71907a86342c40f9" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pyo3-stub-gen" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b159f7704044f57d058f528a6f1f22a0a0a327dcb595c5fb38beae658e0338d6" +dependencies = [ + "anyhow", + "chrono", + "indexmap", + "inventory", + "itertools 0.14.0", + "log", + "maplit", + "num-complex", + "pyo3", + "pyo3-stub-gen-derive", + "rustpython-parser", + "serde", + "serde_json", + "time", + "toml", +] + +[[package]] +name = "pyo3-stub-gen-derive" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c79e7c5b1fcec7c39ab186594658a971c59911eb6fbab5a5932cf2318534be" +dependencies = [ + "heck", + "indexmap", + "proc-macro2", + "quote", + "rustpython-parser", + "syn 2.0.119", +] + [[package]] name = "quick-error" version = "2.0.1" @@ -4629,6 +4946,40 @@ dependencies = [ "bitflags 2.13.1", ] +[[package]] +name = "ref-cast" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "referencing" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e9c261f7ce75418b3beadfb3f0eb1299fe8eb9640deba45ffa2cb783098697d" +dependencies = [ + "ahash", + "fluent-uri", + "once_cell", + "parking_lot", + "percent-encoding", + "serde_json", +] + [[package]] name = "regex" version = "1.13.1" @@ -4671,7 +5022,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7ceb6607dd738c99bc8cb28eff249b7cd5c8ec88b9db96c0608c1480d140fb1" dependencies = [ "cpal", - "lewton", ] [[package]] @@ -4741,6 +5091,63 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustpython-ast" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cdaf8ee5c1473b993b398c174641d3aa9da847af36e8d5eb8291930b72f31a5" +dependencies = [ + "is-macro", + "num-bigint", + "rustpython-parser-core", + "static_assertions", +] + +[[package]] +name = "rustpython-parser" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "868f724daac0caf9bd36d38caf45819905193a901e8f1c983345a68e18fb2abb" +dependencies = [ + "anyhow", + "is-macro", + "itertools 0.11.0", + "lalrpop-util", + "log", + "num-bigint", + "num-traits", + "phf", + "phf_codegen", + "rustc-hash 1.1.0", + "rustpython-ast", + "rustpython-parser-core", + "tiny-keccak", + "unic-emoji-char", + "unic-ucd-ident", + "unicode_names2", +] + +[[package]] +name = "rustpython-parser-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b6c12fa273825edc7bccd9a734f0ad5ba4b8a2f4da5ff7efe946f066d0f4ad" +dependencies = [ + "is-macro", + "memchr", + "rustpython-parser-vendored", +] + +[[package]] +name = "rustpython-parser-vendored" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04fcea49a4630a3a5d940f4d514dc4f575ed63c14c3e3ed07146634aed7f67a6" +dependencies = [ + "memchr", + "once_cell", +] + [[package]] name = "rustversion" version = "1.0.23" @@ -4851,6 +5258,15 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -4894,6 +5310,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "siphasher" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33f4fe9184a62d842c9ef383018f3306d8ba224fd9d836f56d7288308847c256" + [[package]] name = "skrifa" version = "0.39.0" @@ -5105,6 +5527,12 @@ dependencies = [ "slotmap", ] +[[package]] +name = "target-lexicon" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + [[package]] name = "tempfile" version = "3.27.0" @@ -5190,6 +5618,34 @@ dependencies = [ "zune-jpeg", ] +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tiny-skia" version = "0.11.4" @@ -5240,6 +5696,21 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "toml" +version = "1.1.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.4", +] + [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" @@ -5291,6 +5762,12 @@ dependencies = [ "winnow 1.0.4", ] +[[package]] +name = "toml_writer" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" + [[package]] name = "tracing" version = "0.1.44" @@ -5402,6 +5879,58 @@ version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "214ca0b2191785cbc06209b9ca1861e048e39b5ba33574b3cedd58363d5bb5f6" +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -5444,6 +5973,34 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unicode_names2" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1673eca9782c84de5f81b82e4109dcfb3611c8ba0d52930ec4a9478f547b2dd" +dependencies = [ + "phf", + "unicode_names2_generator", +] + +[[package]] +name = "unicode_names2_generator" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91e5b84611016120197efd7dc93ef76774f4e084cd73c9fb3ea4a86c570c56e" +dependencies = [ + "getopts", + "log", + "phf_codegen", + "rand 0.8.7", +] + +[[package]] +name = "unindent" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" + [[package]] name = "url" version = "2.5.8" @@ -5480,6 +6037,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "uuid-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b082222b4f6619906941c17eb2297fff4c2fb96cb60164170522942a200bd8" +dependencies = [ + "outref", + "uuid", + "vsimd", +] + [[package]] name = "valuable" version = "0.1.1" @@ -5497,18 +6065,18 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "walkdir" version = "2.5.0" @@ -5947,23 +6515,11 @@ version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-collections 0.2.0", + "windows-collections", "windows-core 0.61.2", - "windows-future 0.2.1", + "windows-future", "windows-link 0.1.3", - "windows-numerics 0.2.0", -] - -[[package]] -name = "windows" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" -dependencies = [ - "windows-collections 0.3.2", - "windows-core 0.62.2", - "windows-future 0.3.2", - "windows-numerics 0.3.1", + "windows-numerics", ] [[package]] @@ -5975,15 +6531,6 @@ dependencies = [ "windows-core 0.61.2", ] -[[package]] -name = "windows-collections" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" -dependencies = [ - "windows-core 0.62.2", -] - [[package]] name = "windows-core" version = "0.54.0" @@ -6041,18 +6588,7 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", "windows-link 0.1.3", - "windows-threading 0.1.0", -] - -[[package]] -name = "windows-future" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" -dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", - "windows-threading 0.2.1", + "windows-threading", ] [[package]] @@ -6121,16 +6657,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-numerics" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" -dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", -] - [[package]] name = "windows-result" version = "0.1.2" @@ -6297,15 +6823,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-threading" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" -dependencies = [ - "windows-link 0.2.1", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" diff --git a/Cargo.toml b/Cargo.toml index bb59301..89dd14a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [workspace] resolver = "2" -members = ["crates/hyper-viz", "crates/hyper-viz-bevy"] +members = ["crates/hyper-viz", "crates/hyper-viz-bevy", "crates/hyper-viz-python"] [workspace.package] -version = "0.1.0" +version = "0.1.2" edition = "2024" license = "MIT OR Apache-2.0" authors = ["Bregy Malpartida "] @@ -15,7 +15,7 @@ homepage = "https://github.com/atomicstrata/hyper" hyper-viz = { path = "crates/hyper-viz" } hyper-viz-bevy = { path = "crates/hyper-viz-bevy" } serde = { version = "1", features = ["derive"] } -serde_json = "1" +serde_json = { version = "1", features = ["arbitrary_precision"] } thiserror = "2" tracing = "0.1" diff --git a/README.md b/README.md index 40f5a88..6bb1900 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ # hyper -**hyper** is a general-purpose **hypergraph visualization** stack for Rust: -scene IR, force-directed 3D layout, and an optional native Bevy viewer. +**hyper** provides native 3D hypergraph exploration for **Python scientific +workflows** and **embedded Rust applications**. Explore explicit group memberships +with picking, search, lasso selection, and neighborhood isolation. -It is domain-agnostic — vertices and hyperedges only. +The Rust core provides HIF interchange, projections, and force-directed layout +without Bevy. The typed Python package exposes HIF interchange and can launch +the separately installed native viewer. Use scientific libraries for analysis +and Hyper to explore their exported datasets. [![Animated Mathlib source dependency atlas: 9,384 nodes, 37,356 imports, and 7,550 dependency groups](docs/media/mathlib.gif)](docs/media/mathlib.mp4) @@ -13,21 +17,125 @@ and 272 external-module placeholders. The video uses a settled layout; [reproduction instructions and live benchmarks](docs/mathlib-demo.md) explain the workload and rendering limits. -## What this is +## Choose a workflow -- A reusable library (`hyper-viz`) any app can feed with a hypergraph -- Optional native 3D window (`hyper-viz-bevy`) -- A small CLI (`hyper`) for demos and JSON file viewing -- Interchange format: **`hypergraph.v1`** JSON +| Workflow | Entry point | +|---|---| +| Exchange scientific datasets from Python | `hyper_viz.HifDocument`: load, validate, retain metadata, and export HIF | +| Explore a dataset in a native 3D window | `hyper` CLI or Python `hyper_viz.show()` | +| Embed projections and layout in a Rust application | Bevy-free `hyper-viz` library and render-independent scene | +| Embed the native viewer in a Rust host | `hyper-viz-bevy` plugin and live scene channel | + +Inputs include scientific **HIF**, native **`hypergraph.v1`** JSON, and supported +legacy exports. The model is domain-agnostic: vertices, hyperedges, and memberships. + +## Python and scientific interoperability -## What this is not +Install the **`hypergraph-viz`** Python distribution with **CPython 3.10+**: -- **Not** Atomic Memory Core or any proprietary memory engine -- **Not** a claim / conversation / fact pipeline -- **Not** a packaged browser/WASM product (session JSON is portable; no web serve here) -- **Not** published to crates.io until an explicit public-release decision +```bash +python -m pip install hypergraph-viz +``` -See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the hard boundary. +The import name is `hyper_viz`. Wheels cover Linux x86_64, macOS arm64/x86_64, +and Windows x86_64; a compatible wheel requires no Rust compiler. Install the native desktop viewer through the optional extra: + +```bash +python -m pip install --upgrade "hypergraph-viz[viewer]" +``` + +Use `--upgrade` when the core package is already installed so pip selects the +release that provides the viewer extra. The viewer needs a desktop session and +graphics driver. Linux desktop wheels +require glibc 2.28+; macOS targets 11.0+. Rust is needed only for source builds. +For a Python development build from this checkout with **Rust 1.89+**: + +```bash +python -m venv .venv +. .venv/bin/activate # Windows: .venv\Scripts\activate +python -m pip install maturin==1.9.6 +cd crates/hyper-viz-python +maturin develop --locked +cd ../.. +``` + +```python +import hyper_viz + +document = hyper_viz.HifDocument.load("dataset.hif.json") +document.save("roundtrip.hif.json") +print(document.to_json()) + +# Install hypergraph-viz[viewer] first. +viewer = hyper_viz.show(document, projection="bipartite") +exit_code = viewer.wait() # or viewer.close() to terminate +``` + +Pass `executable="/path/to/hyper"` to select a viewer explicitly. Otherwise the +launcher finds the matching companion package, then `hyper` on PATH. The launcher +validates compatibility, starts the process asynchronously without a shell, and +owns a temporary native snapshot that is cleaned up after exit. The Python +extension itself does not depend on Bevy or download a viewer. + +Keep the `HifDocument` for scientific export: it preserves JSON values, record +order, metadata, integer/string ID identity, isolated nodes, empty edges, and +incidence properties. Whitespace and object-key order are not preserved. +Validation uses a bundled, pinned schema and works offline. + +**Document interchange supports more semantics than the viewer.** Viewer +conversion accepts undirected memberships and rejects directed or simplicial +semantics, incidence direction, weights, or attributes, and duplicate identities +or memberships. Node/edge weights round to finite `f32` for viewing; original +numbers remain in the document. Validation and compatibility exceptions expose +`location` and `reason`. Retained attributes are not automatically mapped to +viewer colors or exposed by the stock inspector. + +See [HIF and Python integration](docs/HIF.md) for API details, generated typing, +wheel builds, and runnable [XGI](crates/hyper-viz-python/examples/xgi_interop.py) +and [Julia](examples/hif_interop.jl) interchange examples. Direct scientific +object adapters, notebook embedding, layout bindings, and live updates from +Python are deferred. The [notebook prototype roadmap](docs/NOTEBOOK_ROADMAP.md) +sets out an anywidget canvas probe and Bevy/WASM feasibility checks. + +## Ecosystem fit and measured findings + +Hyper complements scientific analysis libraries and offers a Rust/native route +to exploring groups. The ecosystem comparison suggests this positioning; user +preference and adoption have not been measured. + +| Tool family | Fit alongside Hyper | +|---|---| +| XGI, HyperNetX, Hypergraphx | Scientific analysis and figures; exchange datasets through HIF where semantics are compatible | +| HyperGodot | Dedicated interactive 2D hypergraph exploration; an alternative when a 2D workflow fits | +| Sigma.js, Cytoscape.js, 3d-force-graph | Browser graph components; represent groups through incidence adapters and custom drawing | +| Graphia, Gephi | General graph analysis and attribute-driven exploration; useful references for richer inspection and filtering | + +The [October 3, 2026 benchmark](docs/benchmarks/ecosystem-2026-10-03/README.md) +records **445 measured attempts across 89 configurations** on one Apple M5 Pro +Mac. Scientific figures, browser rendering, native rendering, and import/save +workflows are separate tracks; these results do not establish an overall speed +ranking or native-versus-browser speed advantage. + +| Finding | Evidence and implication | +|---|---| +| Dense hulls are expensive | For 1,000 nodes / 2,000 groups in frozen bipartite mode, median per-run p95 application interval rose from **9.68 ms** without hulls to **62.08 ms** with hulls; both completed 5/5 attempts. Profile geometry and update costs before adding visual effects. | +| A successful demo is not a capacity guarantee | Full Mathlib bipartite runs completed 0/5 attempts within the 180-second budget in each frozen mode. Star mode with hulls and the default label policy completed 5/5, but frozen p95 was **219.91 ms**. Projection and settings change both work and responsiveness. | +| Failures need investigation | **16/120 native attempts** did not complete, including a small fixture. Completed-run timings exclude those failures; their cause remains unresolved. | +| Model fidelity must be checked | XGI preserved every tested model record. Our HNX adapter omitted isolated nodes and empty groups; HGX merged parallel groups by membership. Keep the original HIF document and validate adapter boundaries. | +| Smooth navigation does not guarantee fast updates | Sigma completed all 35 browser attempts, but its Mathlib update command took **13.24 s**. Update strategies differed across tools; evaluate updates separately from camera motion. | + +Application intervals and browser callback cadence are not GPU timings or +input-to-visible latency. Hulls approximate large groups, representations differ, +and display refresh was not locked across browser batches. Read the +[method and limitations](docs/benchmarks/ecosystem-2026-10-03/methodology.md), +[raw results](docs/benchmarks/ecosystem-2026-10-03/summary.csv), and +[rerun instructions](scripts/benchmarks/README.md) before interpreting timings. + +The next priorities are **attribute inspection and filtering**, **measured +responsiveness during dense geometry updates**, and **easier installation with +accurate documentation**. The [community and ecosystem research](docs/ecosystem-comparison.md) +explains the evidence behind those priorities. Browser distribution, directed +rendering, and timeline views remain outside the current viewer scope. ## Docs for external readers @@ -36,6 +144,11 @@ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the hard boundary. | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | Package boundary, host coupling policy | | [docs/API.md](docs/API.md) | Library API overview | | [docs/VIEWER.md](docs/VIEWER.md) | How to run the viewer + example scene | +| [docs/HIF.md](docs/HIF.md) | Scientific interchange, Python API, supported semantics, and packaging | +| [Measured ecosystem comparison](docs/benchmarks/ecosystem-2026-10-03/README.md) | Results, completion counts, raw evidence, and limitations | +| [Community and ecosystem research](docs/ecosystem-comparison.md) | Positioning and roadmap evidence | +| [Notebook prototype roadmap](docs/NOTEBOOK_ROADMAP.md) | Small-dataset anywidget plan and WASM feasibility checks | +| [Python release guide](docs/PYTHON_RELEASE.md) | PyPI Trusted Publisher setup and tag-based releases | | [docs/PUBLIC_RELEASE_CHECKLIST.md](docs/PUBLIC_RELEASE_CHECKLIST.md) | Maintainer release-decision checklist | | [CONTRIBUTING.md](CONTRIBUTING.md) | Dev setup and PR norms | @@ -75,7 +188,7 @@ let mut layout = ForceLayout3D::from_scene(&scene, LayoutConfig::default()); layout.step(); ``` -Load JSON (`hypergraph.v1`): +Load JSON (automatic HIF, native, or supported legacy format detection): ```rust use hyper_viz::{from_json_str, load_json}; @@ -97,8 +210,9 @@ run_visualizer(scene); // already projected run_visualizer_live(scene, rx, None); // host thread pushes scenes ``` -A new scene on the live channel (or a file-watch reload) rebuilds the layout -and **clears selection**. +A new scene on the live channel (or a file-watch reload) rebuilds the scene +while retaining positions by stable ID and remapping surviving selection and +focus. Invalid watched input leaves the previous scene visible. ### Embed in a Bevy app @@ -119,7 +233,9 @@ the Bevy app. ## Crate map ```text -Hypergraph JSON (or builder API) +HIF JSON → HifDocument → compatibility check +Native JSON / Rust builder API + → Hypergraph → project(Bipartite | Clique | Star) → HypergraphScene # render-agnostic IR → ForceLayout3D # Barnes-Hut octree, CPU @@ -128,8 +244,9 @@ Hypergraph JSON (or builder API) | Crate | Role | Depend on this? | |---|---|---| -| [`crates/hyper-viz`](crates/hyper-viz) | Scene IR, projections, layout, hulls, JSON I/O | **Yes** — library | +| [`crates/hyper-viz`](crates/hyper-viz) | HIF, scene IR, projections, layout, hulls, JSON I/O | **Yes** — library | | [`crates/hyper-viz-bevy`](crates/hyper-viz-bevy) | Bevy 0.18 + egui native viewer | Only if you want the window | +| [`crates/hyper-viz-python`](crates/hyper-viz-python) | Typed Python HIF bindings and viewer launcher; no Bevy | For Python workflows | | `hyper` (this binary) | CLI: load JSON or open the built-in demo | No — not a library | ## Quick start (CLI) @@ -141,6 +258,9 @@ cargo run # Example scene cargo run -- fixtures/sample.json +# Scientific HIF dataset (also auto-detected without --format) +cargo run -- dataset.hif.json --format hif + # Hot-reload while you edit the file cargo run -- --watch fixtures/sample.json @@ -151,8 +271,10 @@ cargo run -- --projection clique fixtures/sample.json cargo run -p hyper-viz --example project_scene ``` -First Bevy compile is slow (~40s cold). Subsequent runs are fast. Native window -only — this is not a web app. More detail: [docs/VIEWER.md](docs/VIEWER.md). +Requires Rust 1.89+. The first Bevy build can take several minutes, depending +on the machine. For regular use, build with `cargo build --release --bin hyper` +or install with `cargo install --path . --locked`. See [docs/VIEWER.md](docs/VIEWER.md) +for native viewer setup. ## JSON schema (`hypergraph.v1`) @@ -161,7 +283,9 @@ only — this is not a web app. More detail: [docs/VIEWER.md](docs/VIEWER.md). "version": "hypergraph.v1", "meta": { "id": "g1", "title": "Coauthorship" }, "vertices": [ - { "id": "alice", "label": "Alice", "kind": "person" } + { "id": "alice", "label": "Alice", "kind": "person" }, + { "id": "bob", "label": "Bob", "kind": "person" }, + { "id": "carol", "label": "Carol", "kind": "person" } ], "hyperedges": [ { "id": "paper-a", "label": "Paper A", "vertices": ["alice", "bob", "carol"] } @@ -189,7 +313,11 @@ viewer hides those hubs by default so the hyperedge is read as a **set**: | 2 | Colored line between the two members | | 3 | Triangle hull | | 4 | Tetrahedron hull | -| 5+ | Convex hull of members | +| 5+ | Convex hull, using at most 24 sampled members | + +Large-group hulls are approximate: the geometry cap does not remove memberships +from the graph, and a hull is not an exact membership boundary. Empty edges have +no drawable geometry. Each hyperedge keeps a **stable hue** from its id (hash), so nested sets stay distinguishable. Nested hulls are inflated slightly to avoid z-fighting. @@ -290,7 +418,9 @@ Dual-licensed under **MIT OR Apache-2.0**. See [LICENSE](LICENSE), ```text crates/hyper-viz/ # library other projects depend on crates/hyper-viz-bevy/ # optional Bevy renderer -docs/ # architecture, API, viewer, release checklist +crates/hyper-viz-python/ # Python extension, typed package, and launcher +scripts/benchmarks/ # opt-in ecosystem benchmark harness +docs/ # API, interoperability, ecosystem research, benchmark evidence fixtures/sample.json # coauthorship demo src/main.rs # CLI ``` diff --git a/crates/hyper-viz-bevy/Cargo.toml b/crates/hyper-viz-bevy/Cargo.toml index 970cf25..c88cc41 100644 --- a/crates/hyper-viz-bevy/Cargo.toml +++ b/crates/hyper-viz-bevy/Cargo.toml @@ -22,13 +22,27 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] +serde_json = { workspace = true, optional = true } hyper-viz = { workspace = true } -bevy = { version = "0.18", default-features = true, features = ["wayland"] } +# Preserve Bevy's visual defaults without unused audio/gamepad native libraries. +bevy = { version = "0.18", default-features = false, features = [ + "default_app", "2d_bevy_render", "3d_bevy_render", "ui_bevy_render", "scene", "picking", + "std", "android-game-activity", "android_shared_stdcxx", "bevy_winit", + "default_font", "multi_threaded", "webgl2", "x11", "wayland", "sysinfo_plugin", +] } bevy_egui = "0.39" bevy_panorbit_camera = { version = "0.34", features = ["bevy_egui"] } winit = "0.30" tracing = { workspace = true } +[features] +benchmark = ["dep:serde_json"] + +[[bin]] +name = "ecosystem-benchmark" +path = "src/bin/ecosystem_benchmark.rs" +required-features = ["benchmark"] + [target.'cfg(target_arch = "wasm32")'.dependencies] web-sys = { version = "0.3", features = ["Window", "Storage"] } diff --git a/crates/hyper-viz-bevy/src/benchmark.rs b/crates/hyper-viz-bevy/src/benchmark.rs new file mode 100644 index 0000000..f4d62f3 --- /dev/null +++ b/crates/hyper-viz-bevy/src/benchmark.rs @@ -0,0 +1,295 @@ +//! Opt-in finite benchmark of the production renderer, with picking and HUD. +use crate::{ + graph::{GraphLayout, GraphSceneEpoch, LayoutSettings}, + hyperedge_hull::HyperedgeHullSettings, + node_visual::NodeRenderSettings, + session::{SessionStore, SkipAutoFit}, +}; +use bevy::prelude::*; +use bevy_panorbit_camera::PanOrbitCamera; +use hyper_viz::{Hypergraph, HypergraphScene, NodeRole, Projection}; +use serde_json::{Value, json}; +use std::{collections::HashMap, path::PathBuf, time::Instant}; + +#[derive(Resource)] +struct Run { + graph: Hypergraph, + updated: HypergraphScene, + frame: usize, + warmup: usize, + frames: usize, + hulls: bool, + live: bool, + report: PathBuf, + last: Instant, + intervals: Vec, + updates: Vec, + layout_ms: Vec, + parse_ms: f64, + projection_ms: f64, + completed: bool, + capture: Option, + capture_pending: bool, + projection: String, + labels: bool, + expected_iterations: u64, +} + +/// Arguments: native JSON, output JSON, hulls, live, frames, capture, projection, labels. +pub fn run() { + let args: Vec = std::env::args().collect(); + assert!( + args.len() >= 5, + "usage: ecosystem-benchmark INPUT OUTPUT HULLS LIVE [FRAMES] [CAPTURE|-] [PROJECTION] [LABELS]" + ); + let raw = std::fs::read_to_string(&args[1]).expect("read dataset"); + let start = Instant::now(); + let graph = hyper_viz::from_json_str(&raw).expect("load canonical native dataset"); + let parse_ms = start.elapsed().as_secs_f64() * 1000.; + let start = Instant::now(); + let projection_name = args.get(7).map(String::as_str).unwrap_or("bipartite"); + let projection = Projection::parse(projection_name).expect("supported projection"); + let scene = graph.project(projection); + let projection_ms = start.elapsed().as_secs_f64() * 1000.; + let updated_path = args[1].strip_suffix(".json").unwrap().to_string() + ".updated.json"; + let updated = hyper_viz::load_json(updated_path) + .expect("shared update fixture") + .project(projection); + let mut app = crate::visualizer_app_with( + crate::HyperVisualizerPlugin::from_scene(scene).with_projection(projection), + crate::VisualizerConfig::new("Hyper ecosystem benchmark").with_size(1920, 1080), + false, + ); + app.world_mut().resource_mut::().enabled = false; + app.world_mut().resource_mut::().0 = true; + for mut window in app + .world_mut() + .query::<&mut Window>() + .iter_mut(app.world_mut()) + { + window.resolution = + bevy::window::WindowResolution::new(1920, 1080).with_scale_factor_override(1.0); + window.present_mode = bevy::window::PresentMode::AutoVsync; + } + app.insert_resource(bevy::winit::WinitSettings::continuous()) + .insert_resource(crate::LinkRenderSettings { + max_lines: 0, + opacity: 0.10, + }) + .insert_resource(Run { + graph, + updated, + frame: 0, + warmup: 120, + frames: args.get(5).map(|v| v.parse().unwrap()).unwrap_or(300), + hulls: args[3] == "1", + live: args[4] == "1", + report: (&args[2]).into(), + last: Instant::now(), + intervals: vec![], + updates: vec![], + layout_ms: vec![], + parse_ms, + projection_ms, + completed: false, + capture: args.get(6).filter(|p| p.as_str() != "-").map(PathBuf::from), + capture_pending: false, + projection: projection_name.into(), + labels: args.get(8).is_some_and(|v| v == "1"), + expected_iterations: 0, + }) + .add_systems(PostStartup, prepare) + .add_systems(PreUpdate, tick) + .add_systems( + Update, + disable_builtin_steps.before(crate::graph::step_layout), + ) + .add_systems(Last, finish) + .run(); +} + +fn prepare( + mut state: ResMut, + mut layout: ResMut, + mut settings: ResMut, + mut hulls: ResMut, + mut nodes: ResMut, + mut cameras: Query<&mut PanOrbitCamera>, +) { + let positions: HashMap<_, _> = state + .graph + .vertices + .iter() + .map(|v| { + let p = v.attrs["position"].as_array().unwrap(); + ( + v.id.as_str(), + hyper_viz::Vec3::new( + p[0].as_f64().unwrap() as f32, + p[1].as_f64().unwrap() as f32, + p[2].as_f64().unwrap() as f32, + ), + ) + }) + .collect(); + let edges: HashMap<_, _> = state + .graph + .hyperedges + .iter() + .map(|e| (e.id.as_str(), e)) + .collect(); + for node in layout.scene.nodes.clone() { + let p = if node.role == NodeRole::Vertex { + positions[node.id.as_str()] + } else { + let e = edges[node.hyperedge_id.as_deref().unwrap()]; + let mut p = hyper_viz::Vec3::ZERO; + for v in &e.vertices { + p += positions[v.as_str()]; + } + if !e.vertices.is_empty() { + p = p / e.vertices.len() as f32; + } + p + }; + layout.layout.positions[node.index] = p; + } + // Keep production live hull skinning/topology refresh enabled, while the + // timed step replaces the built-in solver calls. + layout.running = state.live; + layout.iterations_per_frame = 0; + settings.iterations_per_frame = 0; + settings.node_size = 2.; + hulls.enabled = state.hulls; + hulls.wireframe = false; + hulls.hide_hubs = state.projection == "star"; + nodes.labels_enabled = state.labels; + nodes.hyperedge_labels = state.labels; + for mut camera in &mut cameras { + camera.enabled = false; + } + state.last = Instant::now(); +} + +fn disable_builtin_steps( + state: Res, + mut layout: ResMut, + mut settings: ResMut, +) { + // The HUD slider clamps its displayed zero to one. Reset before the stock + // stepping system, without disabling the HUD or live geometry systems. + layout.iterations_per_frame = 0; + layout.running = state.live; + settings.iterations_per_frame = 0; +} + +fn tick( + mut state: ResMut, + mut layout: ResMut, + settings: Res, + mut epoch: ResMut, + mut cameras: Query<&mut Transform, With>, +) { + if state.completed { + return; + } + assert_eq!( + layout.layout.iterations, state.expected_iterations, + "unexpected untimed solver step" + ); + let now = Instant::now(); + let elapsed = now.duration_since(state.last).as_secs_f64() * 1000.; + state.last = now; + if state.frame > state.warmup { + state.intervals.push(elapsed); + } + if state.live { + let start = Instant::now(); + layout.layout.step(); + state.expected_iterations += 1; + if state.frame >= state.warmup { + state.layout_ms.push(start.elapsed().as_secs_f64() * 1000.); + } + } + // Scripted orbit keeps every frame dirty. This is not hardware input latency. + let angle = state.frame as f32 * 0.003; + for mut camera in &mut cameras { + *camera = Transform::from_xyz(angle.sin() * 1500., 350., angle.cos() * 1500.) + .looking_at(Vec3::ZERO, Vec3::Y); + } + if state.frame == state.warmup + state.frames { + let start = Instant::now(); + *layout = GraphLayout::from_scene_preserve(&layout, state.updated.clone(), &settings); + state.expected_iterations = 0; + // Match the shared updated hub centroids; stable vertex positions survive. + let hubs: Vec<_> = layout + .scene + .hyperedges + .iter() + .filter(|e| layout.scene.nodes[e.hub_index].role == NodeRole::HyperedgeHub) + .map(|e| { + let mut p = hyper_viz::Vec3::ZERO; + for &i in &e.member_indices { + p += layout.layout.positions[i]; + } + if !e.member_indices.is_empty() { + p = p / e.member_indices.len() as f32; + } + (e.hub_index, p) + }) + .collect(); + for (index, position) in hubs { + layout.layout.positions[index] = position; + } + epoch.0 += 1; + state.updates.push(start.elapsed().as_secs_f64() * 1000.); + } + state.frame += 1; +} + +fn finish( + mut commands: Commands, + mut state: ResMut, + layout: Res, + mut exit: MessageWriter, +) { + if state.completed || state.frame <= state.warmup + state.frames + 3 { + return; + } + if state.capture_pending { + return; + } + if let Some(path) = state.capture.take() { + state.capture_pending = true; + commands + .spawn(bevy::render::view::screenshot::Screenshot::primary_window()) + .observe( + move |event: On, + mut run: ResMut| { + event + .image + .clone() + .try_into_dynamic() + .unwrap() + .to_rgb8() + .save(&path) + .unwrap(); + run.capture_pending = false; + }, + ); + return; + } + state.completed = true; + assert_eq!( + layout.layout.iterations, state.expected_iterations, + "unexpected untimed solver step" + ); + let intervals: Vec<_> = state.intervals.iter().copied().take(state.frames).collect(); + let result: Value = json!({"tool":"hyper", "dataset":state.graph.meta.id, "profile":"release", "projection":state.projection, "window":[1920,1080], "dpr":1, "present_mode":"AutoVsync", "hulls":state.hulls,"live_layout":state.live,"hud":true,"picking":true,"labels":state.labels,"label_policy":"default Capped; count threshold250; hovered/selected exemptions","line_opacity":0.10,"node_size":2,"hull_opacity":0.28,"hull_wireframe":false,"hull_vertex_cap":hyper_viz::MAX_HULL_VERTICES,"hull_topology_interval":8,"live_solver_steps_per_frame":if state.live {1} else {0},"line_budget":0,"warmup_frames":state.warmup,"frames":state.frames,"frame_interval_ms":intervals,"layout_step_ms":state.layout_ms,"update_commit_ms":state.updates,"update_next_frame_ms":state.intervals.get(state.frames),"native_parse_ms":state.parse_ms,"projection_ms":state.projection_ms,"nodes":layout.node_count,"scene_links":layout.link_count,"rendered_lines":if state.projection == "star" { state.graph.hyperedges.iter().filter(|e| e.vertices.len()==2).count() } else { layout.link_count },"measurement":"CPU wall intervals; no GPU timer, hardware input or presentation timestamp"}); + if let Some(parent) = state.report.parent() { + std::fs::create_dir_all(parent).unwrap(); + } + std::fs::write(&state.report, serde_json::to_vec_pretty(&result).unwrap()) + .expect("write measurements"); + exit.write(AppExit::Success); +} diff --git a/crates/hyper-viz-bevy/src/bin/ecosystem_benchmark.rs b/crates/hyper-viz-bevy/src/bin/ecosystem_benchmark.rs new file mode 100644 index 0000000..7c810de --- /dev/null +++ b/crates/hyper-viz-bevy/src/bin/ecosystem_benchmark.rs @@ -0,0 +1,3 @@ +fn main() { + hyper_viz_bevy::benchmark::run(); +} diff --git a/crates/hyper-viz-bevy/src/graph.rs b/crates/hyper-viz-bevy/src/graph.rs index af14b1d..f224e17 100644 --- a/crates/hyper-viz-bevy/src/graph.rs +++ b/crates/hyper-viz-bevy/src/graph.rs @@ -3,7 +3,8 @@ use std::time::SystemTime; use bevy::prelude::*; use hyper_viz::{ - ForceLayout3D, HypergraphScene, LayoutConfig, Projection, load_json, project, scenes_equivalent, + ForceLayout3D, HypergraphScene, InputFormat, LayoutConfig, Projection, load_json_with_format, + project, scenes_equivalent, }; #[derive(Resource)] @@ -47,6 +48,7 @@ pub struct LayoutSettings { pub graph_path: Option, pub watch: bool, pub projection: Projection, + pub input_format: InputFormat, } /// Published with each replacement scene so renderers cannot consume a stale layout. @@ -72,6 +74,7 @@ impl Default for LayoutSettings { graph_path: None, watch: false, projection: Projection::Bipartite, + input_format: InputFormat::Auto, } } } @@ -80,7 +83,7 @@ fn layout_from_path( path: &str, settings: &LayoutSettings, ) -> Result { - let graph = load_json(path)?; + let graph = load_json_with_format(path, settings.input_format)?; let scene = project(&graph, settings.projection); Ok(GraphLayout::from_scene(scene, settings)) } @@ -205,8 +208,12 @@ pub fn poll_graph_watch( return; } - let Ok(graph) = load_json(&watch.path) else { - return; + let graph = match load_json_with_format(&watch.path, settings.input_format) { + Ok(graph) => graph, + Err(error) => { + tracing::warn!(path = %watch.path, %error, "retaining scene after failed reload"); + return; + } }; let scene = project(&graph, settings.projection); @@ -336,6 +343,31 @@ mod watch_tests { (tmp, app) } + #[test] + fn hif_watch_obeys_explicit_format_and_keeps_scene_on_incompatible_update() { + let (tmp, mut app) = fixture(); + let path = tmp.path().join("graph.json"); + std::fs::write(&path, r#"{"incidences":[{"node":"n","edge":"e"}]}"#).unwrap(); + app.world_mut() + .resource_mut::() + .input_format = hyper_viz::InputFormat::Hif; + app.world_mut().resource_mut::().last_mtime = None; + app.update(); + assert_eq!( + app.world().resource::().scene.vertices_count(), + 1 + ); + assert_eq!(app.world().resource::().0, 1); + std::fs::write(&path, r#"{"network-type":"directed","incidences":[]}"#).unwrap(); + app.world_mut().resource_mut::().last_mtime = None; + app.update(); + assert_eq!(app.world().resource::().0, 1); + assert_eq!( + app.world().resource::().scene.vertices_count(), + 1 + ); + } + #[test] fn unchanged_file_stamp_does_not_replace_the_live_scene() { let (_tmp, mut app) = fixture(); diff --git a/crates/hyper-viz-bevy/src/lib.rs b/crates/hyper-viz-bevy/src/lib.rs index 1f5e6c3..bf48237 100644 --- a/crates/hyper-viz-bevy/src/lib.rs +++ b/crates/hyper-viz-bevy/src/lib.rs @@ -46,6 +46,8 @@ mod render; mod search; mod session; pub use search::{SearchPage, SearchProvider, SearchRow}; +#[cfg(feature = "benchmark")] +pub mod benchmark; pub mod showcase; mod ui; @@ -130,7 +132,7 @@ impl HyperVisualizerPlugin { } } - /// Load `hypergraph.v1` JSON from `path`. Set `watch` to reload on file change. + /// Load native or HIF JSON from `path`. Set `watch` to reload on file change. pub fn from_path(path: impl Into, watch: bool) -> Self { let path = path.into(); Self { @@ -147,6 +149,12 @@ impl HyperVisualizerPlugin { } } + /// Select the format for initial loading and every watched reload. + pub fn with_input_format(mut self, format: hyper_viz::InputFormat) -> Self { + self.settings.input_format = format; + self + } + pub fn with_projection(mut self, projection: Projection) -> Self { self.settings.projection = projection; self @@ -251,9 +259,28 @@ pub fn run_visualizer_from_path_with( watch: bool, projection: Projection, config: VisualizerConfig, +) { + run_visualizer_from_path_with_format( + path, + watch, + projection, + config, + hyper_viz::InputFormat::Auto, + ); +} + +/// File viewer with an explicit interchange format, including watched updates. +pub fn run_visualizer_from_path_with_format( + path: String, + watch: bool, + projection: Projection, + config: VisualizerConfig, + format: hyper_viz::InputFormat, ) { visualizer_app_with( - HyperVisualizerPlugin::from_path(path, watch).with_projection(projection), + HyperVisualizerPlugin::from_path(path, watch) + .with_projection(projection) + .with_input_format(format), config, false, ) diff --git a/crates/hyper-viz-python/Cargo.toml b/crates/hyper-viz-python/Cargo.toml new file mode 100644 index 0000000..da9fa6e --- /dev/null +++ b/crates/hyper-viz-python/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "hyper-viz-python" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +publish = false + +[lib] +name = "hyper_viz_python" +crate-type = ["cdylib", "rlib"] + +[dependencies] +hyper-viz.workspace = true +serde_json.workspace = true +pyo3 = { version = "=0.27.2", features = ["abi3-py310"] } +pyo3-stub-gen = { version = "=0.19.0", default-features = false } + +[[bin]] +name = "stub_gen" +path = "src/bin/stub_gen.rs" diff --git a/crates/hyper-viz-python/README.md b/crates/hyper-viz-python/README.md new file mode 100644 index 0000000..fab45ad --- /dev/null +++ b/crates/hyper-viz-python/README.md @@ -0,0 +1,86 @@ +# Hyper for Python + +`hypergraph-viz` provides Rust-backed [Hypergraph Interchange Format (HIF)](https://github.com/HIF-org/HIF-standard) +validation and interchange, plus a launcher for Hyper's native 3D viewer. Use it +to exchange scientific datasets while retaining metadata and explicit group identities. +The package includes generated extension type stubs, typed Python wrappers, and `py.typed`. + +## Installation + +```sh +python -m pip install hypergraph-viz +``` + +Requires standard CPython 3.10 or newer. Platform wheels cover Linux x86_64, +macOS arm64/x86_64, and Windows x86_64; installing a compatible wheel requires +no Rust compiler. Building from source requires Rust 1.89 or newer and maturin. +The Python extension has no Bevy dependency. + +## Exchange datasets + +```python +import hyper_viz + +document = hyper_viz.HifDocument.load("dataset.hif.json") +document.save("roundtrip.hif.json") +print(document.to_json()) + +# Explicitly check compatibility and convert to Hyper's native JSON. +native_json = document.to_hypergraph_json() +``` + +`HifDocument.from_json(raw)` accepts HIF JSON; `from_hypergraph_json(raw)` accepts +native Hyper JSON. Validation uses a pinned schema bundled with the package and +never fetches resources at runtime. + +Keep the original document for scientific export. It preserves JSON values, +record order, optional field presence, integer/string IDs, isolated nodes, empty +edges, metadata, and incidence properties. Whitespace and object-key order are +not preserved. + +## Launch the native viewer + +Install the matching prebuilt desktop viewer with the optional extra: + +```sh +python -m pip install --upgrade "hypergraph-viz[viewer]" +``` + +Wheels cover Linux x86_64, macOS arm64/x86_64, and Windows x86_64. No Rust +compiler or repository checkout is needed on these platforms. A desktop session +and graphics driver are required. Linux viewer wheels require glibc 2.28+; +macOS wheels target 11.0+. Notebook kernels on remote machines cannot open a +window on your laptop; see the +[notebook prototype roadmap](https://github.com/atomicstrata/hyper/blob/python-v0.1.2/docs/NOTEBOOK_ROADMAP.md). + +Then launch it from Python: + +```python +viewer = hyper_viz.show(document, projection="bipartite") +exit_code = viewer.wait() +# Or viewer.close() to terminate and clean up. +``` + +Pass `executable="/path/to/hyper"` to select a binary explicitly; otherwise the +launcher finds the version-matched installed companion, then falls back to +`hyper` on PATH. It locates the companion through installed-package metadata, +so activating a virtual environment is not required. It launches asynchronously without a shell, +retains an owned temporary snapshot, and cleans up after exit. It does not +download a viewer. + +## Compatibility and errors + +Full-document interchange supports more semantics than viewer conversion. +The viewer supports undirected memberships. Directed and simplicial semantics, +incidence direction/weights/attributes, and duplicate declarations or incidence +pairs produce `HifCompatibilityError`. Node/edge weights round to finite `f32` +for viewing; original values remain in the document. Arbitrary attributes are +retained but not exposed by the stock viewer inspector. + +Malformed HIF raises `HifValidationError`. Both exceptions carry `location` +and `reason`; file failures raise `OSError`. Direct scientific object adapters, +notebook embedding, headless layout bindings, and live Python updates are deferred. + +See the [integration guide](https://github.com/atomicstrata/hyper/blob/python-v0.1.2/docs/HIF.md) +and [XGI example](https://github.com/atomicstrata/hyper/blob/python-v0.1.2/crates/hyper-viz-python/examples/xgi_interop.py). +Hyper is licensed under MIT OR Apache-2.0; the bundled HIF schema is MIT-licensed. diff --git a/crates/hyper-viz-python/examples/xgi_interop.py b/crates/hyper-viz-python/examples/xgi_interop.py new file mode 100644 index 0000000..647cf62 --- /dev/null +++ b/crates/hyper-viz-python/examples/xgi_interop.py @@ -0,0 +1,27 @@ +"""Run with XGI 0.10.2 and the built hyper_viz package.""" +import argparse +import tempfile +from pathlib import Path + +import xgi +import hyper_viz + +parser = argparse.ArgumentParser() +parser.add_argument('--viewer', help='Optional path to the installed native Hyper viewer') +args = parser.parse_args() +graph = xgi.Hypergraph() +graph.add_nodes_from(['a', 'b', 'isolated']) +graph.add_edge(['a', 'b'], idx='research') +graph.nodes['a']['source'] = 'experiment' +with tempfile.TemporaryDirectory() as directory: + incoming = Path(directory) / 'xgi.json' + outgoing = Path(directory) / 'roundtrip.json' + xgi.write_hif(graph, str(incoming)) + document = hyper_viz.HifDocument.load(str(incoming)) + document.save(str(outgoing)) + restored = xgi.read_hif(str(outgoing)) + print(f'Preserved {len(restored.nodes)} nodes and {len(restored.edges)} edges') + if args.viewer: + # Some exporters include incidence properties; conversion then raises + # a compatibility error instead of silently dropping scientific data. + hyper_viz.show(document, executable=args.viewer).wait() diff --git a/crates/hyper-viz-python/pyproject.toml b/crates/hyper-viz-python/pyproject.toml new file mode 100644 index 0000000..d013ce0 --- /dev/null +++ b/crates/hyper-viz-python/pyproject.toml @@ -0,0 +1,37 @@ +[build-system] +requires = ["maturin==1.9.6"] +build-backend = "maturin" + +[project] +name = "hypergraph-viz" +version = "0.1.2" +requires-python = ">=3.10" +description = "Rust-backed HIF interchange and native Hyper viewer launcher" +readme = "README.md" +license = "MIT OR Apache-2.0" +keywords = ["hypergraph", "hif", "visualization", "scientific-computing", "rust"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Rust", + "Topic :: Scientific/Engineering :: Visualization", + "Typing :: Typed", +] + +[project.optional-dependencies] +viewer = ["hypergraph-viz-viewer==0.1.2"] + +[project.urls] +Homepage = "https://github.com/atomicstrata/hyper" +Documentation = "https://github.com/atomicstrata/hyper/blob/python-v0.1.2/docs/HIF.md" +Repository = "https://github.com/atomicstrata/hyper" +Issues = "https://github.com/atomicstrata/hyper/issues" + +[tool.maturin] +module-name = "hyper_viz._core" +python-source = "python" +features = ["pyo3/extension-module"] +include = ["python/hyper_viz/HIF-LICENSE"] +exclude = ["**/__pycache__/**", "**/*.pyc"] diff --git a/crates/hyper-viz-python/python/hyper_viz/HIF-LICENSE b/crates/hyper-viz-python/python/hyper_viz/HIF-LICENSE new file mode 100644 index 0000000..5997fb9 --- /dev/null +++ b/crates/hyper-viz-python/python/hyper_viz/HIF-LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024-2025 HIF development team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/hyper-viz-python/python/hyper_viz/LICENSE-APACHE b/crates/hyper-viz-python/python/hyper_viz/LICENSE-APACHE new file mode 100644 index 0000000..a066530 --- /dev/null +++ b/crates/hyper-viz-python/python/hyper_viz/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 Bregy Malpartida / AtomicStrata + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/crates/hyper-viz-python/python/hyper_viz/LICENSE-MIT b/crates/hyper-viz-python/python/hyper_viz/LICENSE-MIT new file mode 100644 index 0000000..305152e --- /dev/null +++ b/crates/hyper-viz-python/python/hyper_viz/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Bregy Malpartida / AtomicStrata + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/hyper-viz-python/python/hyper_viz/__init__.py b/crates/hyper-viz-python/python/hyper_viz/__init__.py new file mode 100644 index 0000000..5355627 --- /dev/null +++ b/crates/hyper-viz-python/python/hyper_viz/__init__.py @@ -0,0 +1,8 @@ +"""HIF interchange in Rust and an optional native Hyper viewer.""" +from ._core import HifDocument as HifDocument +from ._errors import HifCompatibilityError as HifCompatibilityError +from ._errors import HifValidationError as HifValidationError +from ._viewer import ViewerHandle as ViewerHandle +from ._viewer import show as show + +__all__ = ["HifDocument", "HifValidationError", "HifCompatibilityError", "ViewerHandle", "show"] diff --git a/crates/hyper-viz-python/python/hyper_viz/_core.pyi b/crates/hyper-viz-python/python/hyper_viz/_core.pyi new file mode 100644 index 0000000..528d02f --- /dev/null +++ b/crates/hyper-viz-python/python/hyper_viz/_core.pyi @@ -0,0 +1,38 @@ +# This file is automatically generated by pyo3_stub_gen +# ruff: noqa: E501, F401, F403, F405 + +import builtins +import typing +__all__ = [ + "HifDocument", +] + +@typing.final +class HifDocument: + @staticmethod + def from_json(raw: builtins.str) -> HifDocument: + r""" + Parse and validate HIF without requiring viewer compatibility. + """ + @staticmethod + def load(path: builtins.str) -> HifDocument: + r""" + Load HIF offline from a UTF-8 JSON file. + """ + @staticmethod + def from_hypergraph_json(raw: builtins.str) -> HifDocument: + r""" + Export native hypergraph.v1 JSON to a new HIF document. + """ + def to_json(self) -> builtins.str: + r""" + Serialize all HIF properties, including unsupported viewer semantics. + """ + def save(self, path: builtins.str) -> None: + r""" + Save all HIF properties as UTF-8 JSON. + """ + def to_hypergraph_json(self) -> builtins.str: + r""" + Convert to viewer JSON, rejecting unsupported semantics and weight overflow. + """ diff --git a/crates/hyper-viz-python/python/hyper_viz/_errors.py b/crates/hyper-viz-python/python/hyper_viz/_errors.py new file mode 100644 index 0000000..97d6976 --- /dev/null +++ b/crates/hyper-viz-python/python/hyper_viz/_errors.py @@ -0,0 +1,11 @@ +"""Native exceptions expose JSON Pointer locations and a reason.""" +from . import _core + + +class _LocatedError(Exception): + location: str + reason: str + + +HifValidationError: type[_LocatedError] = getattr(_core, "HifValidationError") +HifCompatibilityError: type[_LocatedError] = getattr(_core, "HifCompatibilityError") diff --git a/crates/hyper-viz-python/python/hyper_viz/_viewer.py b/crates/hyper-viz-python/python/hyper_viz/_viewer.py new file mode 100644 index 0000000..8f56977 --- /dev/null +++ b/crates/hyper-viz-python/python/hyper_viz/_viewer.py @@ -0,0 +1,138 @@ +"""A separately installed viewer owns its window and event loop.""" +from __future__ import annotations + +import os +from importlib.metadata import PackageNotFoundError, distribution, version +from pathlib import Path +import shutil +import subprocess +import tempfile +import threading +from typing import Literal + +from ._core import HifDocument + +Projection = Literal["bipartite", "clique", "star"] + + +class ViewerHandle: + """Own a viewer process and snapshot; dropping the handle leaves the viewer open. + + A background waiter removes the snapshot after process exit. Use close() or + a context manager to explicitly terminate the viewer. + """ + + def __init__(self, process: subprocess.Popen[bytes], snapshot: tempfile.TemporaryDirectory[str]) -> None: + self._process = process + self._snapshot = snapshot + self._lock = threading.Lock() + self._waiter = threading.Thread(target=self._reap, daemon=True) + self._waiter.start() + + def _cleanup(self) -> None: + with self._lock: + self._snapshot.cleanup() + + def _reap(self) -> None: + self._process.wait() + self._cleanup() + + def poll(self) -> int | None: + result = self._process.poll() + if result is not None: + self._cleanup() + return result + + def wait(self, timeout: float | None = None) -> int: + """Return the exit code; timeout leaves the viewer and its snapshot intact.""" + result = self._process.wait(timeout=timeout) + self._cleanup() + return result + + def close(self) -> None: + """Terminate, then kill after five seconds if necessary; safe to repeat.""" + if self._process.poll() is None: + try: + self._process.terminate() + except ProcessLookupError: + pass + try: + self.wait(timeout=5) + except subprocess.TimeoutExpired: + self._process.kill() + self.wait() + + def __enter__(self) -> ViewerHandle: + return self + + def __exit__(self, *_: object) -> None: + self.close() + + +def _resolve_executable(executable: str | os.PathLike[str] | None) -> str: + guidance = 'python -m pip install --upgrade "hypergraph-viz[viewer]"' + if executable is not None: + candidate = os.fspath(executable) + resolved = shutil.which(candidate) + if resolved is None: + raise FileNotFoundError(f"Cannot find executable {candidate!r}; install it or select an existing path") + return resolved + + try: + companion = distribution("hypergraph-viz-viewer") + except PackageNotFoundError: + companion = None + if companion is not None: + core_version = version("hypergraph-viz") + if companion.version != core_version: + raise RuntimeError( + f"Installed viewer {companion.version} does not match hypergraph-viz " + f"{core_version}; install matching packages with {guidance}" + ) + # pip rewrites the wheel's script RECORD to its actual installed path. + # Using it avoids assuming that the environment's Scripts/bin is on PATH. + binaries = [ + Path(str(companion.locate_file(record))).resolve() + for record in companion.files or [] + if record.name in ("hyper", "hyper.exe") + ] + if len(binaries) > 1: + raise RuntimeError(f"Installed viewer contains multiple executables; reinstall with {guidance}") + if not binaries or not binaries[0].is_file() or not os.access(binaries[0], os.X_OK): + raise FileNotFoundError(f"Installed viewer executable is missing or unusable; reinstall with {guidance}") + return str(binaries[0]) + + resolved = shutil.which("hyper") + if resolved is None: + raise FileNotFoundError( + f"Cannot find the native viewer; install it with {guidance}, " + "pass executable='/path/to/hyper', or put hyper on PATH" + ) + return resolved + + +def show( + document: HifDocument, + *, + executable: str | os.PathLike[str] | None = None, + projection: Projection = "bipartite", +) -> ViewerHandle: + """Validate, snapshot, and launch Hyper asynchronously without shell execution. + + Install hypergraph-viz[viewer], put a native binary on PATH, or pass its path. + No executable is downloaded automatically. Nonzero exit codes are returned + by wait(); inherited stdout/stderr retain the viewer's diagnostics. + """ + if projection not in ("bipartite", "clique", "star"): + raise ValueError("projection must be bipartite, clique, or star") + raw = document.to_hypergraph_json() + resolved = _resolve_executable(executable) + snapshot = tempfile.TemporaryDirectory(prefix="hyper-viz-") + try: + path = Path(snapshot.name) / "graph.json" + path.write_text(raw, encoding="utf-8") + process = subprocess.Popen([resolved, str(path), "--projection", projection]) + return ViewerHandle(process, snapshot) + except BaseException: + snapshot.cleanup() + raise diff --git a/crates/hyper-viz-python/python/hyper_viz/py.typed b/crates/hyper-viz-python/python/hyper_viz/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/crates/hyper-viz-python/requirements-test.txt b/crates/hyper-viz-python/requirements-test.txt new file mode 100644 index 0000000..2bcf9c9 --- /dev/null +++ b/crates/hyper-viz-python/requirements-test.txt @@ -0,0 +1,3 @@ +pytest==8.4.2 +xgi==0.10.2 +mypy==1.18.2 diff --git a/crates/hyper-viz-python/src/bin/stub_gen.rs b/crates/hyper-viz-python/src/bin/stub_gen.rs new file mode 100644 index 0000000..129c4d8 --- /dev/null +++ b/crates/hyper-viz-python/src/bin/stub_gen.rs @@ -0,0 +1,16 @@ +fn main() -> pyo3_stub_gen::Result<()> { + let mut stubs = hyper_viz_python::stub_info()?; + // The parent is handwritten Python: a generated parent stub would hide + // show(), ViewerHandle, and exception aliases from type checkers. + stubs + .modules + .retain(|name, _| name.starts_with("hyper_viz._core")); + stubs.generate()?; + let root = stubs.python_root.join("hyper_viz"); + std::fs::rename(root.join("_core/__init__.pyi"), root.join("_core.pyi"))?; + let stub = root.join("_core.pyi"); + let content = std::fs::read_to_string(&stub)?; + std::fs::write(stub, format!("{}\n", content.trim_end()))?; + std::fs::remove_dir(root.join("_core"))?; + Ok(()) +} diff --git a/crates/hyper-viz-python/src/lib.rs b/crates/hyper-viz-python/src/lib.rs new file mode 100644 index 0000000..5316bd2 --- /dev/null +++ b/crates/hyper-viz-python/src/lib.rs @@ -0,0 +1,93 @@ +//! Small Python binding boundary; all interchange logic stays in hyper-viz. +use hyper_viz::{HifError, HifErrorKind, InputFormat}; +use pyo3::{ + create_exception, + exceptions::{PyException, PyOSError}, + prelude::*, +}; +use pyo3_stub_gen::{define_stub_info_gatherer, derive::*}; + +create_exception!(_core, HifValidationError, PyException); +create_exception!(_core, HifCompatibilityError, PyException); + +fn error(py: Python<'_>, err: HifError) -> PyErr { + let result = match err.kind { + HifErrorKind::Compatibility => HifCompatibilityError::new_err(err.to_string()), + HifErrorKind::Io => PyOSError::new_err(err.to_string()), + _ => HifValidationError::new_err(err.to_string()), + }; + let value = result.value(py); + let _ = value.setattr("location", err.location); + let _ = value.setattr("reason", err.reason); + result +} + +#[gen_stub_pyclass] +#[pyclass(module = "hyper_viz._core", name = "HifDocument", frozen)] +pub struct Document { + document: hyper_viz::HifDocument, +} + +#[gen_stub_pymethods] +#[pymethods] +impl Document { + /// Parse and validate HIF without requiring viewer compatibility. + #[staticmethod] + fn from_json(py: Python<'_>, raw: &str) -> PyResult { + hyper_viz::parse_hif(raw) + .map(|document| Self { document }) + .map_err(|e| error(py, e)) + } + /// Load HIF offline from a UTF-8 JSON file. + #[staticmethod] + fn load(py: Python<'_>, path: &str) -> PyResult { + hyper_viz::load_hif(path) + .map(|document| Self { document }) + .map_err(|e| error(py, e)) + } + /// Export native hypergraph.v1 JSON to a new HIF document. + #[staticmethod] + fn from_hypergraph_json(py: Python<'_>, raw: &str) -> PyResult { + let graph = + hyper_viz::from_json_str_with_format(raw, InputFormat::Hypergraph).map_err(|e| { + error( + py, + HifError { + kind: HifErrorKind::Validation, + location: "/".into(), + reason: e.to_string(), + }, + ) + })?; + hyper_viz::HifDocument::from_hypergraph(&graph) + .map(|document| Self { document }) + .map_err(|e| error(py, e)) + } + /// Serialize all HIF properties, including unsupported viewer semantics. + fn to_json(&self, py: Python<'_>) -> PyResult { + hyper_viz::serialize_hif(&self.document).map_err(|e| error(py, e)) + } + /// Save all HIF properties as UTF-8 JSON. + fn save(&self, py: Python<'_>, path: &str) -> PyResult<()> { + hyper_viz::save_hif(path, &self.document).map_err(|e| error(py, e)) + } + /// Convert to viewer JSON, rejecting unsupported semantics and weight overflow. + fn to_hypergraph_json(&self, py: Python<'_>) -> PyResult { + let graph = self.document.to_hypergraph().map_err(|e| error(py, e))?; + serde_json::to_string(&graph).map_err(|e| HifValidationError::new_err(e.to_string())) + } +} +#[pymodule] +fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_class::()?; + m.add( + "HifValidationError", + m.py().get_type::(), + )?; + m.add( + "HifCompatibilityError", + m.py().get_type::(), + )?; + Ok(()) +} +define_stub_info_gatherer!(stub_info); diff --git a/crates/hyper-viz-python/tests/check_wheel.py b/crates/hyper-viz-python/tests/check_wheel.py new file mode 100644 index 0000000..b47f7be --- /dev/null +++ b/crates/hyper-viz-python/tests/check_wheel.py @@ -0,0 +1,19 @@ +"""Validate distributable contents without relying on an editable installation.""" +from pathlib import Path +import sys +import zipfile + +wheels = list(Path(sys.argv[1]).glob('*.whl')) +assert wheels, 'No wheels built' +for wheel in wheels: + assert '-cp310-abi3-' in wheel.name, wheel.name + with zipfile.ZipFile(wheel) as archive: + names = set(archive.namelist()) + for required in ['hyper_viz/__init__.py', 'hyper_viz/_core.pyi', 'hyper_viz/_viewer.py', 'hyper_viz/_errors.py', 'hyper_viz/py.typed', 'hyper_viz/HIF-LICENSE', 'hyper_viz/LICENSE-MIT', 'hyper_viz/LICENSE-APACHE']: + assert required in names, f'{wheel.name}: missing {required}' + assert 'hyper_viz/__init__.pyi' not in names, 'Parent stub hides handwritten API' + native = [name for name in names if name.startswith('hyper_viz/_core') and name.endswith(('.so','.pyd'))] + assert len(native) == 1, f'Expected one native extension: {native}' + assert archive.read('hyper_viz/HIF-LICENSE').startswith(b'MIT License') + assert not any('__pycache__' in name for name in names), 'Wheel includes bytecode caches' + print(f'Checked {wheel.name}') diff --git a/crates/hyper-viz-python/tests/test_api.py b/crates/hyper-viz-python/tests/test_api.py new file mode 100644 index 0000000..b7949e7 --- /dev/null +++ b/crates/hyper-viz-python/tests/test_api.py @@ -0,0 +1,152 @@ +import json +import os +import sys +from pathlib import Path + +import pytest +import hyper_viz as hv + + +def test_rich_hif_is_preserved_but_not_viewed(tmp_path): + raw = {"network-type":"directed", "incidences":[{"node":1,"edge":"e","direction":"head","attrs":{"role":None}}]} + doc = hv.HifDocument.from_json(json.dumps(raw)) + path = tmp_path / 'roundtrip.json' + doc.save(str(path)) + assert json.loads(hv.HifDocument.load(str(path)).to_json()) == raw + with pytest.raises(hv.HifCompatibilityError) as error: + doc.to_hypergraph_json() + assert error.value.location == '/network-type' + assert error.value.reason + + +def test_validation_and_native_constructor(): + with pytest.raises(hv.HifValidationError) as error: + hv.HifDocument.from_json('{"incidences":[{"node":false,"edge":1}]}') + assert '/incidences/0/node' in error.value.location + native = {"version":"hypergraph.v1","vertices":[{"id":"a","attrs":{"source":[None,1]}}],"hyperedges":[{"id":"e","vertices":["a"]}]} + doc = hv.HifDocument.from_hypergraph_json(json.dumps(native)) + assert json.loads(doc.to_json())['nodes'][0]['attrs']['source'] == [None,1] + assert json.loads(doc.to_hypergraph_json())['hyperedges'][0]['vertices'] == ['s:a'] + with pytest.raises(hv.HifValidationError): + hv.HifDocument.from_hypergraph_json('{"incidences":[]}') + with pytest.raises(OSError): + hv.HifDocument.load('/nonexistent/hyper-hif-test') + + +@pytest.mark.skipif(sys.platform == 'win32', reason='POSIX executable fixture; cross-platform missing-executable test still runs') +def test_launcher_owns_snapshot_and_cleans_after_exit(tmp_path): + exe = tmp_path / 'viewer with spaces' + output = tmp_path / 'observed.json' + exe.write_text(f'#!{sys.executable}\nimport sys,json,time\nfrom pathlib import Path\nPath({str(output)!r}).write_text(json.dumps({{"args":sys.argv[1:],"graph":json.loads(Path(sys.argv[1]).read_text())}}))\ntime.sleep(0.1)\nsys.exit(7)\n') + exe.chmod(0o755) + doc = hv.HifDocument.from_json('{"incidences":[{"node":"a","edge":"e"}]}') + handle = hv.show(doc, executable=exe, projection='star') + assert handle.wait(timeout=10) == 7 + observed = json.loads(output.read_text()) + assert observed['args'][1:] == ['--projection','star'] + assert observed['graph']['vertices'][0]['id'] == 's:a' + assert not Path(observed['args'][0]).exists() + handle.close() + + +@pytest.mark.skipif(sys.platform == 'win32', reason='POSIX executable fixture') +def test_close_terminates_and_cleanup_is_automatic(tmp_path): + exe = tmp_path / 'sleeper' + output = tmp_path / 'path.txt' + exe.write_text(f'#!{sys.executable}\nimport sys,time\nfrom pathlib import Path\nPath({str(output)!r}).write_text(sys.argv[1])\ntime.sleep(60)\n') + exe.chmod(0o755) + handle = hv.show(hv.HifDocument.from_json('{"incidences":[]}'), executable=exe) + import time + deadline = time.monotonic() + 10 + while not output.exists() and time.monotonic() < deadline: + time.sleep(0.01) + snapshot = Path(output.read_text()) + assert snapshot.exists() + handle.close() + assert handle.poll() is not None + assert not snapshot.exists() + + +def test_launcher_failure_cleans_and_validates(monkeypatch, tmp_path): + doc = hv.HifDocument.from_json('{"incidences":[]}') + monkeypatch.setenv('PATH', str(tmp_path)) + # Exercise a missing installation even when the test environment has the extra. + import hyper_viz._viewer as launcher + from importlib.metadata import PackageNotFoundError + real_distribution = launcher.distribution + def without_companion(name): + if name == 'hypergraph-viz-viewer': + raise PackageNotFoundError(name) + return real_distribution(name) + monkeypatch.setattr(launcher, 'distribution', without_companion) + with pytest.raises(FileNotFoundError, match='install'): + hv.show(doc) + with pytest.raises(FileNotFoundError): + hv.show(doc, executable=tmp_path/'absent') + with pytest.raises(ValueError): + hv.show(doc, projection='wrong') + with pytest.raises(hv.HifCompatibilityError): + hv.show(hv.HifDocument.from_json('{"network-type":"directed","incidences":[]}')) + + +def test_xgi_roundtrip(tmp_path): + import xgi + graph = xgi.Hypergraph() + graph.add_nodes_from(['isolated','a','b']) + graph.add_edge(['a','b'],idx='edge') + graph.nodes['a']['nested'] = {'evidence':[None,3]} + incoming, outgoing = tmp_path/'xgi.json', tmp_path/'hyper.json' + xgi.write_hif(graph, str(incoming)) + doc = hv.HifDocument.load(str(incoming)) + doc.save(str(outgoing)) + restored = xgi.read_hif(str(outgoing)) + assert set(restored.nodes) == {'isolated','a','b'} + assert restored.edges.members('edge') == {'a','b'} + assert restored.nodes['a']['nested'] == {'evidence':[None,3]} + # XGI may export incidence weights; full interchange must still succeed. + + +def test_big_integer_ids_and_exact_decimal_metadata(): + raw = '{"incidences":[],"nodes":[{"node":184467440737095516160,"attrs":{"precise":0.12345678901234567890123456789}}]}' + doc = hv.HifDocument.from_json(raw) + assert '184467440737095516160' in doc.to_json() + assert '0.12345678901234567890123456789' in doc.to_json() + assert json.loads(doc.to_hypergraph_json())['vertices'][0]['id'] == 'i:184467440737095516160' + + +def test_cross_platform_process_exit_cleanup(): + # Empty native graph JSON is also a valid Python dictionary expression. + # A real Python child consumes the snapshot and exits on every platform. + handle = hv.show(hv.HifDocument.from_json('{"incidences":[]}'), executable=sys.executable) + assert handle.wait(timeout=10) == 0 + handle.close() + + +@pytest.mark.skipif(sys.platform == 'win32', reason='POSIX executable fixture') +def test_automatic_exit_cleanup_without_wait(tmp_path): + exe = tmp_path / 'quick-viewer' + output = tmp_path / 'snapshot.txt' + exe.write_text(f'#!{sys.executable}\nimport sys,time\nfrom pathlib import Path\nPath({str(output)!r}).write_text(sys.argv[1])\ntime.sleep(0.1)\n') + exe.chmod(0o755) + handle = hv.show(hv.HifDocument.from_json('{"incidences":[]}'), executable=exe) + import time + deadline = time.monotonic() + 10 + while not output.exists() and time.monotonic() < deadline: + time.sleep(0.01) + path = Path(output.read_text()) + while path.exists() and time.monotonic() < deadline: + time.sleep(0.01) + assert not path.exists() + assert handle.poll() == 0 + + +@pytest.mark.skipif(sys.platform == 'win32', reason='POSIX invalid interpreter fixture') +def test_spawn_failure_removes_created_snapshot(monkeypatch, tmp_path): + import tempfile + monkeypatch.setattr(tempfile, 'tempdir', str(tmp_path)) + exe = tmp_path / 'broken-viewer' + exe.write_text('#!/nonexistent/hyper-test-interpreter\n') + exe.chmod(0o755) + with pytest.raises(FileNotFoundError): + hv.show(hv.HifDocument.from_json('{"incidences":[]}'), executable=exe) + assert list(tmp_path.glob('hyper-viz-*')) == [] diff --git a/crates/hyper-viz-python/tests/test_viewer_installation.py b/crates/hyper-viz-python/tests/test_viewer_installation.py new file mode 100644 index 0000000..fde33f6 --- /dev/null +++ b/crates/hyper-viz-python/tests/test_viewer_installation.py @@ -0,0 +1,80 @@ +"""Installed-package discovery: no activation, downloads, or fake package API.""" +import csv +from importlib.metadata import version +import os +from pathlib import Path +import sys + +import pytest +from hyper_viz._viewer import _resolve_executable + + +def companion(tmp_path, monkeypatch, package_version=None, filenames=None): + site = tmp_path / 'site packages' + metadata = site / 'hypergraph_viz_viewer.dist-info' + metadata.mkdir(parents=True) + metadata.joinpath('METADATA').write_text( + 'Metadata-Version: 2.1\nName: hypergraph-viz-viewer\n' + f'Version: {package_version or version("hypergraph-viz")}\n' + ) + binaries = [] + for filename in filenames or ['hyper.exe' if sys.platform == 'win32' else 'hyper']: + binary = site.parent / 'bin with spaces' / filename + binary.parent.mkdir(exist_ok=True) + binary.write_text('#!/bin/sh\nexit 0\n') + binary.chmod(0o755) + binaries.append(binary) + with metadata.joinpath('RECORD').open('w', newline='') as stream: + writer = csv.writer(stream) + for binary in binaries: + writer.writerow([os.path.relpath(binary, site), '', '']) + writer.writerow(['hypergraph_viz_viewer.dist-info/METADATA', '', '']) + monkeypatch.syspath_prepend(str(site)) + monkeypatch.setenv('PATH', '') + return binaries + + +def test_companion_is_found_without_activating_environment(tmp_path, monkeypatch): + binaries = companion(tmp_path, monkeypatch) + assert Path(_resolve_executable(None)) == binaries[0] + + +def test_explicit_executable_overrides_mismatched_companion(tmp_path, monkeypatch): + companion(tmp_path, monkeypatch, '0.0.0') + assert Path(_resolve_executable(sys.executable)) == Path(sys.executable) + with pytest.raises(FileNotFoundError): + _resolve_executable(tmp_path / 'missing') + + +def test_companion_version_mismatch_has_install_guidance(tmp_path, monkeypatch): + companion(tmp_path, monkeypatch, '0.0.0') + with pytest.raises(RuntimeError, match=r'hypergraph-viz\[viewer\]'): + _resolve_executable(None) + + +def test_damaged_companion_is_reported(tmp_path, monkeypatch): + binaries = companion(tmp_path, monkeypatch) + binaries[0].unlink() + with pytest.raises(FileNotFoundError, match='reinstall'): + _resolve_executable(None) + + +def test_ambiguous_companion_is_rejected(tmp_path, monkeypatch): + companion(tmp_path, monkeypatch, filenames=['hyper', 'hyper.exe']) + with pytest.raises(RuntimeError, match='multiple'): + _resolve_executable(None) + + +@pytest.mark.skipif(sys.platform == 'win32', reason='Windows does not use POSIX executable bits') +def test_non_executable_companion_is_reported(tmp_path, monkeypatch): + binaries = companion(tmp_path, monkeypatch) + binaries[0].chmod(0o644) + with pytest.raises(FileNotFoundError, match='unusable'): + _resolve_executable(None) + + +def test_missing_record_is_reported(tmp_path, monkeypatch): + companion(tmp_path, monkeypatch) + (tmp_path / 'site packages' / 'hypergraph_viz_viewer.dist-info' / 'RECORD').unlink() + with pytest.raises(FileNotFoundError, match='reinstall'): + _resolve_executable(None) diff --git a/crates/hyper-viz/Cargo.toml b/crates/hyper-viz/Cargo.toml index 9a64b4d..441eea5 100644 --- a/crates/hyper-viz/Cargo.toml +++ b/crates/hyper-viz/Cargo.toml @@ -28,5 +28,8 @@ serde_json = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true } +num-bigint = "=0.4.8" +jsonschema = { version = "=0.33.0", default-features = false } + [dev-dependencies] tempfile = "3" diff --git a/crates/hyper-viz/examples/ecosystem_core.rs b/crates/hyper-viz/examples/ecosystem_core.rs new file mode 100644 index 0000000..6b93cd0 --- /dev/null +++ b/crates/hyper-viz/examples/ecosystem_core.rs @@ -0,0 +1,154 @@ +//! Isolated conversion, projection, layout, and hull CPU timings. +use hyper_viz::{ForceLayout3D, LayoutConfig, NodeRole, Projection}; +use serde_json::json; +use std::{ + collections::{BTreeSet, HashMap}, + hint::black_box, + time::Instant, +}; +fn main() { + let args: Vec = std::env::args().collect(); + let raw = std::fs::read_to_string(&args[1]).unwrap(); + let hif_raw = + std::fs::read_to_string(args[1].trim_end_matches(".json").to_string() + ".hif.json") + .unwrap(); + let start = Instant::now(); + let graph = hyper_viz::from_json_str(&raw).unwrap(); + let native_ms = start.elapsed().as_secs_f64() * 1000.; + let start = Instant::now(); + let document = hyper_viz::parse_hif(&hif_raw).unwrap(); + let hif_ms = start.elapsed().as_secs_f64() * 1000.; + let start = Instant::now(); + let converted = document.to_hypergraph().unwrap(); + let convert_ms = start.elapsed().as_secs_f64() * 1000.; + assert_eq!(converted.vertices.len(), graph.vertices.len()); + assert_eq!(converted.hyperedges.len(), graph.hyperedges.len()); + // These fixtures intentionally use string IDs. Verify every record, not just counts. + for (node, native) in document.nodes().unwrap().iter().zip(&converted.vertices) { + let hyper_viz::HifId::String(id) = &node.node else { + panic!("fixture ID must be a string") + }; + assert_eq!(native.id, format!("s:{id}")); + assert_eq!(native.attrs, node.attrs.clone().unwrap_or_default()); + } + for (edge, native) in graph.hyperedges.iter().zip(&converted.hyperedges) { + assert_eq!(native.id, format!("s:{}", edge.id)); + assert_eq!( + native.vertices, + edge.vertices + .iter() + .map(|id| format!("s:{id}")) + .collect::>() + ); + } + for (edge, native) in document.edges().unwrap().iter().zip(&converted.hyperedges) { + assert_eq!(native.attrs, edge.attrs.clone().unwrap_or_default()); + } + assert_eq!( + converted.meta.attrs, + serde_json::from_value::>( + serde_json::to_value(&document).unwrap()["metadata"].clone() + ) + .unwrap() + ); + let roundtrip = hyper_viz::parse_hif(&hyper_viz::serialize_hif(&document).unwrap()).unwrap(); + assert_eq!(document, roundtrip); + let start = Instant::now(); + let scene = graph.project(Projection::Bipartite); + let projection_ms = start.elapsed().as_secs_f64() * 1000.; + assert!(scene.warnings.is_empty(), "invalid canonical projection"); + assert_eq!( + scene.links.len(), + graph + .hyperedges + .iter() + .map(|e| e.vertices.len()) + .sum::() + ); + let expected: BTreeSet<_> = graph + .hyperedges + .iter() + .flat_map(|e| e.vertices.iter().map(|v| (e.id.clone(), v.clone()))) + .collect(); + let actual: BTreeSet<_> = scene + .links + .iter() + .map(|link| { + assert_eq!(scene.nodes[link.source].role, NodeRole::HyperedgeHub); + assert_eq!(scene.nodes[link.target].role, NodeRole::Vertex); + ( + link.hyperedge_id.clone().unwrap(), + scene.nodes[link.target].id.clone(), + ) + }) + .collect(); + assert_eq!( + actual, expected, + "every incidence must connect its original group/member" + ); + let positions: HashMap<_, _> = graph + .vertices + .iter() + .map(|v| { + let p = v.attrs["position"].as_array().unwrap(); + ( + v.id.as_str(), + hyper_viz::Vec3::new( + p[0].as_f64().unwrap() as f32, + p[1].as_f64().unwrap() as f32, + p[2].as_f64().unwrap() as f32, + ), + ) + }) + .collect(); + let mut layout = ForceLayout3D::from_scene(&scene, LayoutConfig::default()); + let edges: HashMap<_, _> = graph + .hyperedges + .iter() + .map(|e| (e.id.as_str(), e)) + .collect(); + for node in &scene.nodes { + layout.positions[node.index] = if node.role == NodeRole::Vertex { + positions[node.id.as_str()] + } else { + let e = edges[node.hyperedge_id.as_deref().unwrap()]; + let mut p = hyper_viz::Vec3::ZERO; + for v in &e.vertices { + p += positions[v.as_str()]; + } + if e.vertices.is_empty() { + p + } else { + p / e.vertices.len() as f32 + } + }; + } + // Warm the same solver, then record consecutive steps; not layout convergence. + for _ in 0..5 { + layout.step(); + } + let mut steps = Vec::new(); + for _ in 0..20 { + let start = Instant::now(); + layout.step(); + steps.push(start.elapsed().as_secs_f64() * 1000.); + } + let start = Instant::now(); + let mut hulls = 0; + for e in &graph.hyperedges { + let points: Vec<_> = e + .vertices + .iter() + .map(|v| { + let p = positions[v.as_str()]; + [p.x, p.y, p.z] + }) + .collect(); + if black_box(hyper_viz::hull_from_points(&points)).is_some() { + hulls += 1; + } + } + let hull_ms = start.elapsed().as_secs_f64() * 1000.; + let result = json!({"tool":"hyper-core","dataset":graph.meta.id,"native_parse_ms":native_ms,"hif_parse_validate_ms":hif_ms,"hif_viewer_conversion_ms":convert_ms,"bipartite_projection_ms":projection_ms,"layout_step_ms":steps,"all_hulls_cpu_ms":hull_ms,"hulls":hulls,"hull_vertex_cap":hyper_viz::MAX_HULL_VERTICES,"correctness":"passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences","vertices":graph.vertices.len(),"hyperedges":graph.hyperedges.len(),"links":scene.links.len(),"layout_settings":"LayoutConfig::default,5warmup20steps,seeded common XYZ"}); + std::fs::write(&args[2], serde_json::to_vec_pretty(&result).unwrap()).unwrap(); +} diff --git a/crates/hyper-viz/examples/hif_roundtrip.rs b/crates/hyper-viz/examples/hif_roundtrip.rs new file mode 100644 index 0000000..0135be2 --- /dev/null +++ b/crates/hyper-viz/examples/hif_roundtrip.rs @@ -0,0 +1,20 @@ +//! `cargo run -p hyper-viz --example hif_roundtrip -- input.json output.json` +use hyper_viz::{load_hif, save_hif}; +fn main() -> Result<(), Box> { + let args: Vec = std::env::args().collect(); + if args.len() != 3 { + return Err("usage: hif_roundtrip INPUT OUTPUT".into()); + } + let document = load_hif(&args[1])?; + save_hif(&args[2], &document)?; + println!("Preserved {} incidences", document.incidences().len()); + match document.to_hypergraph() { + Ok(graph) => println!( + "Viewer compatible: {} nodes / {} edges", + graph.vertices.len(), + graph.hyperedges.len() + ), + Err(error) => println!("Document preserved; viewer conversion unavailable: {error}"), + } + Ok(()) +} diff --git a/crates/hyper-viz/schemas/HIF-LICENSE b/crates/hyper-viz/schemas/HIF-LICENSE new file mode 100644 index 0000000..5997fb9 --- /dev/null +++ b/crates/hyper-viz/schemas/HIF-LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024-2025 HIF development team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/hyper-viz/schemas/README.md b/crates/hyper-viz/schemas/README.md new file mode 100644 index 0000000..663993c --- /dev/null +++ b/crates/hyper-viz/schemas/README.md @@ -0,0 +1,8 @@ +# Vendored HIF schema + +Source: https://github.com/HIF-org/HIF-standard +Commit: b691a3d2ec32100c0229ebe1151e9afad015c356 +Path: schemas/hif_schema.json +License: HIF-LICENSE (upstream MIT) + +The unmodified schema declares Draft 7. Its `unevaluatedProperties` keywords are not part of that draft; `additionalProperties: false` supplies the closed-object checks. The validator is compiled from embedded bytes, with network retrieval features disabled. No schema is fetched at runtime. diff --git a/crates/hyper-viz/schemas/hif.schema.json b/crates/hyper-viz/schemas/hif.schema.json new file mode 100644 index 0000000..5651f07 --- /dev/null +++ b/crates/hyper-viz/schemas/hif.schema.json @@ -0,0 +1,112 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/pszufe/HIF_validators/main/schemas/schema.json", + "title": "Hypergraph Interchange Format", + "version": "latest", + "type": "object", + "properties": { + "network-type": { + "enum": [ + "undirected", + "directed", + "asc" + ] + }, + "metadata": { + "type": "object" + }, + "incidences": { + "type": "array", + "items": { + "type": "object", + "properties": { + "edge": { + "type": [ + "string", + "integer" + ] + }, + "node": { + "type": [ + "string", + "integer" + ] + }, + "weight": { + "type": "number" + }, + "direction": { + "enum": [ + "head", + "tail" + ] + }, + "attrs": { + "type": "object" + } + }, + "unevaluatedProperties": false, + "additionalProperties": false, + "required": [ + "edge", + "node" + ] + } + }, + "nodes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "node": { + "type": [ + "string", + "integer" + ] + }, + "weight": { + "type": "number" + }, + "attrs": { + "type": "object" + } + }, + "unevaluatedProperties": false, + "additionalProperties": false, + "required": [ + "node" + ] + } + }, + "edges": { + "type": "array", + "items": { + "type": "object", + "properties": { + "edge": { + "type": [ + "string", + "integer" + ] + }, + "weight": { + "type": "number" + }, + "attrs": { + "type": "object" + } + }, + "unevaluatedProperties": false, + "additionalProperties": false, + "required": [ + "edge" + ] + } + } + }, + "unevaluatedProperties": false, + "additionalProperties": false, + "required": [ + "incidences" + ] +} \ No newline at end of file diff --git a/crates/hyper-viz/src/error.rs b/crates/hyper-viz/src/error.rs index f693b54..2b99387 100644 --- a/crates/hyper-viz/src/error.rs +++ b/crates/hyper-viz/src/error.rs @@ -4,6 +4,10 @@ use thiserror::Error; #[derive(Debug, Error)] pub enum VizError { + #[error(transparent)] + Hif(#[from] crate::hif::HifError), + #[error("input format: {0}")] + InputFormat(String), #[error("io error at {path}: {source}")] Io { path: PathBuf, diff --git a/crates/hyper-viz/src/hif.rs b/crates/hyper-viz/src/hif.rs new file mode 100644 index 0000000..482631c --- /dev/null +++ b/crates/hyper-viz/src/hif.rs @@ -0,0 +1,421 @@ +//! Offline HIF interchange. A document is not necessarily safe to visualize. +use crate::{Hyperedge, Hypergraph, Vertex}; +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Number, Value}; +use std::{ + collections::{HashMap, HashSet}, + path::Path, + sync::OnceLock, +}; + +/// Location-aware format or viewer compatibility failure (JSON Pointer locations). +#[derive(Debug, Clone, thiserror::Error)] +#[error("HIF {kind:?} at {location}: {reason}")] +pub struct HifError { + pub kind: HifErrorKind, + pub location: String, + pub reason: String, +} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HifErrorKind { + Json, + Validation, + Compatibility, + Io, +} +impl HifError { + fn new(kind: HifErrorKind, location: impl Into, reason: impl Into) -> Self { + Self { + kind, + location: location.into(), + reason: reason.into(), + } + } + fn incompatible(location: impl Into, reason: impl Into) -> Self { + Self::new(HifErrorKind::Compatibility, location, reason) + } +} + +/// Integer and string IDs inhabit separate namespaces. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)] +#[serde(untagged)] +pub enum HifId { + String(String), + Integer(Number), +} +impl<'de> Deserialize<'de> for HifId { + fn deserialize>(deserializer: D) -> Result { + match Value::deserialize(deserializer)? { + Value::String(s) => Ok(Self::String(s)), + Value::Number(n) if is_integral(&n) => Ok(Self::Integer(n)), + _ => Err(serde::de::Error::custom( + "HIF IDs must be strings or integers", + )), + } + } +} +impl HifId { + fn viewer_id(&self) -> String { + match self { + Self::String(s) => format!("s:{s}"), + Self::Integer(n) => format!("i:{}", canonical_integer(n)), + } + } + fn label(&self) -> String { + match self { + Self::String(s) => s.clone(), + Self::Integer(n) => n.to_string(), + } + } +} +// A normalized significand and arbitrary-size decimal exponent avoid both +// float rounding and overflow when IDs use JSON's exponent notation. +fn integer_parts(n: &Number) -> Option<(bool, String, num_bigint::BigInt)> { + let raw = n.to_string(); + let (mantissa, exponent) = raw.split_once(['e', 'E']).unwrap_or((&raw, "0")); + let exp = exponent.parse::().ok()?; + let negative = mantissa.starts_with('-'); + let (_, fraction) = mantissa.split_once('.').unwrap_or((mantissa, "")); + let digits = mantissa.trim_start_matches('-').replace('.', ""); + let digits = digits.trim_start_matches('0'); + if digits.is_empty() { + return Some((false, "0".into(), 0.into())); + } + let trailing = digits.len() - digits.trim_end_matches('0').len(); + let shift = exp - num_bigint::BigInt::from(fraction.len()) + num_bigint::BigInt::from(trailing); + Some((negative, digits[..digits.len() - trailing].into(), shift)) +} +fn is_integral(n: &Number) -> bool { + integer_parts(n).is_some_and(|(_, _, shift)| shift.sign() != num_bigint::Sign::Minus) +} +fn canonical_integer(n: &Number) -> String { + let (negative, mut digits, shift) = integer_parts(n).expect("validated integer ID"); + let sign = if negative { "-" } else { "" }; + // Avoid allocating huge expanded strings while preserving exact identity. + if &shift + num_bigint::BigInt::from(digits.len()) > num_bigint::BigInt::from(10000) { + return format!("{sign}{digits}e{shift}"); + } + let zeros = usize::try_from(shift).expect("validated bounded nonnegative exponent"); + digits.extend(std::iter::repeat_n('0', zeros)); + format!("{sign}{digits}") +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct HifNode { + pub node: HifId, + #[serde(skip_serializing_if = "Option::is_none")] + pub weight: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub attrs: Option>, +} +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct HifEdge { + pub edge: HifId, + #[serde(skip_serializing_if = "Option::is_none")] + pub weight: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub attrs: Option>, +} +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct HifIncidence { + pub node: HifId, + pub edge: HifId, + #[serde(skip_serializing_if = "Option::is_none")] + pub weight: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub direction: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub attrs: Option>, +} +/// Validated HIF document; use the interchange functions rather than raw deserialization. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct HifDocument { + #[serde(rename = "network-type", skip_serializing_if = "Option::is_none")] + network_type: Option, + #[serde(skip_serializing_if = "Option::is_none")] + metadata: Option>, + incidences: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + nodes: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + edges: Option>, +} +fn validator() -> &'static jsonschema::Validator { + static VALIDATOR: OnceLock = OnceLock::new(); + VALIDATOR.get_or_init(|| { + let schema: Value = serde_json::from_str(include_str!("../schemas/hif.schema.json")) + .expect("bundled HIF schema"); + jsonschema::options() + .with_draft(jsonschema::Draft::Draft7) + .build(&schema) + .expect("valid bundled HIF schema") + }) +} +pub fn parse_hif(raw: &str) -> Result { + let value: Value = serde_json::from_str(raw) + .map_err(|e| HifError::new(HifErrorKind::Json, "/", e.to_string()))?; + // jsonschema 0.33's union type validator recognizes only i64/u64 as + // integers. Validate a shadow copy with mathematically integral IDs + // normalized to zero; deserialize the untouched arbitrary-precision input. + let mut validation_value = value.clone(); + for (collection, keys) in [ + ("nodes", &["node"][..]), + ("edges", &["edge"][..]), + ("incidences", &["node", "edge"][..]), + ] { + if let Some(records) = validation_value + .get_mut(collection) + .and_then(Value::as_array_mut) + { + for record in records { + for key in keys { + if record + .get(*key) + .and_then(Value::as_number) + .is_some_and(is_integral) + { + record[*key] = Value::Number(0.into()); + } + } + } + } + } + if let Some(e) = validator().iter_errors(&validation_value).next() { + return Err(HifError::new( + HifErrorKind::Validation, + format!("/{}", e.instance_path.to_string().trim_start_matches('/')), + e.to_string(), + )); + } + serde_json::from_str(raw) + .map_err(|e| HifError::new(HifErrorKind::Validation, "/", e.to_string())) +} +pub fn load_hif(path: impl AsRef) -> Result { + let path = path.as_ref(); + let raw = std::fs::read_to_string(path) + .map_err(|e| HifError::new(HifErrorKind::Io, path.display().to_string(), e.to_string()))?; + parse_hif(&raw) +} +pub fn serialize_hif(doc: &HifDocument) -> Result { + let raw = serde_json::to_string_pretty(doc) + .map_err(|e| HifError::new(HifErrorKind::Json, "/", e.to_string()))?; + parse_hif(&raw)?; + Ok(raw) +} +pub fn save_hif(path: impl AsRef, doc: &HifDocument) -> Result<(), HifError> { + let raw = serialize_hif(doc)?; + let path = path.as_ref(); + std::fs::write(path, raw) + .map_err(|e| HifError::new(HifErrorKind::Io, path.display().to_string(), e.to_string())) +} +fn weight(value: &Option, location: &str) -> Result, HifError> { + value + .as_ref() + .map(|n| { + let w = n.as_f64().unwrap_or(f64::INFINITY) as f32; + if w.is_finite() { + Ok(w) + } else { + Err(HifError::incompatible( + location, + "weight overflows viewer f32; original is retained in HIF", + )) + } + }) + .transpose() +} +impl HifDocument { + pub fn incidences(&self) -> &[HifIncidence] { + &self.incidences + } + pub fn nodes(&self) -> Option<&[HifNode]> { + self.nodes.as_deref() + } + pub fn edges(&self) -> Option<&[HifEdge]> { + self.edges.as_deref() + } + pub fn to_hypergraph(&self) -> Result { + // Revalidate even documents constructed via serde by Rust callers. + serialize_hif(self)?; + if self + .network_type + .as_deref() + .is_some_and(|v| v != "undirected") + { + return Err(HifError::incompatible( + "/network-type", + "viewer supports only undirected hypergraphs", + )); + } + let mut graph = Hypergraph::new(); + graph.meta.attrs = self.metadata.clone().unwrap_or_default(); + let mut nodes = HashSet::new(); + let mut edges = HashMap::new(); + for (i, n) in self.nodes.iter().flatten().enumerate() { + let id = n.node.viewer_id(); + if !nodes.insert(id.clone()) { + return Err(HifError::incompatible( + format!("/nodes/{i}/node"), + "duplicate node declaration", + )); + } + let mut v = Vertex::new(id, n.node.label()); + v.attrs = n.attrs.clone().unwrap_or_default(); + v.weight = weight(&n.weight, &format!("/nodes/{i}/weight"))?; + graph.vertices.push(v); + } + for (i, e) in self.edges.iter().flatten().enumerate() { + let id = e.edge.viewer_id(); + if edges.insert(id.clone(), graph.hyperedges.len()).is_some() { + return Err(HifError::incompatible( + format!("/edges/{i}/edge"), + "duplicate edge declaration", + )); + } + let mut edge = Hyperedge::new(id, Vec::::new()).with_label(e.edge.label()); + edge.attrs = e.attrs.clone().unwrap_or_default(); + edge.weight = weight(&e.weight, &format!("/edges/{i}/weight"))?; + graph.hyperedges.push(edge); + } + let mut pairs = HashSet::new(); + for (i, incidence) in self.incidences.iter().enumerate() { + for (field, present) in [ + ("direction", incidence.direction.is_some()), + ("weight", incidence.weight.is_some()), + ("attrs", incidence.attrs.is_some()), + ] { + if present { + return Err(HifError::incompatible( + format!("/incidences/{i}/{field}"), + format!( + "incidence {field} cannot be represented by the viewer; retained in HIF" + ), + )); + } + } + let node = incidence.node.viewer_id(); + let edge = incidence.edge.viewer_id(); + if !pairs.insert((node.clone(), edge.clone())) { + return Err(HifError::incompatible( + format!("/incidences/{i}"), + "duplicate incidence pair", + )); + } + if nodes.insert(node.clone()) { + graph + .vertices + .push(Vertex::new(&node, incidence.node.label())); + } + let index = *edges.entry(edge.clone()).or_insert_with(|| { + graph.hyperedges.push( + Hyperedge::new(edge, Vec::::new()).with_label(incidence.edge.label()), + ); + graph.hyperedges.len() - 1 + }); + graph.hyperedges[index].vertices.push(node); + } + Ok(graph) + } + pub fn from_hypergraph(graph: &Hypergraph) -> Result { + fn attrs( + base: &Map, + display: Value, + location: &str, + ) -> Result>, HifError> { + let mut result = base.clone(); + if result.contains_key("_hyper_viz") { + return Err(HifError::incompatible( + location, + "reserved _hyper_viz attribute already exists", + )); + } + result.insert("_hyper_viz".into(), display); + Ok(Some(result)) + } + fn number(w: Option, location: &str) -> Result, HifError> { + w.map(|w| { + Number::from_f64(w as f64) + .ok_or_else(|| HifError::incompatible(location, "non-finite native weight")) + }) + .transpose() + } + if graph.version != crate::GRAPH_VERSION { + return Err(HifError::incompatible( + "/version", + "unsupported native graph version", + )); + } + let mut nodes = Vec::new(); + let mut edges = Vec::new(); + let mut incidences = Vec::new(); + let mut node_ids = HashSet::new(); + let mut edge_ids = HashSet::new(); + for (i, v) in graph.vertices.iter().enumerate() { + if !node_ids.insert(v.id.clone()) { + return Err(HifError::incompatible( + format!("/vertices/{i}/id"), + "duplicate native vertex ID", + )); + } + nodes.push(HifNode { + node: HifId::String(v.id.clone()), + weight: number(v.weight, &format!("/vertices/{i}/weight"))?, + attrs: attrs( + &v.attrs, + serde_json::json!({"label":v.label,"kind":v.kind,"status":v.status}), + &format!("/vertices/{i}/attrs"), + )?, + }); + } + for (i, e) in graph.hyperedges.iter().enumerate() { + if !edge_ids.insert(e.id.clone()) { + return Err(HifError::incompatible( + format!("/hyperedges/{i}/id"), + "duplicate native edge ID", + )); + } + let mut members = HashSet::new(); + for (j, v) in e.vertices.iter().enumerate() { + if !node_ids.contains(v) || !members.insert(v) { + return Err(HifError::incompatible( + format!("/hyperedges/{i}/vertices/{j}"), + "missing native vertex or duplicate membership", + )); + } + incidences.push(HifIncidence { + node: HifId::String(v.clone()), + edge: HifId::String(e.id.clone()), + weight: None, + direction: None, + attrs: None, + }); + } + edges.push(HifEdge { + edge: HifId::String(e.id.clone()), + weight: number(e.weight, &format!("/hyperedges/{i}/weight"))?, + attrs: attrs( + &e.attrs, + serde_json::json!({"label":e.label,"kind":e.kind,"status":e.status}), + &format!("/hyperedges/{i}/attrs"), + )?, + }); + } + let doc = Self { + network_type: Some("undirected".into()), + metadata: attrs( + &graph.meta.attrs, + serde_json::json!({"id":graph.meta.id,"title":graph.meta.title}), + "/meta/attrs", + )?, + nodes: Some(nodes), + edges: Some(edges), + incidences, + }; + serialize_hif(&doc)?; + Ok(doc) + } +} diff --git a/crates/hyper-viz/src/io/mod.rs b/crates/hyper-viz/src/io/mod.rs index b1bc0c3..6373244 100644 --- a/crates/hyper-viz/src/io/mod.rs +++ b/crates/hyper-viz/src/io/mod.rs @@ -10,13 +10,37 @@ use std::path::Path; use crate::error::VizError; use crate::schema::{GRAPH_VERSION, Hypergraph}; +/// Select an interchange format. Auto rejects mixed HIF/native envelopes. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum InputFormat { + #[default] + Auto, + Hypergraph, + Hif, +} +impl InputFormat { + pub fn parse(value: &str) -> Option { + match value { + "auto" => Some(Self::Auto), + "hypergraph" => Some(Self::Hypergraph), + "hif" => Some(Self::Hif), + _ => None, + } + } +} pub fn load_json(path: impl AsRef) -> Result { + load_json_with_format(path, InputFormat::Auto) +} +pub fn load_json_with_format( + path: impl AsRef, + format: InputFormat, +) -> Result { let path = path.as_ref(); let raw = std::fs::read_to_string(path).map_err(|source| VizError::Io { path: path.to_path_buf(), source, })?; - from_json_str(&raw) + from_json_str_with_format(&raw, format) } pub fn save_json(path: impl AsRef, graph: &Hypergraph) -> Result<(), VizError> { @@ -29,7 +53,24 @@ pub fn save_json(path: impl AsRef, graph: &Hypergraph) -> Result<(), VizEr } pub fn from_json_str(raw: &str) -> Result { + from_json_str_with_format(raw, InputFormat::Auto) +} +pub fn from_json_str_with_format(raw: &str, format: InputFormat) -> Result { let value: serde_json::Value = serde_json::from_str(raw)?; + let hif = ["incidences", "nodes", "edges", "network-type", "metadata"] + .iter() + .any(|key| value.get(key).is_some()); + let native = ["version", "vertices", "hyperedges", "meta"] + .iter() + .any(|key| value.get(key).is_some()); + if format == InputFormat::Hif || (format == InputFormat::Auto && hif && !native) { + return Ok(crate::parse_hif(raw)?.to_hypergraph()?); + } + if hif || !native || !value.is_object() { + return Err(VizError::InputFormat( + "unrecognized or ambiguous envelope; select a format and supply its fields".into(), + )); + } let version = value .get("version") .and_then(|v| v.as_str()) diff --git a/crates/hyper-viz/src/lib.rs b/crates/hyper-viz/src/lib.rs index 651257d..b7db2bb 100644 --- a/crates/hyper-viz/src/lib.rs +++ b/crates/hyper-viz/src/lib.rs @@ -28,6 +28,7 @@ //! ``` pub mod error; +pub mod hif; pub mod hull; pub mod io; pub mod layout; @@ -40,8 +41,15 @@ pub mod semantics; pub mod session; pub use error::VizError; +pub use hif::{ + HifDocument, HifEdge, HifError, HifErrorKind, HifId, HifIncidence, HifNode, load_hif, + parse_hif, save_hif, serialize_hif, +}; pub use hull::{HullMesh, MAX_HULL_VERTICES, hull_from_points, select_hull_vertices}; -pub use io::{from_json_str, load_json, save_json}; +pub use io::{ + InputFormat, from_json_str, from_json_str_with_format, load_json, load_json_with_format, + save_json, +}; pub use layout::{ForceLayout3D, LayoutConfig, Vec3, layout_edges_from_scene}; pub use motion::{ ONE_SHOT_CAP, ONE_SHOT_SECS, StatusMotion, apply_motion_rgba, hyperedge_status_key, id_phase, diff --git a/crates/hyper-viz/tests/hif.rs b/crates/hyper-viz/tests/hif.rs new file mode 100644 index 0000000..5d6ba5b --- /dev/null +++ b/crates/hyper-viz/tests/hif.rs @@ -0,0 +1,172 @@ +use hyper_viz::{ + HifDocument, InputFormat, from_json_str, from_json_str_with_format, parse_hif, serialize_hif, +}; +use serde_json::{Value, json}; + +#[test] +fn faithful_roundtrip_and_viewer_identity() { + let value = json!({"metadata":{"nested":{"list":[null,true,7]}},"nodes":[{"node":1,"attrs":{"label":"integer"}},{"node":"1","weight":2.5},{"node":"isolated"}],"edges":[{"edge":"empty"}],"incidences":[{"node":1,"edge":4},{"node":"1","edge":4}]}); + let doc = parse_hif(&value.to_string()).unwrap(); + assert_eq!( + serde_json::from_str::(&serialize_hif(&doc).unwrap()).unwrap(), + value + ); + let graph = doc.to_hypergraph().unwrap(); + assert_eq!( + graph + .vertices + .iter() + .map(|v| v.id.as_str()) + .collect::>(), + ["i:1", "s:1", "s:isolated"] + ); + assert_eq!(graph.hyperedges[0].vertices.len(), 0); + assert_eq!(graph.hyperedges[1].vertices, ["i:1", "s:1"]); + assert_eq!(graph.vertices[1].weight, Some(2.5)); + assert_eq!(graph.meta.attrs["nested"]["list"][0], Value::Null); +} + +#[test] +fn preserves_properties_that_viewer_rejects() { + for value in [ + json!({"network-type":"directed","incidences":[{"node":"a","edge":"e","direction":"head","weight":0.123456789,"attrs":{"role":null}}]}), + json!({"network-type":"asc","incidences":[]}), + json!({"incidences":[{"node":1,"edge":2,"attrs":{}}]}), + json!({"incidences":[{"node":1,"edge":2,"weight":0}]}), + json!({"incidences":[{"node":1,"edge":2,"direction":"tail"}]}), + ] { + let doc = parse_hif(&value.to_string()).unwrap(); + assert_eq!( + serde_json::from_str::(&serialize_hif(&doc).unwrap()).unwrap(), + value + ); + let error = doc.to_hypergraph().unwrap_err(); + assert!(!error.location.is_empty()); + } +} + +#[test] +fn schema_errors_and_duplicates_are_actionable() { + for raw in [ + r#"{}"#, + r#"{"incidences":[{"node":true,"edge":1}]}"#, + r#"{"incidences":[],"unexpected":1}"#, + ] { + assert!(parse_hif(raw).is_err()); + } + for value in [ + json!({"incidences":[],"nodes":[{"node":1},{"node":1}]}), + json!({"incidences":[],"edges":[{"edge":"x"},{"edge":"x"}]}), + json!({"incidences":[{"node":1,"edge":2},{"node":1,"edge":2}]}), + ] { + let doc = parse_hif(&value.to_string()).unwrap(); + assert!( + doc.to_hypergraph() + .unwrap_err() + .reason + .contains("duplicate") + ); + } + let doc = parse_hif(r#"{"incidences":[],"nodes":[{"node":1,"weight":1e100}]}"#).unwrap(); + assert!(doc.to_hypergraph().is_err()); +} + +#[test] +fn loading_detects_hif_and_rejects_ambiguity() { + assert_eq!( + from_json_str(r#"{"incidences":[{"node":"a","edge":"e"}]}"#) + .unwrap() + .vertices + .len(), + 1 + ); + assert!(from_json_str(r#"{"incidences":[],"vertices":[]}"#).is_err()); + assert!(from_json_str(r#"{}"#).is_err()); + assert!(from_json_str_with_format(r#"{"incidences":[]}"#, InputFormat::Hypergraph).is_err()); + assert!( + from_json_str_with_format( + r#"{"version":"hypergraph.v1","vertices":[]}"#, + InputFormat::Hif + ) + .is_err() + ); +} + +#[test] +fn native_export_keeps_attributes_and_weights() { + let mut graph = hyper_viz::sample_coauthorship(); + graph.vertices[0] + .attrs + .insert("evidence".into(), json!([null, 3])); + graph.vertices[0].weight = Some(1.25); + let doc = HifDocument::from_hypergraph(&graph).unwrap(); + let value: Value = serde_json::from_str(&serialize_hif(&doc).unwrap()).unwrap(); + assert_eq!(value["nodes"][0]["node"], "alice"); + assert_eq!(value["nodes"][0]["weight"], 1.25); + assert_eq!(value["nodes"][0]["attrs"]["evidence"], json!([null, 3])); +} + +#[test] +fn numerical_id_spellings_share_identity_but_big_ids_stay_exact() { + let doc = parse_hif(r#"{"nodes":[{"node":1.0},{"node":1e0}],"incidences":[]}"#).unwrap(); + assert!( + doc.to_hypergraph() + .unwrap_err() + .reason + .contains("duplicate") + ); + let raw = r#"{"nodes":[{"node":184467440737095516160,"attrs":{"precise":0.12345678901234567890123456789}}],"incidences":[]}"#; + let doc = parse_hif(raw).unwrap(); + let output = serialize_hif(&doc).unwrap(); + assert!(output.contains("184467440737095516160")); + assert!(output.contains("0.12345678901234567890123456789")); + assert_eq!( + doc.to_hypergraph().unwrap().vertices[0].id, + "i:184467440737095516160" + ); +} + +#[test] +fn explicit_native_format_requires_native_envelope() { + assert!(from_json_str_with_format("{}", InputFormat::Hypergraph).is_err()); +} + +#[test] +fn file_roundtrip_and_reserved_export_keys() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("hif.json"); + let doc = parse_hif(r#"{"incidences":[],"nodes":[],"metadata":{}}"#).unwrap(); + hyper_viz::save_hif(&path, &doc).unwrap(); + assert_eq!(hyper_viz::load_hif(&path).unwrap(), doc); + let mut graph = hyper_viz::sample_coauthorship(); + graph.vertices[0] + .attrs + .insert("_hyper_viz".into(), json!({"original":true})); + assert!(HifDocument::from_hypergraph(&graph).is_err()); + graph.vertices[0].attrs.clear(); + graph.vertices[0].weight = Some(f32::NAN); + assert!(HifDocument::from_hypergraph(&graph).is_err()); + graph.vertices[0].weight = None; + graph.hyperedges[0].vertices.push("missing".into()); + assert!(HifDocument::from_hypergraph(&graph).is_err()); +} + +#[test] +fn extreme_integer_exponents_do_not_panic_or_split_identity() { + let doc = parse_hif(r#"{"nodes":[{"node":10e9223372036854775807}],"incidences":[]}"#).unwrap(); + assert_eq!( + doc.to_hypergraph().unwrap().vertices[0].id, + "i:1e9223372036854775808" + ); + let doc = + parse_hif(r#"{"nodes":[{"node":0.1e10003},{"node":1e10002}],"incidences":[]}"#).unwrap(); + assert!( + doc.to_hypergraph() + .unwrap_err() + .reason + .contains("duplicate") + ); + assert!( + parse_hif(r#"{"nodes":[{"node":1e-999999999999999999999999}],"incidences":[]}"#).is_err() + ); +} diff --git a/docs/DESKTOP_PACKAGE.md b/docs/DESKTOP_PACKAGE.md new file mode 100644 index 0000000..15bad5b --- /dev/null +++ b/docs/DESKTOP_PACKAGE.md @@ -0,0 +1,38 @@ +# Hyper desktop viewer + +Prebuilt native viewer for [hypergraph-viz](https://pypi.org/project/hypergraph-viz/). +Install the Python API and matching viewer together: + +```sh +python -m pip install --upgrade "hypergraph-viz[viewer]" +``` + +```python +import hyper_viz + +document = hyper_viz.HifDocument.load("dataset.hif.json") +viewer = hyper_viz.show(document, projection="bipartite") +viewer.wait() # Or viewer.close() to terminate. +``` + +No repository checkout or Rust compiler is needed on supported platforms. +The viewer executable is also available as `hyper` in your Python environment. +The launcher locates it through installed-package metadata, so activating the +virtual environment is optional when running that environment's Python. + +Wheels cover Linux x86_64 (glibc 2.28+), macOS arm64/x86_64 (11.0+), and +Windows x86_64. A desktop session and working graphics driver are required. The viewer runs on the +machine executing Python; it does not embed into a notebook or send a window +from a remote kernel to your browser. Use the +[notebook prototype roadmap](https://github.com/atomicstrata/hyper/blob/python-v0.1.2/docs/NOTEBOOK_ROADMAP.md) +for the planned browser frontend. + +This package contains a native binary, not a Python extension. It does not +include HIF Python bindings by itself; use the `[viewer]` extra shown above. +Windows wheels link the C runtime statically, avoiding a separate Visual C++ +Redistributable installation. An explicit `executable="/path/to/hyper"` still overrides the packaged binary. +There are no downloads during `show()`. + +MIT OR Apache-2.0. The bundled HIF schema is MIT-licensed. Graphics drivers +remain system dependencies. Linux binary dependencies are audited without bundling external libraries. +Unused audio and gamepad backends are excluded from the desktop build. diff --git a/docs/HIF.md b/docs/HIF.md new file mode 100644 index 0000000..67d779b --- /dev/null +++ b/docs/HIF.md @@ -0,0 +1,86 @@ +# HIF interchange and Python integration + +Hyper can read [HIF](https://github.com/HIF-org/HIF-standard), the Hypergraph Interchange Format used by scientific hypergraph tools. Full-document interchange and viewer conversion are separate operations: a valid directed HIF document can be saved unchanged, but cannot currently be visualized faithfully. + +## Rust + +```rust +use hyper_viz::{load_hif, save_hif, HifDocument}; + +let document = load_hif("dataset.hif.json")?; +save_hif("roundtrip.hif.json", &document)?; +let graph = document.to_hypergraph()?; // checks viewer compatibility +let exported = HifDocument::from_hypergraph(&graph)?; +# Ok::<(), Box>(()) +``` + +`parse_hif` and `serialize_hif` operate on strings. `load_json` and `from_json_str` automatically detect HIF, native `hypergraph.v1`, and supported legacy exports. `load_json_with_format` and `from_json_str_with_format` accept `InputFormat::{Auto,Hif,Hypergraph}`. Mixed or unidentified envelopes are rejected. Native documents without a version remain accepted when native fields identify them. + +```sh +cargo run -- dataset.hif.json --format hif +cargo run -- dataset.hif.json --format hif --watch +``` + +Both initial loading and watched reloads use the selected format. An invalid watched update leaves the previous scene visible and logs its error. + +## Supported semantics + +HIF serialization preserves JSON values, record order, optional field presence, metadata, typed string/integer IDs, node/edge weights, and incidence direction, weights, and attributes. Whitespace and object-key ordering are not retained. The pinned upstream schema is bundled and validated offline; network retrieval features are disabled. Upstream provenance and license live in `crates/hyper-viz/schemas`. + +Viewer conversion supports undirected documents, including an omitted `network-type` when incidences have no direction. It rejects `directed`, `asc`, and any incidence direction, weight, or attributes (including an explicitly empty attributes object). Errors identify the offending JSON Pointer. Duplicate declarations and incidence pairs are rejected at conversion; they can still be preserved by document interchange. + +Nodes/edges referenced only by incidences are inferred. Explicit isolated nodes and empty edges are retained in the graph, although empty edges have no drawable geometry. Integer `1` becomes viewer ID `i:1`; string `"1"` becomes `s:1`. Labels show original IDs. Integral numeric spellings denote the same identity. Scientific attribute keys are not interpreted as viewer coloring or status. + +Node/edge weights are rounded to finite `f32` during viewer conversion; overflow is an error. Their original numbers remain in the HIF document. Keep that document for scientific export—exporting a converted native graph does not reconstruct the original IDs or precision. + +Native-to-HIF export keeps native string IDs, memberships, weights, and attributes. Native display fields are preserved under the reserved `_hyper_viz` attribute on metadata/nodes/edges. A collision with that key, non-finite weights, duplicate IDs/memberships, or missing member references is an error. HIF-to-viewer conversion treats this reserved content as opaque metadata too. + +## Python installation + +The distribution is named `hypergraph-viz`; import it as `hyper_viz`. The Python extension depends on the Rust core, not Bevy. Requires standard CPython 3.10 or newer. Install a platform wheel with `python -m pip install hypergraph-viz`; a compatible wheel needs no Rust compiler. For local development, build with Rust 1.89 and maturin: + +```sh +python -m venv .venv +. .venv/bin/activate # Windows: .venv\Scripts\activate +python -m pip install maturin==1.9.6 +cd crates/hyper-viz-python +cargo run --bin stub_gen +maturin develop --locked +``` + +For wheel installation, run `maturin build --release --locked` in the same directory and install the resulting platform wheel from `target/wheels`. CI prepares Linux x86_64, macOS arm64/x86_64, and Windows x86_64 wheels. Version tags (`python-v`) publish tested wheels and a verified source archive through PyPI Trusted Publishing; ordinary CI runs do not publish. See the [Python release guide](PYTHON_RELEASE.md) for the one-time publisher setup and release procedure. Wheels use the CPython stable ABI starting at 3.10, not the free-threaded ABI. To build a standalone source archive (maintainer Python 3.11+), run `python scripts/releases/build_python_sdist.py --out dist` from the repository root with maturin on PATH; the helper excludes the root viewer package so the archive can build independently. + +```python +import hyper_viz + +document = hyper_viz.HifDocument.load("dataset.hif.json") +document.save("roundtrip.hif.json") +print(document.to_json()) + +# Install hypergraph-viz[viewer], then: +viewer = hyper_viz.show(document, projection="bipartite") +code = viewer.wait() # or viewer.close() to terminate +``` + +`HifDocument.from_json(raw)` accepts HIF JSON; `from_hypergraph_json(raw)` accepts native JSON. `to_hypergraph_json()` explicitly checks viewer compatibility. File arguments on document methods are strings; the launcher also accepts `pathlib.Path`. + +`HifValidationError` and `HifCompatibilityError` expose `location` and `reason`. I/O failures raise `OSError`. The launcher validates before launching and resolves an explicit executable, a version-matched installed `hypergraph-viz-viewer` binary, or `hyper` on PATH, in that order. Companion discovery uses installed RECORD metadata and works without virtual-environment activation. Missing/damaged installations raise `FileNotFoundError` with reinstall guidance; companion/core version mismatches raise `RuntimeError`. It never invokes a shell and inherits stdout/stderr. It creates an owned temporary snapshot, returns immediately, and cleans up automatically when the viewer exits. `wait()` returns nonzero exit codes without hiding diagnostics. A timeout preserves the running process and snapshot. `close()` terminates, waits five seconds, then kills if necessary. A context manager closes on exit. Dropping the handle keeps the viewer open; a background waiter retains ownership until exit. + +Install the desktop wheel with `python -m pip install --upgrade "hypergraph-viz[viewer]"`; no Rust compiler or checkout is needed. The companion is pinned to the core version. Linux desktop wheels require glibc 2.28+, macOS wheels target 11.0+, and Windows wheels target x86_64. A desktop session and a working graphics driver are required; graphics drivers remain system dependencies. For source installation, use `cargo install --path . --locked`. Nothing is downloaded on first launch. Live Python updates, notebook embedding, analysis/layout bindings, and direct scientific-library object adapters are outside this release. See the [notebook prototype roadmap](NOTEBOOK_ROADMAP.md) for the next anywidget canvas and WASM probes. + +## XGI and Julia + +The runnable [XGI example](../crates/hyper-viz-python/examples/xgi_interop.py) demonstrates scientific export, HIF round-trip, and optional viewer launch. Tests pin XGI 0.10.2. No XGI import is required to use `hyper_viz`. + +The [Julia example](../examples/hif_interop.jl) uses SimpleHypergraphs.jl and its HIF export. Run it in a Julia environment containing SimpleHypergraphs, then preserve its output with the Rust `hif_roundtrip` example or Python package. SimpleHypergraphs exports incidence weights, so viewer conversion rejects that output explicitly. Julia integration is an example; Julia is not a Python runtime dependency. + +## Development checks + +```sh +cargo test -p hyper-viz +cargo run -p hyper-viz-python --bin stub_gen +python -m pytest crates/hyper-viz-python/tests +python -m mypy --strict crates/hyper-viz-python/python/hyper_viz +``` + +The generated `_core.pyi` declares the native API. A CI regeneration diff catches drift; `py.typed` and annotated Python wrappers provide IDE and type-checker support. The wrappers are handwritten; stubs are generated from Rust binding declarations. diff --git a/docs/NOTEBOOK_ROADMAP.md b/docs/NOTEBOOK_ROADMAP.md new file mode 100644 index 0000000..a811c94 --- /dev/null +++ b/docs/NOTEBOOK_ROADMAP.md @@ -0,0 +1,134 @@ +# Notebook prototype: anywidget canvas and a small dataset + +Status: planned follow-up after desktop wheels. There is no `[notebook]` extra +or `hyper_viz.widget()` API in the current release. Desktop `show()` opens a +window on the kernel machine; it does not display inside a remote notebook. + +## Goal and first dataset + +Prove that a pip-installed widget can render a small compatible HIF document in +a notebook cell and exchange selections with Python. Use +[`fixtures/notebook-small.hif.json`](../fixtures/notebook-small.hif.json): + +- 7 nodes, including distinct integer `1` and string `"1"`, plus an isolated node. +- 4 edges: a pair, a triple, an overlapping triple, and an empty edge. +- 8 incidences; nested metadata, arbitrary attributes, and a node/edge weight. + +This fixture is also used for desktop graphics startup tests. It is synthetic; +it is not a performance benchmark or evidence of notebook compatibility. + +## Proposed user experience + +The following API is a design target, not runnable today: + +```sh +python -m pip install "hypergraph-viz[notebook]" +``` + +```python +import hyper_viz + +document = hyper_viz.HifDocument.load("notebook-small.hif.json") +view = hyper_viz.widget(document) +view # Inline notebook output; no desktop process or viewer wheel. +``` + +Keep `show()` explicitly desktop and `widget()` explicitly inline. The Python +kernel retains the original HIF document for export. An anywidget frontend +receives a compatible display snapshot and owns its canvas and interaction. + +## Step 1: establish widget transport and lifecycle + +Create a disposable prototype using anywidget and traitlets. Pin their actual +versions in the prototype environment and record Python, notebook, and browser +versions. Package the frontend ESM/CSS locally; consumers should need neither +Node.js nor external CDN access. + +1. Validate the fixture with `HifDocument` and obtain `to_hypergraph_json()`. + The existing Python package exposes native JSON, not headless scene/layout + bindings. Do not assume a projection or layout Python API already exists. +2. Draw a simple Canvas2D diagnostic with fixed, seeded coordinates. Show all + seven nodes, group membership, and an explicit list for the empty edge. + This is a transport/identity test, not the final 3D renderer. +3. Click a node or group and synchronize selected IDs back to Python. Highlight + a Python-driven selection in the frontend. Keep camera and pointer movement + local; do not send every animation frame through the kernel. +4. Display arbitrary attributes for the selection without interpreting attribute + names as status or coloring. Fetch original metadata from the retained + document when needed rather than treating display JSON as a lossless export. +5. Re-render the same widget and create two independent widgets. Dispose event + listeners, observers, animation loops, and graphics resources when a view is + removed. Closing one view must not corrupt another view of the same model. + +Use the type-prefixed native IDs as browser keys and map selections back to +original HIF identities in Python. Do not use row indexes or plain object keys +that collapse integer/string identities. For future large integer IDs, transmit +integer identity as a tagged decimal string rather than a JavaScript Number. + +## Step 2: test Bevy/WASM reuse before choosing the 3D frontend + +Run a separate feasibility probe; do not assume the desktop dependency graph +compiles for the browser. The current `hyper-viz-bevy` manifest selects visual +Bevy features and desktop window backends, including Wayland, while excluding +audio and gamepads. The core's dependency features also need inspection. + +```sh +rustup target add wasm32-unknown-unknown +cargo check -p hyper-viz --target wasm32-unknown-unknown +``` + +Record failures, including random-number generation and schema-validation +features. Keep HIF validation in the Python kernel for the first widget; a +browser build need not include the complete schema validator. If required, +separate portable scene/layout code from platform-specific dependencies through +explicit features rather than hiding incompatibility. + +Build a small browser entry point with a dedicated feature set and a canvas +owned by the widget. Test WebGL2 first for broad compatibility; assess WebGPU +separately. Verify shader/asset delivery, pointer and keyboard focus, resizing, +multiple canvases, and teardown. Confirm a second view can start after the first +is removed without retaining an old event loop or GPU context. + +Compare two concrete probes: + +| Probe | Evidence required | +|---|---| +| Bevy renderer compiled to WASM | Successfully render the fixture in the widget; demonstrate picking, resize, and clean disposal; measure cold/warm load and bundle size | +| Browser renderer with selected Rust algorithms in WASM | Same fixture and interaction checks; establish the work required for hulls, picking, and UI parity; identify algorithms that can run in a worker | + +Choose based on measured integration cost and behavior, not the assumption that +WASM is automatically faster or that a successful compile proves notebook DX. +WASM runs inside the frontend; it does not replace anywidget's kernel connection. + +## Step 3: verify environments and decide whether to productize + +Start with JupyterLab and VS Code notebooks, then test Colab and a remote Jupyter +kernel explicitly. Anywidget supports those hosts; Hyper compatibility still +has to be demonstrated in each one. Also test a fresh kernel, rerunning a cell, +multiple views, a kernel restart, and a document rejected by viewer compatibility. + +Acceptance criteria for this small prototype: + +- All nodes/edges are represented, including isolates and the empty edge list. +- Integer/string identity remains distinct in display and selection round-trips. +- Selecting an object exposes its metadata; scientific export remains unchanged. +- Python-to-browser and browser-to-Python selection updates work independently + for two widgets, without duplicate events or stale state after reruns. +- Bundled assets load with external networking disabled in the local Jupyter + test. Document any host-specific asset-delivery or content-security restrictions. +- Capture a screenshot and record cold/warm startup, bundle size, selection + response, and retained resources after repeated creation/disposal. These are + prototype observations, not a comparison with native benchmarks. + +Only after these checks define and publish the `[notebook]` extra, the widget API, +and supported host versions. Add binary buffers and workers when measured costs +justify them; preserve a bounded state/update protocol with sequence numbers. +Notebook support, standalone browser hosting, browser-side HIF validation, and +full desktop feature parity are separate delivery decisions. + +## References + +- [anywidget getting started](https://anywidget.dev/en/getting-started/) +- [Widget state, messages, and binary buffers](https://anywidget.dev/en/jupyter-widgets-the-good-parts/) +- [anywidget bundling](https://anywidget.dev/en/bundling/) +- [Bevy browser examples](https://bevy.org/examples/) diff --git a/docs/PYTHON_RELEASE.md b/docs/PYTHON_RELEASE.md new file mode 100644 index 0000000..7758a5f --- /dev/null +++ b/docs/PYTHON_RELEASE.md @@ -0,0 +1,112 @@ +# Publishing the Python packages + +`hypergraph-viz` provides the `hyper_viz` import, HIF bindings, typing, and a +launcher. `hypergraph-viz-viewer` provides the native desktop executable. +Both packages are published at `0.1.2`; `hypergraph-viz[viewer]` pins the companion to the core +version. Installing the core alone does not install Bevy or a viewer. + +PyPI treats hyphens and underscores as equivalent. The original `hyper-viz` / +`hyper_viz` distribution name was rejected as too similar to an existing project. +The distribution name does not change the Python import. + +## One-time PyPI setup + +Configure a GitHub Trusted Publisher for **each** project. For a new project, +add a pending publisher at . For an +existing project, use its publishing settings. + +| Field | Value | +|---|---| +| PyPI project names | `hypergraph-viz` and `hypergraph-viz-viewer` (separate entries) | +| GitHub owner | `atomicstrata` | +| Repository | `hyper` | +| Workflow filename | `python.yml` | +| Environment name | `pypi` | + +Pending publishers do not reserve names; the first successful upload creates the +project. Both publishers have been configured for this release. Create the +`pypi` GitHub environment too. The workflow requests `id-token: write` only in +the publish job; no long-lived API token is needed. Any configured environment +reviewers must approve that job before it runs. + +See PyPI's [pending publisher instructions](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/) +and [publishing instructions](https://docs.pypi.org/trusted-publishers/using-a-publisher/). + +## Release a version + +1. Validate the release commit with `python wheels and release`. It checks stubs, + Rust/Python tests, clean installs, standalone core source builds, four desktop + binary wheels, and installation through the viewer extra. Linux also captures + a real window using software Vulkan and Xvfb. macOS arm64 graphics startup was + checked locally; Windows and macOS Intel CI verify CLI startup and discovery, + without claiming graphics coverage on those systems. +2. Keep versions identical in `[workspace.package]` of `Cargo.toml`, `[project]` + of the root `pyproject.toml`, and `[project]` of + `crates/hyper-viz-python/pyproject.toml`. Regenerate `Cargo.lock`, update the + exact `[viewer]` dependency, and update release-tag documentation URLs. +3. Tag the validated commit. Prefer a merged release commit; an explicitly + selected validated PR commit can also be released. For `0.1.2`: + + ```sh + git tag python-v0.1.2 + git push origin python-v0.1.2 + ``` + +The tag workflow builds and tests that exact source again. Versions must match +`python-v`. All build and integration jobs gate publication. The +publisher uploads the companion first, then the core, so the extra's exact +viewer dependency is available when the core is released. Pull requests, branch +pushes, and manual runs never publish. + +The core ships CPython 3.10+ `abi3` wheels for Linux x86_64, macOS arm64/x86_64, +and Windows x86_64, plus a source archive. The source helper +(`scripts/releases/build_python_sdist.py`) stages only the core and bindings, +excluding the root desktop project and retaining locked dependencies. CI builds +and installs a wheel from this archive. + +The companion ships `py3-none` platform wheels containing a native executable; +there is no desktop source archive. Maturin installs it in the environment's +scripts directory and records its installed location. Linux desktop wheels +require glibc 2.28+, macOS requires 11.0+, and Windows targets x86_64. A desktop +session and working graphics driver remain required. Build from source with +`cargo install --path . --locked` on other supported Rust targets. Wheel checks +verify executable format/permissions, package identity, bundled licenses, and +the default 100 MiB PyPI file limit. Windows desktop builds link the C runtime +statically and verify that the executable imports no external Visual C++ runtime +DLL. The unpublished `python-v0.1.1` tag was stopped after this dependency was +found; it remains unchanged, and the corrected release uses `0.1.2`. + +## Verify the published packages + +Use a fresh standard CPython 3.10+ environment and the normal PyPI index: + +```sh +python -m venv /tmp/hyper-release-check +/tmp/hyper-release-check/bin/python -m pip install --only-binary=:all: "hypergraph-viz[viewer]==0.1.2" +/tmp/hyper-release-check/bin/python - <<'PY' +import os +import subprocess +from importlib.metadata import version +from hyper_viz import HifDocument +from hyper_viz._viewer import _resolve_executable + +assert version("hypergraph-viz") == version("hypergraph-viz-viewer") == "0.1.2" +document = HifDocument.from_json('{"incidences":[{"node":"a","edge":"e"}]}') +assert 's:a' in document.to_hypergraph_json() +os.environ["PATH"] = "" # Discovery works without activation. +subprocess.run([_resolve_executable(None), "--version"], check=True) +PY +``` + +Adapt the environment path for Windows. Confirm both PyPI pages render their +README and list all four wheels; the core also lists its source archive. On a +local desktop, launch `hyper_viz.show(document)` and close its window to check +actual graphics. A remote notebook kernel does not display this window in the +browser; see the [notebook prototype roadmap](NOTEBOOK_ROADMAP.md). + +PyPI releases cannot be overwritten. If an uploaded release is unusable, yank +it and publish a higher version. Do not reuse or move its tag. If an upload +partially succeeds, inspect the published files and retry only missing files; +the workflow does not silently skip existing distributions. If only the +companion upload succeeds, finish the matching core upload from the tested +artifacts before announcing the extra. diff --git a/docs/benchmark-protocol.md b/docs/benchmark-protocol.md new file mode 100644 index 0000000..5c97df3 --- /dev/null +++ b/docs/benchmark-protocol.md @@ -0,0 +1,93 @@ +# Hyper comparative benchmark protocol + +Proposed experiment, October 2, 2026. The executed October 3 subset, its measurements and remaining coverage gaps are recorded in the [measured comparison](benchmarks/ecosystem-2026-10-03/README.md). This document defines future measurements; it does not report an executed comparison. Existing evidence and the ecosystem table are in [Hyper ecosystem comparison and roadmap](ecosystem-comparison.md). + +The objective is to determine which users and workloads Hyper serves well enough to justify adoption, and which roadmap changes improve that outcome. Compare responsiveness, exact membership interpretation, and integration effort alongside raw rendering throughput. + +## Baselines and comparison tracks + +| Track | Baselines | Controlled question | +|---|---|---| +| Native/group exploration | Hyper, HyperGodot, HGPolyVis, HNX Widget | Can users identify memberships and overlapping groups accurately, and inspect their data? | +| Spatial node-link rendering | Hyper with explicit incidence view, 3d-force-graph, Graphia | How responsive is navigation with the same projected nodes and links? | +| Scientific figure generation | HNX, XGI; optionally HGX/Julia workflows | How quickly can the same small/medium hypergraph become an interpretable export? | +| 2D web exploration | Cytoscape.js and Sigma.js with incidence adapters | What does the simpler browser alternative provide at the same membership workload? | + +Comparisons across 2D and 3D should evaluate task accuracy, time, and usability. They should not imply identical GPU rendering work. Similarly, static Matplotlib figure generation is not the same metric as continuously rendering a native window. + +Use Windows for a baseline involving the current HGPolyVis binary, or mark it unavailable on the tested OS. Keep that separate from a performance failure. Record native vs browser runtime and exact versions. + +## Datasets + +Keep a canonical membership list, stable IDs, metadata, dataset license, and SHA-256 for every input. Verify each adapter against those records before timing. HIF files and Hyper JSON are different schemas; renaming keys or accepting an empty deserialization is not valid conversion. + +| Workload | Proposed size or source | What it tests | +|---|---|---| +| Correctness fixture | 30 vertices / 15 groups, arity 1–8; overlapping, duplicate-member, isolated and empty cases where allowed | Membership, ID stability, unsupported-input behavior, and dual/incidence interpretation | +| Moderate synthetic | 1k vertices / 2k groups; fixed seed; several arity and overlap distributions | Everyday exploration and layout cost | +| Larger synthetic | 10k vertices / 10k groups; control total memberships separately | Rendering/layout growth without changing graph semantics implicitly | +| High arity | Hold vertex count fixed; sweep maximum arity through 10, 100, 1k and near-total membership | Hull accuracy and projection expansion | +| Real scientific data | Pin suitable [XGI-DATA](https://github.com/xgi-org/xgi-data) or [HGX datasets](https://hgx-team.github.io/hypergraphx-data/) with metadata and license | Evidence beyond generated graphs; preserve documented dataset semantics | +| Mathlib atlas | Existing pinned export: 9,384 vertices; 44,906 total hyperedges; 118,066 memberships | Reproduce the current real-world stress case and investigate large-group hull overhead | +| Incremental updates | Add/remove/change 1% of records with stable IDs and fixed timing | Scene replacement latency, position stability, selection and focus preservation | + +For size comparisons, report `(vertices, hyperedges, memberships, maximum arity)` and actual scene nodes, rendered segments, hulls, and triangles. Do not use node count alone. Keep scientific simplices and hyperedges distinct when loading datasets. + +The Mathlib atlas includes both import pairs and source-plus-import groups. Preserve both across adapters, and report their counts separately. A comparison using only the pairwise import graph is a different workload. In a bipartite projection the existing atlas has 54,290 scene nodes and 118,066 incidence links; the star-centroid tour has 9,384 scene nodes. + +Do not run unrestricted clique expansion on the full atlas: its 8,532-member umbrella group alone yields 36,393,246 pairs before accounting for other groups. Test clique projection on bounded fixtures and label that limit. This pair count is `k*(k-1)/2`, not an observed renderer capacity. + +## Measurement modes + +Separate these modes rather than combining them into one FPS score: + +1. Parse and validate input, then project it; record time and peak memory. +2. Compute layout with a fixed seed, positions and parameter record; report iteration time and time to an explicit convergence criterion. +3. Render frozen common coordinates with basic nodes/segments and identical camera framing, resolution and pixel ratio where possible. +4. Add equivalent group geometry where each tool supports it; record sampling/approximation settings. +5. Run live layout with a stated number of steps per frame. +6. Enable picking, labels, UI panels and metadata inspection; exercise orbit/pan, zoom, click and search. +7. Apply incremental updates and record visual continuity and latency. + +For representation-specific layouts, also run a separate “best documented settings” comparison. A common-coordinate render test isolates rendering but does not evaluate layout quality. Fix camera views when comparing hulls on/off; the historical tour changes the view between phases. + +Run at least five repetitions after a documented warmup. Randomize tool/mode order, record power/thermal conditions, background load and vsync, and report every run as well as the aggregate. Pin commits, builds, OS, CPU, GPU, RAM, graphics backend, browser, device pixel ratio, window size, labels, line budgets, opacity and hull settings. Keep screenshot readback and video capture outside live timing. + +Wall-clock frame intervals, GPU timestamps, layout-step duration, and input-to-feedback latency measure different stages. Name each precisely. Report both percentile frame intervals and average throughput; `1000 / median frame ms` is not average FPS. Include startup stalls, memory, failed loads and omitted objects rather than hiding them in a success score. + +## Task evaluation + +Use identical target records and ground truth across tools. Counterbalance tool order and provide comparable practice. Proposed pilot: 5–8 participants; use findings to refine the study rather than treating that sample as population-level proof. + +| Task | Ground truth and measure | Roadmap decision | +|---|---|---| +| Find a named or attribute-matched vertex | Correct record; elapsed time; query/result latency | Search and metadata support | +| Explain one hyperedge | Exact member set; completeness and false-member count | Hull interpretation and inspector accuracy | +| Identify overlap between groups | Exact intersection; accuracy and elapsed time | Focus, linked tables, complementary 2D view | +| Explain provenance or quantitative attributes | Correct attribute values and source records | Preservation of opaque metadata and attribute styling | +| Track a selected record through updates | Correct identity; selection retention; displacement of unchanged vertices | Stable-ID reload behavior and saved positions | +| Export a selected neighborhood | Correct records, IDs, metadata and memberships after reload | Interoperability and sharing | + +Approximate surfaces can exclude genuine members or geometrically enclose nonmembers. Evaluate the membership list and selection highlight as authoritative; record visual ambiguity separately. A plausible-looking screenshot is not correctness evidence. + +## Results table to populate + +The following is a template, not measured data. `Pending` must remain until a compatible run exists; use `Unsupported` or `Unavailable on tested OS` for those conditions. + +| Tool and pinned version | Dataset and representation | Load time | Frozen p50 / p95 | Live p50 / p95 | Input latency p95 | Peak memory | Membership task accuracy | Evidence | +|---|---|---|---|---|---|---|---|---| +| Hyper current HEAD | Mathlib star-centroid, interactive controls enabled | Pending | Pending | Pending | Pending | Pending | Pending | Fresh runs required; historical tour differs | +| Hyper | Canonical moderate fixture, incidence | Pending | Pending | Pending | Pending | Pending | Pending | Pin commit and settings | +| 3d-force-graph | Same projected fixture, same coordinates | Pending | Pending | Pending | Pending | Pending | Pending | Pin browser and rendering settings | +| Graphia | Same projected fixture | Pending | Pending | Pending | Pending | Pending | Pending | Record data adapter and runtime | +| HNX Widget | Canonical moderate hypergraph | Pending | Pending | Pending | Pending | Pending | Pending | Verify documented notebook environment | +| HyperGodot | Canonical moderate hypergraph | Pending | Pending | Pending | Pending | Pending | Pending | Representation differs; evaluate tasks | +| HGPolyVis | Canonical moderate hypergraph | Pending | Pending | Pending | Pending | Pending | Pending | Requires compatible OS; evaluate tasks | + +For HNX/XGI plotting, add a separate figure-generation table with parse/layout/draw/export time, peak memory, and task accuracy. Fill PAOHVis temporal rows only after choosing a genuine temporal dataset and shared comparison task. + +## Initial acceptance criteria + +Proposed targets for a representative moderate workload: p95 interactive frame interval at or below 33.3 ms and p95 input-to-visible-feedback below 100 ms, with correct member inspection and preserved metadata. These are project goals, not guarantees, industry standards, or current results. Agree on hardware and scene settings before using them as release gates. + +For the Mathlib stress case, publish its envelope rather than forcing every visual mode into the same target. If hulls must be sampled, selectively updated or hidden, display that limitation and benchmark the resulting mode explicitly. Optimize the measured bottleneck and then repeat the same controlled workload. diff --git a/docs/benchmarks/ecosystem-2026-10-03/README.md b/docs/benchmarks/ecosystem-2026-10-03/README.md new file mode 100644 index 0000000..4a1d56f --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/README.md @@ -0,0 +1,145 @@ +# Measured ecosystem comparison — October 3, 2026 + +Exploratory results on an Apple M5 Pro / 24 GiB Mac: 445 measured attempts across 89 configurations. Five attempts per case unless noted. The same graph records and seeded coordinates are used through explicit adapters. Scientific figures, 2D browser views and 3D native rendering have different work and are separate tracks. + +Read the [method and limitations](methodology.md), [environment](environment.json), [complete CSV](summary.csv), [structured summary](summary.json) and [raw evidence](evidence/). The [harness guide](../../../scripts/benchmarks/README.md) provides pinned dependencies and rerun commands. + +## Findings to use for roadmap decisions + +Model fidelity is the clearest differentiator in this run: exact group identity, isolated nodes and empty groups need explicit checks at adapter boundaries. Rendering results are conditional on completion. Hyper had intermittent whole-job stalls, including one on a small fixture; their cause remains unresolved. The source audit found a quadratic hub-status lookup worth profiling, but it has not isolated the cause of those stalls. + +Sigma camera measurements use its public cached redraw path. Earlier full-refresh camera measurements were discarded and replaced; the diagnostic evidence is retained separately. Comparisons below keep representations and timing boundaries explicit. + +## Scientific figure workflows + +Times exclude the separately measured top-level tool import, file I/O and JSON parsing. Setup creates the artists and includes any lazy drawing-module initialization; raster draws the Agg canvas. PNG compression is a separate stage. A 120-second timeout covers the whole worker. HGX uses its stock 12-round smoothing, HNX rubber-band outlines, XGI convex hulls. + +| Dataset | Tool | Completed / attempts | Exact model records | Construct ms | Artist setup ms | Raster ms | PNG ms | Worker peak MiB | +|---|---|---|---|---|---|---|---|---| +| moderate | hypergraphx | 0/5 | no / unavailable | — | — | — | — | — | +| moderate | hypernetx | 5/5 | yes | 159.92 | 2,717.87 | 1,089.55 | 28.12 | 242.42 | +| moderate | xgi | 5/5 | yes | 4.71 | 214.57 | 452.82 | 37.70 | 144.00 | +| scientific | hypergraphx | 5/5 | no / unavailable | 1.09 | 13,764.90 | 396.79 | 40.78 | 181.56 | +| scientific | hypernetx | 5/5 | yes | 27.49 | 500.68 | 154.87 | 35.97 | 228.19 | +| scientific | xgi | 5/5 | yes | 1.37 | 23.96 | 28.11 | 33.24 | 132.25 | + +XGI preserves all model records in every tested input. HNX omits isolated vertices and empty groups. HGX identifies groups by member tuple and merges parallel groups: Diseasome becomes 481 groups instead of 903, and the arity-1,000 fixture becomes one group instead of 200. Its lossy timings cannot rank equivalent work. Original records stored in opaque metadata do not repair those native identities. + +## Browser incidence rendering + +All incidences are rendered, forces and labels are off, and the camera moves. Values describe rAF callback cadence; they are not GPU durations or verified presented frames. Average callbacks/s uses total elapsed intervals. The p95 range spans per-run p95 values. “Completed” requires sampling and the shared update to finish within the 90-second job budget. + +| Dataset | Tool | Completed / attempts | p50 ms | p95 ms | p95 run range ms | Avg callbacks/s | Update command ms | Failed stage | +|---|---|---|---|---|---|---|---|---| +| high-arity-10 | 3d-force-graph | 5/5 | 8.30 | 8.50 | 8.40–8.70 | 120.00 | 0.80 | — | +| high-arity-10 | cytoscape | 5/5 | 8.30 | 9.10 | 9.00–16.70 | 120.00 | 45.70 | — | +| high-arity-10 | sigma | 5/5 | 16.70 | 17.50 | 17.50–17.50 | 60.00 | 4.10 | — | +| high-arity-100 | 3d-force-graph | 5/5 | 41.70 | 50.00 | 41.80–50.40 | 24.71 | 5.80 | — | +| high-arity-100 | cytoscape | 5/5 | 8.30 | 9.20 | 9.10–9.20 | 120.00 | 2,450.00 | — | +| high-arity-100 | sigma | 5/5 | 16.70 | 17.50 | 17.40–17.50 | 60.00 | 13.90 | — | +| high-arity-1000 | 3d-force-graph | 0/5 | — | — | —–— | — | — | incremental update, timed sampling | +| high-arity-1000 | cytoscape | 0/5 | — | — | —–— | — | — | timed sampling | +| high-arity-1000 | sigma | 5/5 | 16.70 | 50.00 | 50.00–50.10 | 43.16 | 167.80 | — | +| large | 3d-force-graph | 2/5 | 191.65 | 203.62 | 201.33–205.91 | 5.22 | 48.35 | incremental update | +| large | cytoscape | 4/5 | 8.30 | 9.30 | 9.21–9.30 | 120.02 | 51,638.90 | incremental update | +| large | sigma | 5/5 | 16.70 | 17.50 | 17.40–17.50 | 60.00 | 1,079.80 | — | +| mathlib | 3d-force-graph | 5/5 | 133.30 | 150.00 | 134.00–191.32 | 7.37 | 73.30 | — | +| mathlib | cytoscape | 0/5 | — | — | —–— | — | — | incremental update, timed sampling | +| mathlib | sigma | 5/5 | 16.70 | 17.50 | 17.40–17.60 | 60.00 | 13,242.90 | — | +| moderate | 3d-force-graph | 5/5 | 25.10 | 33.40 | 33.40–34.11 | 35.40 | 4.20 | — | +| moderate | cytoscape | 5/5 | 8.30 | 9.21 | 9.20–10.00 | 120.00 | 1,342.60 | — | +| moderate | sigma | 5/5 | 16.70 | 17.50 | 17.40–17.60 | 60.00 | 42.20 | — | +| scientific | 3d-force-graph | 5/5 | 8.30 | 8.40 | 8.40–8.70 | 120.00 | 0.80 | — | +| scientific | cytoscape | 5/5 | 8.30 | 9.20 | 9.10–10.00 | 120.00 | 37.50 | — | +| scientific | sigma | 5/5 | 16.70 | 17.50 | 17.40–17.50 | 60.00 | 7.50 | — | + +Sigma uses WebGL 2D, Cytoscape uses Canvas2D, and 3d-force-graph uses perspective spheres and WebGL. The corrected Sigma block and original Cytoscape block recorded different small-case callback cadences (roughly 60 versus 120 callbacks/s); display refresh was not locked, so their rate ratio cannot rank rendering efficiency. No link sampling is applied. Update command timings include different adapter commit strategies and sometimes unchanged-record work; see the method. A failed batch has no completed rendering score; its timeout is not a single-frame measurement. + +## Hyper native renderer + +Physical 1920 × 1080, AutoVsync, 120 warmup and 300 recorded application frames. HUD and picking remain installed. Basic modes disable labels; `native-interactive` enables the default Capped policy (count threshold 250, with selected/hovered exemptions). Frozen modes use fixed coordinates; live modes perform one layout step per frame. All lines are enabled. Hulls cap input at 24 vertices and therefore approximate large groups. + +| Track / dataset | Mode | Completed / attempts | p50 app interval ms | p95 app interval ms | p95 run range ms | Avg app frames/s | Update commit ms | Next app interval ms | Peak MiB | +|---|---|---|---|---|---|---|---|---|---| +| native / high-arity-10 | frozen | 4/5 | 8.34 | 8.99 | 8.61–17.45 | 119.83 | 0.24 | 8.40 | 235.17 | +| native / high-arity-10 | frozen+hulls | 5/5 | 8.67 | 17.07 | 16.94–26.34 | 64.62 | 0.28 | 16.41 | 260.62 | +| native / high-arity-100 | frozen | 5/5 | 8.32 | 9.53 | 8.70–17.02 | 117.86 | 0.87 | 8.28 | 255.80 | +| native / high-arity-100 | frozen+hulls | 5/5 | 16.72 | 33.51 | 25.30–33.73 | 49.45 | 0.58 | 42.72 | 271.91 | +| native / high-arity-1000 | frozen | 5/5 | 12.68 | 17.11 | 11.72–20.55 | 72.00 | 6.16 | 18.40 | 411.09 | +| native / high-arity-1000 | frozen+hulls | 5/5 | 33.35 | 41.78 | 34.80–53.86 | 29.78 | 8.97 | 56.79 | 427.14 | +| native / large | frozen | 4/5 | 61.37 | 68.10 | 66.62–82.65 | 16.35 | 5.77 | 62.77 | 700.15 | +| native / large | frozen+hulls | 4/5 | 270.73 | 375.32 | 347.01–455.02 | 3.52 | 6.91 | 302.92 | 1,006.70 | +| native / mathlib | frozen | 0/5 | — | — | —–— | — | — | — | — | +| native / mathlib | frozen+hulls | 0/5 | — | — | —–— | — | — | — | — | +| native / moderate | frozen | 5/5 | 8.31 | 9.68 | 8.73–17.10 | 117.15 | 0.92 | 8.48 | 281.80 | +| native / moderate | frozen+hulls | 5/5 | 49.98 | 62.08 | 51.96–74.23 | 19.57 | 0.79 | 58.94 | 358.38 | +| native / moderate | live | 5/5 | 16.66 | 17.01 | 9.09–17.23 | 60.00 | 0.91 | 16.76 | 287.14 | +| native / moderate | live+hulls | 3/5 | 21.91 | 37.07 | 34.94–43.49 | 41.35 | 0.87 | 25.38 | 384.80 | +| native / scientific | frozen | 5/5 | 8.41 | 9.08 | 8.66–17.76 | 119.37 | 0.36 | 8.50 | 237.25 | +| native / scientific | frozen+hulls | 5/5 | 8.33 | 9.16 | 8.70–17.22 | 119.60 | 0.39 | 8.54 | 257.50 | +| native-interactive / mathlib | frozen+hulls | 5/5 | 162.18 | 219.91 | 191.63–257.63 | 5.93 | 10.35 | 195.36 | 880.30 | +| native-interactive / mathlib | live+hulls | 5/5 | 97.19 | 171.71 | 166.70–176.77 | 9.08 | 9.24 | 97.10 | 935.11 | +| native-interactive / moderate | frozen+hulls | 5/5 | 50.04 | 64.93 | 53.90–95.84 | 19.05 | 0.37 | 50.90 | 298.53 | +| native-interactive / moderate | live+hulls | 5/5 | 18.00 | 30.84 | 30.10–37.94 | 50.06 | 0.33 | 20.60 | 321.88 | +| native-star / mathlib | frozen | 5/5 | 18.16 | 25.95 | 25.78–33.88 | 47.75 | 9.55 | 31.53 | 674.31 | +| native-star / mathlib | frozen+hulls | 5/5 | 187.35 | 216.60 | 174.74–253.40 | 5.39 | 12.26 | 205.94 | 746.03 | +| native-star / mathlib | live | 5/5 | 21.78 | 24.80 | 22.54–27.75 | 45.10 | 7.62 | 42.01 | 666.34 | +| native-star / mathlib | live+hulls | 4/5 | 100.76 | 179.87 | 173.58–220.51 | 8.85 | 9.59 | 101.56 | 1,057.62 | + +`native` is bipartite incidence rendering; `native-star` and `native-interactive` use group/star rendering. Star draws pair lines for dyadic groups and hulls for larger groups; its scene has no spring links. Star without hulls is an ablation that hides larger groups, not a full-group rendering score. Update commit and the next application interval exclude file-watch polling and parse/I/O; they are not input-to-visible latency. Native and browser settings, geometry and vsync differ, so these tables do not establish a native-versus-browser speed ratio. + +## Hyper parsing, conversion and geometry + +Fresh release processes, one discarded warmup and five measured runs. HIF schema validation is offline. Solver timings use the bipartite scene; hull timings use the production 24-vertex cap. Worker RSS includes multiple retained documents for correctness checks. + +| Dataset | Native parse ms | HIF parse / validate ms | HIF conversion ms | Projection ms | All hulls CPU ms | Layout step p50 ms | Peak MiB | +|---|---|---|---|---|---|---|---| +| high-arity-10 | 1.13 | 3.87 | 3.36 | 0.15 | 1.28 | 0.90 | 34.56 | +| high-arity-100 | 1.48 | 13.00 | 16.00 | 0.61 | 37.93 | 1.17 | 91.78 | +| high-arity-1000 | 4.78 | 104.58 | 147.10 | 4.32 | 44.92 | 1.76 | 677.53 | +| large | 14.19 | 60.41 | 85.06 | 4.09 | 78.29 | 31.90 | 423.45 | +| mathlib | 36.26 | 141.10 | 228.00 | 15.37 | 10.77 | 97.66 | 1,020.78 | +| moderate | 2.12 | 11.52 | 12.63 | 0.63 | 15.59 | 3.53 | 80.77 | +| scientific | 0.77 | 3.08 | 2.42 | 0.19 | 0.11 | 2.06 | 30.08 | + +## Desktop alternatives + +Graphia measures stock headless import/save including process startup, then independently checks saved memberships and node attributes. The adapter retains incidence topology and arbitrary `attrs`, but omits document metadata and top-level native label/kind/status/weight fields; XYZ are attributes, and Graphia saves this graph as directed. These are import workflow timings, not rendering FPS. + +| Dataset | Tool | Completed / attempts | Import + save ms | Exact incidence records | Peak MiB | +|---|---|---|---|---|---| +| mathlib | Graphia | 5/5 | 1,490.57 | yes | 656.16 | +| moderate | Graphia | 5/5 | 390.86 | yes | 130.73 | +| scientific | Graphia | 5/5 | 332.84 | yes | 106.38 | + +HyperGodot measures its production 2D group drawing with camera motion, fixed XY coordinates and stock labels/UI. It disables vsync and uses different node/hull geometry. Its five repetitions share one process, with a new warmup each. A disposable source copy removes a missing unused autoload. Original IDs are mapped to safe tokens/line-order group IDs; weights and group categories are normalized for this fixture. Stock HiDPI is enabled and the physical render texture is verified at 1920 × 1080; earlier clamped-window timings are excluded. + +| Dataset | Tool | Completed / attempts | p50 ms | p95 ms | Avg frames/s | Whole-session peak MiB | Render target pixels | +|---|---|---|---|---|---|---|---| +| moderate | HyperGodot | 5/5 | 8.33 | 10.37 | 118.82 | 336.55 | [1920.0, 1080.0] | + +## Coverage and remaining experiments + +| Alternative | Measured scope / reason for no comparable score | +|---|---| +| HyperNetX, XGI, Hypergraphx | Model construction and scientific figure workflow; no GUI FPS claim | +| 3d-force-graph, Cytoscape.js, Sigma | Frozen full-incidence camera motion and scripted update | +| HyperGodot | Native 2D group drawing on the moderate dataset | +| Graphia | Headless incidence import/save; common render coordinates not established | +| HNX Widget | Installation and dictionary-input smoke test; public position API does not freeze all hubs; isolated vertices omitted | +| HGPolyVis | Supplied Windows-only executable; requires a Windows run | +| SimpleHypergraphs.jl | Julia runtime verified; package timings not collected | +| PAOHVis | Needs a temporal dataset and temporal exploration task | +| Gephi | Outside this automated render harness; requires a separate plugin/task comparison | +| egui_graphs | Embedding framework reference; no matched application benchmark | + +Human task accuracy, dense-diagram readability, layout convergence, GPU completion, input-to-visible latency and other machines remain unmeasured. Metadata retention does not establish that a viewer exposes or filters it. + +## Roadmap implications + +1. **Interoperability:** keep original HIF documents and stable group identities through scientific workflows. The identity-loss cases demonstrate why an incidence adapter needs correctness gates before timing. +2. **Inspection and filtering:** expose provenance, arbitrary attributes and membership before expanding visual effects. Rendering every relation does not establish readability or a successful scientific task. +3. **Responsiveness:** profile the confirmed quadratic hub-status scan in `animation::node_motion` first; the existing `GraphLayout::hub_status` cache offers a direct replacement. Then use the frozen/live split and update traces to separate solver, hull rebuild/upload and redraw costs. Add a controlled input-to-visible study before advertising latency; keep hull approximation and line budgets explicit. +4. **Installation:** ship verified wheels and a clear separately installed viewer path. The widget and desktop packaging probes show that dependency/version friction can block evaluation before performance matters. + +These priorities are interpretations of this exploratory run, not proof of adoption or a universal winner. diff --git a/docs/benchmarks/ecosystem-2026-10-03/checksums.json b/docs/benchmarks/ecosystem-2026-10-03/checksums.json new file mode 100644 index 0000000..68a0f4b --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/checksums.json @@ -0,0 +1,261 @@ +{ + "scope": "Archived evidence/report and current harness sources. Post-run orchestration/checker changes are described in driver-notes; source hashes are not executable hashes.", + "files": { + "docs/benchmarks/ecosystem-2026-10-03/README.md": { + "bytes": 14591, + "sha256": "dcceb0f7da5088c8a87ac6f4afc486853b50d7bf9f42ad284cf047c151157833" + }, + "docs/benchmarks/ecosystem-2026-10-03/environment.json": { + "bytes": 1212, + "sha256": "cb90d952d05e2ae99dad25ea957787aac004cbb7a607937e67136859f5368dc9" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/browser-final-results.json.gz": { + "bytes": 188391, + "sha256": "52608622447d584b4d209abb68413619ece87035028459352c89b725441d735b" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/browser-original-refresh-diagnostic.json.gz": { + "bytes": 208781, + "sha256": "67534c8a1095aea38368684e71af600ef5f335ef1fe7d15b627da903af06821c" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/browser-sigma-final-results.json.gz": { + "bytes": 67512, + "sha256": "d50457c4652e484dcf5d7061c24ef5799072c9bc87ae08cad73275d2fa46f704" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/commands.json": { + "bytes": 2325, + "sha256": "366a6304abcf156491b4e383f9d459f26b8786fe7eb6ef738e1d61ec7f657788" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/core-metadata.json": { + "bytes": 3571, + "sha256": "8c19e0b6d42a12c0745dd1c20fbdb569f14b533625e60a7c74fab706cf68d29b" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/core-raw.json": { + "bytes": 96253, + "sha256": "5d8b8484b0af09751cffa57754108ac2e65b3f4e55727006a480c9d22cf2526d" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/datasets-manifest.json": { + "bytes": 7835, + "sha256": "2e744417247ca069826892d521ce52873d53c05b643e632471eb32974ca74676" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/driver-notes.json": { + "bytes": 1058, + "sha256": "c3aca996c8ddcec6857459b0ad56f1e648821f775c63b627bad1ec009902f895" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/graphia-metadata.json": { + "bytes": 616, + "sha256": "b0935fa38bdc9177febe12cc5045036a6346c63de840f7052b6d12f798d2f4fc" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/graphia-raw.json": { + "bytes": 36247, + "sha256": "20da52eb93962539fe34c63e2b8c59bbe194a93058394d030e9381cd3c5c48fa" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-clamped-window-diagnostic-metadata.json": { + "bytes": 616, + "sha256": "a21fa48b29d78345badf98fab334c78784076616bbed101c9aee1663e75a7afc" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-clamped-window-diagnostic.json.gz": { + "bytes": 5717, + "sha256": "6fc788366f150f425b32c1f84d7e3381751a020448130fabfb77a526719fa566" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-fullscreen-unmatched-diagnostic.json": { + "bytes": 1732, + "sha256": "64b4359dd433e036ae0d617bd089d6f57d1cacebe97ef9f6750f8ac30d4c9c0d" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-hidpi-untimed-smoke.json": { + "bytes": 1777, + "sha256": "dc1b358cd28c3c362505a65f9a7eeb50db7ab63b1eaa389624642b8af4c365b0" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-metadata.json": { + "bytes": 629, + "sha256": "a97a68c25f14f59ca7314b453b8f83095ff971481d5810d6537073f4dc262315" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-raw.json": { + "bytes": 32699, + "sha256": "f6b2b5bd649fe0fb2b954cc81d4ba064cb5bb4b1e7bd645390c75f7f694f7840" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/native-interactive-metadata.json": { + "bytes": 2032, + "sha256": "e02f50894d1b5137f01ae4f7b75be3b79c06462c30cc824d11817eb852a2890d" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/native-interactive-raw.json.gz": { + "bytes": 50481, + "sha256": "88f053f2d6623e709922e6b17356cc4ed556514a3f0f1adda1fda780fdc882ed" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/native-metadata.json": { + "bytes": 6029, + "sha256": "6b01fc8119aa4959e6293ac0f5afb0c942f20fa95bd1fd0fb68068c2611bcc93" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/native-raw.json.gz": { + "bytes": 118689, + "sha256": "0a0c76018be4cfe9298c1bad82e2c7f871c850286d4989cb8b42ec34a90aaa72" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/native-star-metadata.json": { + "bytes": 2019, + "sha256": "e8f75577be230de47b95c3c218460167f1f56161f8561b7d833244778cdd65c2" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/native-star-raw.json.gz": { + "bytes": 46501, + "sha256": "cd092e726250b6992dbcb4dcd2c0ef258646ecc295908d3598d5402396004c0c" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/native-untimed-render-validation.png": { + "bytes": 510563, + "sha256": "61a985c99d2a21eba481ccae8edfd52040227201d5e507720d302abeb1bda764" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/native-untimed-smoke.json": { + "bytes": 1400, + "sha256": "94ec1199fef9e1206534a6a8defb9a6e8d82f472029fa9a4cbff86b0916f9734" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-consolidation.json": { + "bytes": 526, + "sha256": "2ed07a3dd0ccd539ae2ae733e315f2bf8e4a12282c0e50348937444b4fcf25b5" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-hnx-repeat-metadata.json": { + "bytes": 7338, + "sha256": "038a22118eb9dd36d8184ff3550647b6317cacf5354349f344425a3ea7d84e2d" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-independent-validation.json": { + "bytes": 23168, + "sha256": "23d18fccb05b2092dd0f661f70a19732a206779885b5dba1cdf43257bab4b921" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-metadata.json": { + "bytes": 29585, + "sha256": "54b0f7e8a232ce09c760f85db30113837caaf2c40f8d0ed992e30a135c4e9664" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-raw.json": { + "bytes": 376790, + "sha256": "fb6767860a5a73c3c10b9b2f179cc3ca8544b56b67ac626f95c748f57f271af9" + }, + "docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-requirements.txt": { + "bytes": 667, + "sha256": "aeacc7048d9ecb01c1ede17a3241333f461979e552a1f08bb03a71d9e91671c3" + }, + "docs/benchmarks/ecosystem-2026-10-03/methodology.md": { + "bytes": 11797, + "sha256": "3abe052f5b76b9e8878b67fab68ae391c49882f79b82f17a79bf3d702744bd28" + }, + "docs/benchmarks/ecosystem-2026-10-03/summary.csv": { + "bytes": 16427, + "sha256": "ce10068d3fcd4133cace912edf4f3781e0bd64d8b41da27470997f179065aff6" + }, + "docs/benchmarks/ecosystem-2026-10-03/summary.json": { + "bytes": 47789, + "sha256": "9ab9a22d0dc0c0b6a1a6bcdf66c262e594f0fa7b18bf023da4dfcf45569867fe" + }, + "docs/benchmarks/ecosystem-2026-10-03/verification.json": { + "bytes": 1322, + "sha256": "c70a02602278dcc1bf626892ea298fd17f6d3e607b6ba6fc9b6528aa40ec1fb3" + }, + "scripts/benchmarks/README.md": { + "bytes": 7072, + "sha256": "aece56aec8795346f04d7f45ef0eb27e28503181af402e16cbfc00a00715b01f" + }, + "scripts/benchmarks/archive.py": { + "bytes": 4066, + "sha256": "27221b5432ed9f3d4fc3b5c0c1937f4e2c95085a39618005e3b3a099577d94c7" + }, + "scripts/benchmarks/browser/.gitignore": { + "bytes": 14, + "sha256": "4d56952b0fb13bf8f9b6c13a6d4c34a075bac3af447636a1df4335d7576e2f97" + }, + "scripts/benchmarks/browser/README.md": { + "bytes": 6733, + "sha256": "a504e2aa0be2be9bc0a908f71b1926f22cc9fb64b85756d55d2c0b388740261d" + }, + "scripts/benchmarks/browser/client.js": { + "bytes": 9135, + "sha256": "ff4e3750718e56846859b9e30bfd47e32f895b15bdb4401c4697efbebf3046f2" + }, + "scripts/benchmarks/browser/package-lock.json": { + "bytes": 31634, + "sha256": "fccc8a26441c4df9db48cb0aebd9fa626c15640314db6a8cb0c8fb5376ce1323" + }, + "scripts/benchmarks/browser/package.json": { + "bytes": 392, + "sha256": "99e72eaa73793ac1d80088228aa70a6d3dbd9b82a07325b9312c9e4ba9fea199" + }, + "scripts/benchmarks/browser/run.mjs": { + "bytes": 12546, + "sha256": "e7187bc9a1f40b25b89d5c7bae89b0441d2364d19f39eed3a95cbfbe25df3ee1" + }, + "scripts/benchmarks/consolidate_browser.py": { + "bytes": 2750, + "sha256": "e628f5c229ab12aaa2deb820182edc1734055a2b3fc7c5225cf9f2b5eea175cf" + }, + "scripts/benchmarks/datasets.py": { + "bytes": 6228, + "sha256": "7f9c04608bb704888d48d3f129c4d72870d5828b48dd15d4daada44f50b3b6ec" + }, + "scripts/benchmarks/desktop/README.md": { + "bytes": 6673, + "sha256": "a1069f2666b0062d19f3ae824175ec8eaf5c74a4837fd3321fa93b7336530fc7" + }, + "scripts/benchmarks/desktop/hypergodot_runner.gd": { + "bytes": 165, + "sha256": "3bd7106031f836f9edfb2948a682aa56d51d63711ba941a2d8dff36de4f4a1ee" + }, + "scripts/benchmarks/desktop/hypergodot_scene.gd": { + "bytes": 5256, + "sha256": "b892a729cc5b6d39d2cf9e4126cb5db2b613d4f53fb423139934eb0a2ab06d33" + }, + "scripts/benchmarks/desktop/prepare_graphia.py": { + "bytes": 2737, + "sha256": "7f9a526435f4027aa91dd65c8de2259ecefbb11d037f30d08a252d933fc51f2d" + }, + "scripts/benchmarks/desktop/prepare_hypergodot.py": { + "bytes": 3570, + "sha256": "164369cfa58434ef17b86cb6f48d18f8ab703ec737493e46cf79b7fd6092b080" + }, + "scripts/benchmarks/desktop/run.py": { + "bytes": 6657, + "sha256": "ba17ec4678b542869ba7adde26781ac20f32d54567665f436d3cb10bb01214c1" + }, + "scripts/benchmarks/report.py": { + "bytes": 13149, + "sha256": "12b8e7d51b0b608f9405e53edb81431f206f405fdea7ee15c4d4e610edb940f9" + }, + "scripts/benchmarks/restore_evidence.py": { + "bytes": 1559, + "sha256": "de6100a70c979e856d2cf1aaf19531761732074937bb2a2195d084e619c7d23b" + }, + "scripts/benchmarks/run_local.py": { + "bytes": 4579, + "sha256": "4f9ffc76228eefeeb0cde294e6c211ea2d78921ad437beb92a1926c91704412d" + }, + "scripts/benchmarks/scientific.py": { + "bytes": 16774, + "sha256": "ea1cfaaa0e18cbef578d2bf8b8eae89e8e895fe30216e058f3fb0111579d861c" + }, + "scripts/benchmarks/summarize.py": { + "bytes": 8399, + "sha256": "7ba803999b8c52e5c0c96bdf3073048829981d1692b346f3be098c4eff44caa1" + }, + "scripts/benchmarks/test_datasets.py": { + "bytes": 1315, + "sha256": "03356d021a8e658a855e21df636ec71ecac43619539cf0a54d752aa0eec01e6f" + }, + "scripts/benchmarks/test_scientific.py": { + "bytes": 1395, + "sha256": "286aa2cbac32b015d51acbe720d7885be24eaa7de1941bc89a111a0540f4818a" + }, + "scripts/benchmarks/test_summarize.py": { + "bytes": 1691, + "sha256": "9142eb967812697705f49b7635089606dfa4d915b62a8a81d52d49f9efa46327" + }, + "scripts/benchmarks/verify_scientific.py": { + "bytes": 1081, + "sha256": "51e3159287a8c286cea7bef1567e63b35704564b19afe928cb7c01dba9063ae6" + }, + "crates/hyper-viz/examples/ecosystem_core.rs": { + "bytes": 6133, + "sha256": "ff7a5e844af2bedc4e4b2a3bf8dfde12881c4f1514e14a4d79f863cca33a3f49" + }, + "crates/hyper-viz-bevy/src/benchmark.rs": { + "bytes": 11281, + "sha256": "e5cd1317753101e6455d4ab96090670a1dc6b4ee0ab76e48b6a349c62720b057" + }, + "crates/hyper-viz-bevy/src/bin/ecosystem_benchmark.rs": { + "bytes": 52, + "sha256": "844bb550a9bc51c534686b2a402f4ab80b6aa4eaf2d271650ab0b5ac33f1d8b6" + } + } +} diff --git a/docs/benchmarks/ecosystem-2026-10-03/environment.json b/docs/benchmarks/ecosystem-2026-10-03/environment.json new file mode 100644 index 0000000..82031f9 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/environment.json @@ -0,0 +1,30 @@ +{ + "recorded_unix": 1791051214.3176162, + "platform": "macOS-27.0-arm64-arm-64bit", + "cpu": "Apple M5 Pro", + "cpu_cores": 15, + "gpu": "Apple M5 Pro,16 cores,Metal", + "ram_bytes": 25769803776, + "load_average": [ + 3.15576171875, + 4.658203125, + 4.48046875 + ], + "power": "Now drawing from 'AC Power'", + "thermal": "pmset telemetry unavailable", + "background": "no parallel benchmark/build; ordinary desktop applications uncontrolled", + "display": "variable refresh; actual intervals recorded; no forced60Hz", + "commit": "33c34621a898b6c9d1d11ad4ddae3ad29ccf1855", + "rust": "rustc 1.89.0 (29483883e 2025-08-04)", + "power_checkpoint": { + "recorded_unix": 1791060411.970849, + "lowpowermode": 0, + "display_sleep_minutes": 0, + "prevent_user_idle_display_sleep": 1, + "prevent_user_idle_system_sleep": 1, + "user_is_active": 1, + "note": "pmset checkpoint after a small native batch timeout; display/system idle sleep is prevented. Native timeout causes remain unresolved." + }, + "finished_utc": "2026-10-03T21:37:05.987003+00:00", + "final_hypergodot": "Stock HiDPI enabled; actual Metal render texture1920x1080; display scale2; clamped-window and fullscreen probes excluded." +} diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/browser-final-results.json.gz b/docs/benchmarks/ecosystem-2026-10-03/evidence/browser-final-results.json.gz new file mode 100644 index 0000000..527cfd3 Binary files /dev/null and b/docs/benchmarks/ecosystem-2026-10-03/evidence/browser-final-results.json.gz differ diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/browser-original-refresh-diagnostic.json.gz b/docs/benchmarks/ecosystem-2026-10-03/evidence/browser-original-refresh-diagnostic.json.gz new file mode 100644 index 0000000..3595397 Binary files /dev/null and b/docs/benchmarks/ecosystem-2026-10-03/evidence/browser-original-refresh-diagnostic.json.gz differ diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/browser-sigma-final-results.json.gz b/docs/benchmarks/ecosystem-2026-10-03/evidence/browser-sigma-final-results.json.gz new file mode 100644 index 0000000..50b14d9 Binary files /dev/null and b/docs/benchmarks/ecosystem-2026-10-03/evidence/browser-sigma-final-results.json.gz differ diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/commands.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/commands.json new file mode 100644 index 0000000..03eea76 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/commands.json @@ -0,0 +1,70 @@ +{ + "note": "Commands run sequentially. Browser original full-refresh Sigma cases and early scientific checker probes are excluded; replacements are described in consolidation evidence.", + "core": [ + "python3", + "scripts/benchmarks/run_local.py", + "core", + "--output", + "target/benchmark/core" + ], + "native_incidence": [ + "python3", + "scripts/benchmarks/run_local.py", + "native", + "--output", + "target/benchmark/native" + ], + "native_star": [ + "python3", + "scripts/benchmarks/run_local.py", + "native", + "--datasets", + "mathlib", + "--projection", + "star", + "--live-datasets", + "mathlib", + "--output", + "target/benchmark/native-star" + ], + "native_default_policy": [ + "python3", + "scripts/benchmarks/run_local.py", + "native", + "--datasets", + "moderate,mathlib", + "--projection", + "star", + "--live-datasets", + "moderate,mathlib", + "--labels", + "--only-hulls", + "--output", + "target/benchmark/native-interactive" + ], + "browser_final_method": "browser/run.mjs with seven datasets, camera-motion, warmup120, samples300, repetitions5, job-timeout-ms90000; original70 Cytoscape/3D rows retained and35 corrected Sigma rows substituted after exact environment/protocol/hash checks.", + "browser_replacement": [ + "node", + "scripts/benchmarks/browser/run.mjs", + "--tools", + "sigma", + "--datasets", + "moderate,large,high-arity-10,high-arity-100,high-arity-1000,scientific,mathlib", + "--modes", + "camera-motion", + "--warmup", + "120", + "--samples", + "300", + "--repetitions", + "5", + "--job-timeout-ms", + "90000", + "--output", + "target/benchmark/browser-sigma-final", + "--executable", + "/Users/bregy/Library/Caches/ms-playwright/chromium-1243/chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing" + ], + "desktop": "desktop/run.py with pinned installed executable; HyperGodot prepared moderate project; Graphia moderate,scientific,mathlib; timeout180.", + "scientific": "scientific.py --run main suite plus all HNX correctness/moderate/scientific construct+plot cases repeated after checker/alpha correction. Final raw consolidation records the replacement sources; construct/figure code is unchanged by later orchestration hardening." +} diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/core-metadata.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/core-metadata.json new file mode 100644 index 0000000..1b4e74b --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/core-metadata.json @@ -0,0 +1,272 @@ +{ + "track": "core", + "seed": 20261003, + "repetitions": 5, + "fresh_process_per_run": true, + "warmup": "5 solver steps plus1 discarded run per dataset", + "schedule": [ + [ + -1, + "high-arity-100", + false, + false + ], + [ + -1, + "large", + false, + false + ], + [ + -1, + "scientific", + false, + false + ], + [ + -1, + "high-arity-10", + false, + false + ], + [ + -1, + "mathlib", + false, + false + ], + [ + -1, + "high-arity-1000", + false, + false + ], + [ + -1, + "moderate", + false, + false + ], + [ + 0, + "scientific", + false, + false + ], + [ + 0, + "large", + false, + false + ], + [ + 0, + "mathlib", + false, + false + ], + [ + 0, + "high-arity-100", + false, + false + ], + [ + 0, + "moderate", + false, + false + ], + [ + 0, + "high-arity-1000", + false, + false + ], + [ + 0, + "high-arity-10", + false, + false + ], + [ + 1, + "large", + false, + false + ], + [ + 1, + "high-arity-10", + false, + false + ], + [ + 1, + "scientific", + false, + false + ], + [ + 1, + "high-arity-1000", + false, + false + ], + [ + 1, + "high-arity-100", + false, + false + ], + [ + 1, + "mathlib", + false, + false + ], + [ + 1, + "moderate", + false, + false + ], + [ + 2, + "large", + false, + false + ], + [ + 2, + "high-arity-100", + false, + false + ], + [ + 2, + "high-arity-10", + false, + false + ], + [ + 2, + "moderate", + false, + false + ], + [ + 2, + "scientific", + false, + false + ], + [ + 2, + "high-arity-1000", + false, + false + ], + [ + 2, + "mathlib", + false, + false + ], + [ + 3, + "moderate", + false, + false + ], + [ + 3, + "high-arity-10", + false, + false + ], + [ + 3, + "scientific", + false, + false + ], + [ + 3, + "mathlib", + false, + false + ], + [ + 3, + "large", + false, + false + ], + [ + 3, + "high-arity-100", + false, + false + ], + [ + 3, + "high-arity-1000", + false, + false + ], + [ + 4, + "large", + false, + false + ], + [ + 4, + "high-arity-100", + false, + false + ], + [ + 4, + "high-arity-1000", + false, + false + ], + [ + 4, + "mathlib", + false, + false + ], + [ + 4, + "moderate", + false, + false + ], + [ + 4, + "high-arity-10", + false, + false + ], + [ + 4, + "scientific", + false, + false + ] + ], + "commit": "33c34621a898b6c9d1d11ad4ddae3ad29ccf1855", + "binary_sha256": "6473abf41da7656c061a51a803de141a61d598a20f4fa7234b736e56f950da2d", + "conditions": "M5Pro 24GiB macOS27; lowpowermode0; no parallel benchmark/build; ordinary desktop background uncontrolled; thermal telemetry unavailable; variable display cadence", + "peak_memory": "/usr/bin/time -l process RSS high-water; bytes on macOS; includes startup", + "build_profile": "release", + "projection": "bipartite", + "labels": false, + "only_hulls": false, + "live_datasets": [ + "moderate" + ] +} diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/core-raw.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/core-raw.json new file mode 100644 index 0000000..3e79c43 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/core-raw.json @@ -0,0 +1,2228 @@ +[ + { + "dataset": "high-arity-100", + "hulls": false, + "live": false, + "repetition": -1, + "warmup": true, + "order": 0, + "started_unix": 1791056764.4984431, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "status": "ok", + "returncode": 0, + "stderr": " 0.78 real 0.13 user 0.01 sys\n 83689472 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 5322 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 1 voluntary context switches\n 22 involuntary context switches\n 2301348576 instructions retired\n 580635486 cycles elapsed\n 58818992 peak memory footprint\n", + "process_peak_rss_bytes": 83689472, + "measurements": { + "all_hulls_cpu_ms": 38.164667, + "bipartite_projection_ms": 0.714666, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-100", + "hif_parse_validate_ms": 19.44175, + "hif_viewer_conversion_ms": 18.410125, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.094125, + 1.089167, + 1.125208, + 1.1425, + 1.131, + 1.149541, + 1.224584, + 1.204584, + 1.2119170000000001, + 1.220292, + 1.22, + 1.150416, + 1.1658330000000001, + 1.192708, + 1.300958, + 1.25775, + 1.279292, + 1.238834, + 1.223959, + 1.2055 + ], + "links": 20000, + "native_parse_ms": 3.490833, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.7915098667144775 + }, + { + "dataset": "large", + "hulls": false, + "live": false, + "repetition": -1, + "warmup": true, + "order": 1, + "started_unix": 1791056765.2901518, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "status": "ok", + "returncode": 0, + "stderr": " 1.22 real 1.16 user 0.04 sys\n 393494528 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 24830 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 1 voluntary context switches\n 116 involuntary context switches\n 19177307785 instructions retired\n 5426172475 cycles elapsed\n 263422504 peak memory footprint\n", + "process_peak_rss_bytes": 393494528, + "measurements": { + "all_hulls_cpu_ms": 77.488209, + "bipartite_projection_ms": 4.0425, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "large", + "hif_parse_validate_ms": 61.105708, + "hif_viewer_conversion_ms": 88.38550000000001, + "hull_vertex_cap": 24, + "hulls": 8000, + "hyperedges": 10000, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 30.463708999999998, + 31.119417, + 31.683166999999997, + 31.892709, + 31.962667, + 32.270708, + 31.800750000000004, + 32.102208, + 31.757708, + 31.683208999999998, + 31.342042, + 31.086083000000002, + 31.802540999999998, + 31.218, + 31.585583, + 31.698959, + 31.317083000000004, + 30.899875, + 30.382458, + 30.203750000000003 + ], + "links": 66000, + "native_parse_ms": 14.965084, + "tool": "hyper-core", + "vertices": 10000 + }, + "process_wall_seconds": 1.2248420715332031 + }, + { + "dataset": "scientific", + "hulls": false, + "live": false, + "repetition": -1, + "warmup": true, + "order": 2, + "started_unix": 1791056766.515264, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "status": "ok", + "returncode": 0, + "stderr": " 0.06 real 0.05 user 0.00 sys\n 31604736 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 2142 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 1 voluntary context switches\n 12 involuntary context switches\n 1238890620 instructions retired\n 272802334 cycles elapsed\n 29688216 peak memory footprint\n", + "process_peak_rss_bytes": 31604736, + "measurements": { + "all_hulls_cpu_ms": 0.129875, + "bipartite_projection_ms": 0.17875000000000002, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "diseasome", + "hif_parse_validate_ms": 3.044625, + "hif_viewer_conversion_ms": 2.356458, + "hull_vertex_cap": 24, + "hulls": 163, + "hyperedges": 903, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 2.029458, + 1.948291, + 1.994417, + 2.013084, + 2.080167, + 2.053834, + 1.9953340000000002, + 1.95025, + 2.0328749999999998, + 2.016875, + 2.0478750000000003, + 2.0237089999999998, + 1.944542, + 1.9532500000000002, + 1.9919590000000003, + 2.063166, + 1.9584580000000003, + 1.973125, + 1.9489159999999999, + 1.9649580000000002 + ], + "links": 1550, + "native_parse_ms": 0.779667, + "tool": "hyper-core", + "vertices": 516 + }, + "process_wall_seconds": 0.06586694717407227 + }, + { + "dataset": "high-arity-10", + "hulls": false, + "live": false, + "repetition": -1, + "warmup": true, + "order": 3, + "started_unix": 1791056766.581378, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "status": "ok", + "returncode": 0, + "stderr": " 0.04 real 0.03 user 0.00 sys\n 32866304 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 2220 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 3 voluntary context switches\n 11 involuntary context switches\n 812613291 instructions retired\n 184770842 cycles elapsed\n 30949784 peak memory footprint\n", + "process_peak_rss_bytes": 32866304, + "measurements": { + "all_hulls_cpu_ms": 1.237209, + "bipartite_projection_ms": 0.15287499999999998, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-10", + "hif_parse_validate_ms": 3.777209, + "hif_viewer_conversion_ms": 3.2998749999999997, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 0.980083, + 0.9127080000000001, + 0.868416, + 0.856959, + 0.8676670000000001, + 0.868958, + 0.8971250000000001, + 0.892875, + 0.882084, + 0.891625, + 0.902833, + 0.963083, + 0.9065409999999999, + 0.874167, + 0.864792, + 0.88475, + 0.90325, + 0.8983749999999999, + 0.907166, + 0.917417 + ], + "links": 2000, + "native_parse_ms": 1.1333339999999998, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.04602813720703125 + }, + { + "dataset": "mathlib", + "hulls": false, + "live": false, + "repetition": -1, + "warmup": true, + "order": 4, + "started_unix": 1791056766.6276832, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "status": "ok", + "returncode": 0, + "stderr": " 3.36 real 3.21 user 0.13 sys\n 1052426240 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 71355 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 322 involuntary context switches\n 51195957371 instructions retired\n 14957538951 cycles elapsed\n 656229552 peak memory footprint\n", + "process_peak_rss_bytes": 1052426240, + "measurements": { + "all_hulls_cpu_ms": 10.575833, + "bipartite_projection_ms": 16.342916, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "mathlib-d13f23b723b8a846827a245b89c10fc7d3f11612", + "hif_parse_validate_ms": 152.139625, + "hif_viewer_conversion_ms": 227.148417, + "hull_vertex_cap": 24, + "hulls": 7550, + "hyperedges": 44906, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 102.08608299999999, + 100.89874999999999, + 95.977292, + 94.133709, + 94.985292, + 96.062, + 95.98925, + 95.76750000000001, + 95.491125, + 95.56483300000001, + 96.722833, + 96.082542, + 96.4555, + 96.56058300000001, + 97.029667, + 98.65391699999999, + 99.592292, + 99.949125, + 100.404625, + 101.191667 + ], + "links": 118066, + "native_parse_ms": 37.035916, + "tool": "hyper-core", + "vertices": 9384 + }, + "process_wall_seconds": 3.379786968231201 + }, + { + "dataset": "high-arity-1000", + "hulls": false, + "live": false, + "repetition": -1, + "warmup": true, + "order": 5, + "started_unix": 1791056770.007838, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "status": "ok", + "returncode": 0, + "stderr": " 0.64 real 0.57 user 0.06 sys\n 710672384 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 45678 page reclaims\n 9 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 1 voluntary context switches\n 65 involuntary context switches\n 14020719314 instructions retired\n 2847505190 cycles elapsed\n 409912256 peak memory footprint\n", + "process_peak_rss_bytes": 710672384, + "measurements": { + "all_hulls_cpu_ms": 44.523208000000004, + "bipartite_projection_ms": 4.221667, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-1000", + "hif_parse_validate_ms": 104.391667, + "hif_viewer_conversion_ms": 149.5675, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.423459, + 1.357458, + 1.681333, + 1.958125, + 1.9099169999999999, + 1.880334, + 1.910166, + 1.777125, + 1.729875, + 1.660334, + 1.7262499999999998, + 1.612334, + 1.654417, + 0.9840840000000001, + 1.254167, + 1.749666, + 1.945583, + 2.167583, + 2.089708, + 1.912167 + ], + "links": 200000, + "native_parse_ms": 4.814875, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.6489620208740234 + }, + { + "dataset": "moderate", + "hulls": false, + "live": false, + "repetition": -1, + "warmup": true, + "order": 6, + "started_unix": 1791056770.657172, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "status": "ok", + "returncode": 0, + "stderr": " 0.16 real 0.15 user 0.00 sys\n 84770816 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 5505 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 1 voluntary context switches\n 22 involuntary context switches\n 2753180332 instructions retired\n 703502751 cycles elapsed\n 57885080 peak memory footprint\n", + "process_peak_rss_bytes": 84770816, + "measurements": { + "all_hulls_cpu_ms": 15.60275, + "bipartite_projection_ms": 0.629167, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "moderate", + "hif_parse_validate_ms": 11.59775, + "hif_viewer_conversion_ms": 13.155167, + "hull_vertex_cap": 24, + "hulls": 1600, + "hyperedges": 2000, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 3.5751250000000003, + 3.4767919999999997, + 3.5266249999999997, + 3.518083, + 3.523792, + 3.49925, + 3.423334, + 3.5834170000000003, + 3.602541, + 3.3785, + 3.6035, + 3.658, + 3.5397499999999997, + 3.530208, + 3.841167, + 3.74225, + 3.642208, + 3.48675, + 3.472625, + 3.473583 + ], + "links": 13200, + "native_parse_ms": 2.076708, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.16530299186706543 + }, + { + "dataset": "scientific", + "hulls": false, + "live": false, + "repetition": 0, + "warmup": false, + "order": 7, + "started_unix": 1791056770.822816, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "status": "ok", + "returncode": 0, + "stderr": " 0.06 real 0.05 user 0.00 sys\n 31490048 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 2135 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 13 involuntary context switches\n 1236719605 instructions retired\n 271511866 cycles elapsed\n 29573528 peak memory footprint\n", + "process_peak_rss_bytes": 31490048, + "measurements": { + "all_hulls_cpu_ms": 0.11825000000000001, + "bipartite_projection_ms": 0.17024999999999998, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "diseasome", + "hif_parse_validate_ms": 2.971417, + "hif_viewer_conversion_ms": 2.431292, + "hull_vertex_cap": 24, + "hulls": 163, + "hyperedges": 903, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 2.014834, + 2.017208, + 1.9755829999999999, + 2.019791, + 2.0614589999999997, + 1.9723330000000001, + 2.091667, + 1.891916, + 2.017417, + 2.004708, + 1.987042, + 2.079334, + 1.986959, + 2.105875, + 2.033792, + 1.9624590000000002, + 2.093625, + 2.00425, + 1.9923749999999998, + 2.042959 + ], + "links": 1550, + "native_parse_ms": 0.766541, + "tool": "hyper-core", + "vertices": 516 + }, + "process_wall_seconds": 0.0646510124206543 + }, + { + "dataset": "large", + "hulls": false, + "live": false, + "repetition": 0, + "warmup": false, + "order": 8, + "started_unix": 1791056770.887826, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "status": "ok", + "returncode": 0, + "stderr": " 1.21 real 1.16 user 0.05 sys\n 444022784 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 29851 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 123 involuntary context switches\n 19203761708 instructions retired\n 5409642205 cycles elapsed\n 265257656 peak memory footprint\n", + "process_peak_rss_bytes": 444022784, + "measurements": { + "all_hulls_cpu_ms": 78.63962500000001, + "bipartite_projection_ms": 3.974333, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "large", + "hif_parse_validate_ms": 60.409125, + "hif_viewer_conversion_ms": 85.064167, + "hull_vertex_cap": 24, + "hulls": 8000, + "hyperedges": 10000, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 30.658458, + 30.877041000000002, + 31.479708000000002, + 32.290082999999996, + 32.555708, + 32.171625, + 32.247375000000005, + 31.944124999999996, + 31.970584000000002, + 31.750916999999998, + 31.590209, + 31.352209, + 31.484834000000003, + 31.331290999999997, + 31.857292, + 31.700667000000003, + 31.550458000000003, + 31.175749999999997, + 30.812458, + 30.552625 + ], + "links": 66000, + "native_parse_ms": 14.400834, + "tool": "hyper-core", + "vertices": 10000 + }, + "process_wall_seconds": 1.2228751182556152 + }, + { + "dataset": "mathlib", + "hulls": false, + "live": false, + "repetition": 0, + "warmup": false, + "order": 9, + "started_unix": 1791056772.111129, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "status": "ok", + "returncode": 0, + "stderr": " 3.37 real 3.23 user 0.11 sys\n 1037795328 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 71499 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 368 involuntary context switches\n 51185330585 instructions retired\n 14907879438 cycles elapsed\n 647267528 peak memory footprint\n", + "process_peak_rss_bytes": 1037795328, + "measurements": { + "all_hulls_cpu_ms": 10.779166, + "bipartite_projection_ms": 15.366, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "mathlib-d13f23b723b8a846827a245b89c10fc7d3f11612", + "hif_parse_validate_ms": 142.14974999999998, + "hif_viewer_conversion_ms": 227.017083, + "hull_vertex_cap": 24, + "hulls": 7550, + "hyperedges": 44906, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 101.593875, + 100.90341699999999, + 96.20575000000001, + 93.8205, + 95.282709, + 96.14295800000001, + 97.357958, + 96.261209, + 97.664959, + 96.015875, + 95.949625, + 96.395375, + 98.756, + 96.49704200000001, + 97.50716600000001, + 99.365416, + 102.784333, + 101.644667, + 101.3555, + 101.80629200000001 + ], + "links": 118066, + "native_parse_ms": 36.296042, + "tool": "hyper-core", + "vertices": 9384 + }, + "process_wall_seconds": 3.382607936859131 + }, + { + "dataset": "high-arity-100", + "hulls": false, + "live": false, + "repetition": 0, + "warmup": false, + "order": 10, + "started_unix": 1791056775.494227, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "status": "ok", + "returncode": 0, + "stderr": " 0.13 real 0.12 user 0.00 sys\n 96239616 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 6088 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 21 involuntary context switches\n 2294610618 instructions retired\n 574982540 cycles elapsed\n 58769816 peak memory footprint\n", + "process_peak_rss_bytes": 96239616, + "measurements": { + "all_hulls_cpu_ms": 37.677417, + "bipartite_projection_ms": 0.616291, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-100", + "hif_parse_validate_ms": 12.656208000000001, + "hif_viewer_conversion_ms": 15.926167000000001, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.04375, + 1.041917, + 1.062417, + 1.07875, + 1.122333, + 1.034083, + 1.067083, + 1.093625, + 1.19725, + 1.164334, + 1.167875, + 1.174917, + 1.2094580000000001, + 1.196167, + 1.161625, + 1.179375, + 1.2466249999999999, + 1.237833, + 1.213083, + 1.209417 + ], + "links": 20000, + "native_parse_ms": 1.464667, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.13308119773864746 + }, + { + "dataset": "moderate", + "hulls": false, + "live": false, + "repetition": 0, + "warmup": false, + "order": 11, + "started_unix": 1791056775.6277409, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "status": "ok", + "returncode": 0, + "stderr": " 0.15 real 0.15 user 0.00 sys\n 84377600 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 5481 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 22 involuntary context switches\n 2753181916 instructions retired\n 702022560 cycles elapsed\n 57278896 peak memory footprint\n", + "process_peak_rss_bytes": 84377600, + "measurements": { + "all_hulls_cpu_ms": 15.489125, + "bipartite_projection_ms": 0.621792, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "moderate", + "hif_parse_validate_ms": 11.245167, + "hif_viewer_conversion_ms": 12.448, + "hull_vertex_cap": 24, + "hulls": 1600, + "hyperedges": 2000, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 3.460084, + 3.6327909999999997, + 3.592125, + 3.394583, + 3.674458, + 3.6225, + 3.3990829999999996, + 3.5776250000000003, + 3.681917, + 3.415042, + 3.4954169999999998, + 3.5982499999999997, + 3.436, + 3.4837919999999998, + 3.581084, + 3.410875, + 3.457708, + 3.527916, + 3.40925, + 3.451041 + ], + "links": 13200, + "native_parse_ms": 2.1425, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.16211915016174316 + }, + { + "dataset": "high-arity-1000", + "hulls": false, + "live": false, + "repetition": 0, + "warmup": false, + "order": 12, + "started_unix": 1791056775.7903302, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "status": "ok", + "returncode": 0, + "stderr": " 0.64 real 0.57 user 0.06 sys\n 716865536 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 46064 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 62 involuntary context switches\n 14018128045 instructions retired\n 2851044428 cycles elapsed\n 415876032 peak memory footprint\n", + "process_peak_rss_bytes": 716865536, + "measurements": { + "all_hulls_cpu_ms": 44.921375, + "bipartite_projection_ms": 4.283959, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-1000", + "hif_parse_validate_ms": 104.577791, + "hif_viewer_conversion_ms": 147.932167, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.4075, + 1.429542, + 1.6869999999999998, + 1.893458, + 1.9012499999999999, + 1.914, + 1.9465420000000002, + 1.8067090000000001, + 1.675041, + 1.6306669999999999, + 1.771459, + 1.620041, + 1.565792, + 0.9898750000000001, + 1.199042, + 1.6706670000000001, + 1.9606659999999998, + 2.175375, + 2.079917, + 1.9749160000000001 + ], + "links": 200000, + "native_parse_ms": 4.741750000000001, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.6508150100708008 + }, + { + "dataset": "high-arity-10", + "hulls": false, + "live": false, + "repetition": 0, + "warmup": false, + "order": 13, + "started_unix": 1791056776.4416552, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "status": "ok", + "returncode": 0, + "stderr": " 0.04 real 0.03 user 0.00 sys\n 36241408 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 2425 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 1 voluntary context switches\n 12 involuntary context switches\n 816302347 instructions retired\n 184768424 cycles elapsed\n 34308504 peak memory footprint\n", + "process_peak_rss_bytes": 36241408, + "measurements": { + "all_hulls_cpu_ms": 1.266958, + "bipartite_projection_ms": 0.15075, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-10", + "hif_parse_validate_ms": 3.767458, + "hif_viewer_conversion_ms": 3.348583, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 0.858459, + 0.8436659999999999, + 0.846208, + 0.873167, + 0.974208, + 0.892916, + 0.8965829999999999, + 0.8914580000000001, + 0.898833, + 0.897333, + 1.0048329999999999, + 0.929875, + 0.900083, + 0.898541, + 0.915708, + 0.918292, + 0.922125, + 0.905958, + 0.901167, + 0.922708 + ], + "links": 2000, + "native_parse_ms": 1.1250419999999999, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.04558205604553223 + }, + { + "dataset": "large", + "hulls": false, + "live": false, + "repetition": 1, + "warmup": false, + "order": 14, + "started_unix": 1791056776.487712, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "status": "ok", + "returncode": 0, + "stderr": " 1.22 real 1.16 user 0.04 sys\n 437108736 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 29426 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 129 involuntary context switches\n 19196824481 instructions retired\n 5414862351 cycles elapsed\n 265159352 peak memory footprint\n", + "process_peak_rss_bytes": 437108736, + "measurements": { + "all_hulls_cpu_ms": 77.723625, + "bipartite_projection_ms": 4.1249579999999995, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "large", + "hif_parse_validate_ms": 60.33325, + "hif_viewer_conversion_ms": 84.08125, + "hull_vertex_cap": 24, + "hulls": 8000, + "hyperedges": 10000, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 30.816875, + 31.377957999999996, + 31.7885, + 32.201833, + 32.459959, + 32.559999999999995, + 32.379625000000004, + 32.051583, + 32.234249999999996, + 31.968292, + 31.494750000000003, + 31.522624999999998, + 31.985375000000005, + 31.724292000000002, + 31.822999999999997, + 31.403958000000003, + 31.199708, + 31.277167000000002, + 30.972, + 30.7135 + ], + "links": 66000, + "native_parse_ms": 14.187625, + "tool": "hyper-core", + "vertices": 10000 + }, + "process_wall_seconds": 1.2263832092285156 + }, + { + "dataset": "high-arity-10", + "hulls": false, + "live": false, + "repetition": 1, + "warmup": false, + "order": 15, + "started_unix": 1791056777.7146292, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "status": "ok", + "returncode": 0, + "stderr": " 0.04 real 0.03 user 0.00 sys\n 36356096 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 2432 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 13 involuntary context switches\n 816894957 instructions retired\n 186179021 cycles elapsed\n 34423192 peak memory footprint\n", + "process_peak_rss_bytes": 36356096, + "measurements": { + "all_hulls_cpu_ms": 1.271958, + "bipartite_projection_ms": 0.156459, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-10", + "hif_parse_validate_ms": 3.872959, + "hif_viewer_conversion_ms": 3.35375, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 0.856375, + 0.842958, + 0.847875, + 0.894334, + 0.965666, + 0.8861249999999999, + 0.898792, + 0.8957080000000001, + 0.900833, + 0.902084, + 1.008375, + 0.921916, + 0.9170419999999999, + 0.9111670000000001, + 0.936292, + 1.021084, + 0.945959, + 0.9145, + 0.909083, + 0.92525 + ], + "links": 2000, + "native_parse_ms": 1.12825, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.04611086845397949 + }, + { + "dataset": "scientific", + "hulls": false, + "live": false, + "repetition": 1, + "warmup": false, + "order": 16, + "started_unix": 1791056777.7612429, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "status": "ok", + "returncode": 0, + "stderr": " 0.06 real 0.06 user 0.00 sys\n 31539200 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 2139 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 15 involuntary context switches\n 1235798349 instructions retired\n 275906191 cycles elapsed\n 29622680 peak memory footprint\n", + "process_peak_rss_bytes": 31539200, + "measurements": { + "all_hulls_cpu_ms": 0.112917, + "bipartite_projection_ms": 0.17200000000000001, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "diseasome", + "hif_parse_validate_ms": 3.079375, + "hif_viewer_conversion_ms": 2.402417, + "hull_vertex_cap": 24, + "hulls": 163, + "hyperedges": 903, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.9135, + 2.0326250000000003, + 1.9889579999999998, + 2.045583, + 2.1138749999999997, + 1.985792, + 2.0256250000000002, + 2.005416, + 2.066542, + 2.153916, + 1.9999580000000001, + 2.0860000000000003, + 2.0098339999999997, + 2.072042, + 2.0875, + 1.976083, + 2.069458, + 2.08825, + 2.078375, + 2.092 + ], + "links": 1550, + "native_parse_ms": 0.76725, + "tool": "hyper-core", + "vertices": 516 + }, + "process_wall_seconds": 0.0657811164855957 + }, + { + "dataset": "high-arity-1000", + "hulls": false, + "live": false, + "repetition": 1, + "warmup": false, + "order": 17, + "started_unix": 1791056777.8275578, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "status": "ok", + "returncode": 0, + "stderr": " 0.64 real 0.58 user 0.05 sys\n 582877184 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 37886 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 67 involuntary context switches\n 13953982552 instructions retired\n 2847836903 cycles elapsed\n 382878344 peak memory footprint\n", + "process_peak_rss_bytes": 582877184, + "measurements": { + "all_hulls_cpu_ms": 45.002458000000004, + "bipartite_projection_ms": 4.31825, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-1000", + "hif_parse_validate_ms": 109.044083, + "hif_viewer_conversion_ms": 150.786417, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.399583, + 1.4475410000000002, + 1.722375, + 1.879042, + 2.008375, + 1.917709, + 1.9219169999999999, + 1.780416, + 1.6807079999999999, + 1.633542, + 1.72425, + 1.601375, + 1.531833, + 1.01325, + 1.186917, + 1.70675, + 1.96575, + 2.176834, + 2.054125, + 1.9911249999999998 + ], + "links": 200000, + "native_parse_ms": 4.810083, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.6470222473144531 + }, + { + "dataset": "high-arity-100", + "hulls": false, + "live": false, + "repetition": 1, + "warmup": false, + "order": 18, + "started_unix": 1791056778.475208, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "status": "ok", + "returncode": 0, + "stderr": " 0.13 real 0.12 user 0.00 sys\n 98516992 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 6228 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 22 involuntary context switches\n 2294849243 instructions retired\n 589978929 cycles elapsed\n 58753408 peak memory footprint\n", + "process_peak_rss_bytes": 98516992, + "measurements": { + "all_hulls_cpu_ms": 37.901583, + "bipartite_projection_ms": 0.611, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-100", + "hif_parse_validate_ms": 13.41875, + "hif_viewer_conversion_ms": 16.832, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.035, + 1.077458, + 1.025458, + 1.0192910000000002, + 1.055, + 1.0790000000000002, + 1.145958, + 1.1335, + 1.147792, + 1.173542, + 1.277666, + 1.203458, + 1.1503329999999998, + 1.170875, + 1.282166, + 1.2394999999999998, + 1.230292, + 1.2388329999999999, + 1.234042, + 1.223792 + ], + "links": 20000, + "native_parse_ms": 1.4617090000000001, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.13576102256774902 + }, + { + "dataset": "mathlib", + "hulls": false, + "live": false, + "repetition": 1, + "warmup": false, + "order": 19, + "started_unix": 1791056778.611567, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "status": "ok", + "returncode": 0, + "stderr": " 3.38 real 3.24 user 0.12 sys\n 1070366720 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 72449 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 331 involuntary context switches\n 51180668314 instructions retired\n 14889674633 cycles elapsed\n 663979208 peak memory footprint\n", + "process_peak_rss_bytes": 1070366720, + "measurements": { + "all_hulls_cpu_ms": 10.611583000000001, + "bipartite_projection_ms": 15.237458, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "mathlib-d13f23b723b8a846827a245b89c10fc7d3f11612", + "hif_parse_validate_ms": 139.15212499999998, + "hif_viewer_conversion_ms": 227.94916700000002, + "hull_vertex_cap": 24, + "hulls": 7550, + "hyperedges": 44906, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 103.22654200000001, + 101.55479100000001, + 96.936542, + 94.255208, + 95.54725, + 95.96333299999999, + 96.617666, + 97.20516699999999, + 96.28645900000001, + 96.04662499999999, + 97.217292, + 96.622625, + 96.93320800000001, + 97.657667, + 97.497083, + 101.110208, + 101.236458, + 100.600291, + 101.28845799999999, + 101.414375 + ], + "links": 118066, + "native_parse_ms": 35.955124999999995, + "tool": "hyper-core", + "vertices": 9384 + }, + "process_wall_seconds": 3.3923258781433105 + }, + { + "dataset": "moderate", + "hulls": false, + "live": false, + "repetition": 1, + "warmup": false, + "order": 20, + "started_unix": 1791056782.004544, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "status": "ok", + "returncode": 0, + "stderr": " 0.16 real 0.15 user 0.00 sys\n 84770816 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 5506 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 27 involuntary context switches\n 2753422467 instructions retired\n 699121693 cycles elapsed\n 57868744 peak memory footprint\n", + "process_peak_rss_bytes": 84770816, + "measurements": { + "all_hulls_cpu_ms": 15.438291, + "bipartite_projection_ms": 0.634666, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "moderate", + "hif_parse_validate_ms": 12.121125, + "hif_viewer_conversion_ms": 12.595875, + "hull_vertex_cap": 24, + "hulls": 1600, + "hyperedges": 2000, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 3.5355, + 3.642125, + 3.708125, + 3.443875, + 3.5153749999999997, + 3.549875, + 3.42525, + 3.478125, + 3.535416, + 3.450834, + 3.5217080000000003, + 3.625083, + 3.5509999999999997, + 3.622458, + 3.386833, + 3.470208, + 3.636542, + 3.444083, + 3.4586669999999997, + 3.6609580000000004 + ], + "links": 13200, + "native_parse_ms": 2.123292, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.1640779972076416 + }, + { + "dataset": "large", + "hulls": false, + "live": false, + "repetition": 2, + "warmup": false, + "order": 21, + "started_unix": 1791056782.169285, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "status": "ok", + "returncode": 0, + "stderr": " 1.23 real 1.17 user 0.05 sys\n 443416576 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 29814 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 126 involuntary context switches\n 19200037674 instructions retired\n 5429528981 cycles elapsed\n 263881400 peak memory footprint\n", + "process_peak_rss_bytes": 443416576, + "measurements": { + "all_hulls_cpu_ms": 78.17875000000001, + "bipartite_projection_ms": 4.095167, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "large", + "hif_parse_validate_ms": 62.225333, + "hif_viewer_conversion_ms": 87.642792, + "hull_vertex_cap": 24, + "hulls": 8000, + "hyperedges": 10000, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 30.973417, + 31.130333, + 31.927457999999998, + 32.454457999999995, + 33.012333, + 32.699208, + 32.784917, + 32.486541, + 32.204333, + 32.289125, + 31.851959, + 31.944124999999996, + 31.95025, + 32.087708, + 31.906167, + 31.530084, + 31.739542000000004, + 31.45925, + 31.210833, + 31.163 + ], + "links": 66000, + "native_parse_ms": 14.190125, + "tool": "hyper-core", + "vertices": 10000 + }, + "process_wall_seconds": 1.2347638607025146 + }, + { + "dataset": "high-arity-100", + "hulls": false, + "live": false, + "repetition": 2, + "warmup": false, + "order": 22, + "started_unix": 1791056783.404774, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "status": "ok", + "returncode": 0, + "stderr": " 0.13 real 0.12 user 0.00 sys\n 98484224 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 6225 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 17 involuntary context switches\n 2294951638 instructions retired\n 575812029 cycles elapsed\n 58769840 peak memory footprint\n", + "process_peak_rss_bytes": 98484224, + "measurements": { + "all_hulls_cpu_ms": 38.574584, + "bipartite_projection_ms": 0.611041, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-100", + "hif_parse_validate_ms": 12.815208, + "hif_viewer_conversion_ms": 15.962166999999999, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.013375, + 1.034417, + 1.069333, + 1.066541, + 1.081, + 1.0354590000000001, + 1.0795, + 1.107209, + 1.196542, + 1.181333, + 1.1707079999999999, + 1.178375, + 1.2429590000000001, + 1.1715, + 1.171167, + 1.202083, + 1.274083, + 1.241792, + 1.2147919999999999, + 1.21475 + ], + "links": 20000, + "native_parse_ms": 1.478708, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.13379120826721191 + }, + { + "dataset": "high-arity-10", + "hulls": false, + "live": false, + "repetition": 2, + "warmup": false, + "order": 23, + "started_unix": 1791056783.539259, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "status": "ok", + "returncode": 0, + "stderr": " 0.04 real 0.03 user 0.00 sys\n 36159488 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 2420 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 13 involuntary context switches\n 817134369 instructions retired\n 185527602 cycles elapsed\n 34226584 peak memory footprint\n", + "process_peak_rss_bytes": 36159488, + "measurements": { + "all_hulls_cpu_ms": 1.2967499999999998, + "bipartite_projection_ms": 0.148667, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-10", + "hif_parse_validate_ms": 3.761291, + "hif_viewer_conversion_ms": 3.3592079999999997, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 0.872583, + 0.8595, + 0.875334, + 0.943667, + 0.963083, + 0.896792, + 0.8949579999999999, + 0.898166, + 0.897042, + 0.897292, + 0.9851250000000001, + 0.8718750000000001, + 0.875291, + 0.9005000000000001, + 0.964958, + 0.985708, + 0.936458, + 0.907291, + 0.909833, + 0.936167 + ], + "links": 2000, + "native_parse_ms": 1.096292, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.04575204849243164 + }, + { + "dataset": "moderate", + "hulls": false, + "live": false, + "repetition": 2, + "warmup": false, + "order": 24, + "started_unix": 1791056783.5857458, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "status": "ok", + "returncode": 0, + "stderr": " 0.15 real 0.15 user 0.00 sys\n 71450624 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 4579 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 22 involuntary context switches\n 2741956096 instructions retired\n 697288486 cycles elapsed\n 54460824 peak memory footprint\n", + "process_peak_rss_bytes": 71450624, + "measurements": { + "all_hulls_cpu_ms": 15.594625, + "bipartite_projection_ms": 0.6440830000000001, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "moderate", + "hif_parse_validate_ms": 10.857708, + "hif_viewer_conversion_ms": 12.651000000000002, + "hull_vertex_cap": 24, + "hulls": 1600, + "hyperedges": 2000, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 3.659875, + 3.5037089999999997, + 3.5913749999999998, + 3.6582500000000002, + 3.6292910000000003, + 3.509417, + 3.505583, + 3.610458, + 3.505958, + 3.482292, + 3.492083, + 3.6002080000000003, + 3.3915, + 3.473583, + 3.632167, + 3.384583, + 3.415875, + 3.621, + 3.3552920000000004, + 3.454834 + ], + "links": 13200, + "native_parse_ms": 2.023875, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.16166400909423828 + }, + { + "dataset": "scientific", + "hulls": false, + "live": false, + "repetition": 2, + "warmup": false, + "order": 25, + "started_unix": 1791056783.748168, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "status": "ok", + "returncode": 0, + "stderr": " 0.06 real 0.06 user 0.00 sys\n 28540928 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 1955 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 15 involuntary context switches\n 1236452889 instructions retired\n 274714709 cycles elapsed\n 26624408 peak memory footprint\n", + "process_peak_rss_bytes": 28540928, + "measurements": { + "all_hulls_cpu_ms": 0.106542, + "bipartite_projection_ms": 0.185833, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "diseasome", + "hif_parse_validate_ms": 3.046208, + "hif_viewer_conversion_ms": 2.4141660000000003, + "hull_vertex_cap": 24, + "hulls": 163, + "hyperedges": 903, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.9152500000000001, + 2.03525, + 1.9872090000000002, + 2.049959, + 2.1355419999999996, + 1.93525, + 2.1374589999999998, + 1.907084, + 2.0495, + 2.066125, + 1.980458, + 2.0912499999999996, + 2.010959, + 2.072459, + 2.0835, + 1.9480000000000002, + 2.103333, + 2.108834, + 2.093667, + 2.115875 + ], + "links": 1550, + "native_parse_ms": 0.759958, + "tool": "hyper-core", + "vertices": 516 + }, + "process_wall_seconds": 0.06655502319335938 + }, + { + "dataset": "high-arity-1000", + "hulls": false, + "live": false, + "repetition": 2, + "warmup": false, + "order": 26, + "started_unix": 1791056783.815726, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "status": "ok", + "returncode": 0, + "stderr": " 0.63 real 0.57 user 0.05 sys\n 582828032 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 37883 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 72 involuntary context switches\n 13943983948 instructions retired\n 2819904909 cycles elapsed\n 382845600 peak memory footprint\n", + "process_peak_rss_bytes": 582828032, + "measurements": { + "all_hulls_cpu_ms": 45.005375, + "bipartite_projection_ms": 4.272625000000001, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-1000", + "hif_parse_validate_ms": 104.697458, + "hif_viewer_conversion_ms": 147.07275, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.42125, + 1.440333, + 1.806375, + 1.880333, + 1.9554160000000003, + 1.941459, + 1.9219169999999999, + 1.81, + 1.6775, + 1.623958, + 1.778208, + 1.616166, + 1.562125, + 1.0201660000000001, + 1.22725, + 1.671208, + 1.872666, + 2.191666, + 2.07325, + 2.0551250000000003 + ], + "links": 200000, + "native_parse_ms": 4.776083000000001, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.641930103302002 + }, + { + "dataset": "mathlib", + "hulls": false, + "live": false, + "repetition": 2, + "warmup": false, + "order": 27, + "started_unix": 1791056784.458459, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "status": "ok", + "returncode": 0, + "stderr": " 3.44 real 3.29 user 0.12 sys\n 1052540928 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 71362 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 1521 involuntary context switches\n 51174350395 instructions retired\n 14981124725 cycles elapsed\n 656180376 peak memory footprint\n", + "process_peak_rss_bytes": 1052540928, + "measurements": { + "all_hulls_cpu_ms": 10.838959000000001, + "bipartite_projection_ms": 15.385541000000002, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "mathlib-d13f23b723b8a846827a245b89c10fc7d3f11612", + "hif_parse_validate_ms": 140.18525000000002, + "hif_viewer_conversion_ms": 228.002417, + "hull_vertex_cap": 24, + "hulls": 7550, + "hyperedges": 44906, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 103.644208, + 103.227417, + 97.397458, + 96.25529200000001, + 129.75275, + 99.964042, + 97.64708399999999, + 99.661125, + 100.008375, + 99.292042, + 97.45675, + 97.608958, + 98.684584, + 97.666875, + 99.056625, + 99.40191700000001, + 101.018, + 101.293875, + 101.410958, + 101.685917 + ], + "links": 118066, + "native_parse_ms": 36.261500000000005, + "tool": "hyper-core", + "vertices": 9384 + }, + "process_wall_seconds": 3.452285051345825 + }, + { + "dataset": "moderate", + "hulls": false, + "live": false, + "repetition": 3, + "warmup": false, + "order": 28, + "started_unix": 1791056787.9115689, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "status": "ok", + "returncode": 0, + "stderr": " 0.16 real 0.15 user 0.00 sys\n 84705280 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 5502 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 21 involuntary context switches\n 2751193031 instructions retired\n 699072508 cycles elapsed\n 57868744 peak memory footprint\n", + "process_peak_rss_bytes": 84705280, + "measurements": { + "all_hulls_cpu_ms": 15.588708, + "bipartite_projection_ms": 0.6541669999999999, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "moderate", + "hif_parse_validate_ms": 11.522792, + "hif_viewer_conversion_ms": 12.83225, + "hull_vertex_cap": 24, + "hulls": 1600, + "hyperedges": 2000, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 3.441916, + 3.636333, + 3.628833, + 3.485583, + 3.5382909999999996, + 3.583666, + 3.639542, + 3.501209, + 3.49275, + 3.475792, + 3.504875, + 3.553417, + 3.4853750000000003, + 3.5604999999999998, + 3.5598330000000002, + 3.547625, + 3.6423330000000003, + 3.4518750000000002, + 3.430917, + 3.6324579999999997 + ], + "links": 13200, + "native_parse_ms": 2.120334, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.1646101474761963 + }, + { + "dataset": "high-arity-10", + "hulls": false, + "live": false, + "repetition": 3, + "warmup": false, + "order": 29, + "started_unix": 1791056788.077089, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "status": "ok", + "returncode": 0, + "stderr": " 0.04 real 0.03 user 0.00 sys\n 36241408 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 2426 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 16 involuntary context switches\n 816793706 instructions retired\n 197712939 cycles elapsed\n 34308504 peak memory footprint\n", + "process_peak_rss_bytes": 36241408, + "measurements": { + "all_hulls_cpu_ms": 1.3065, + "bipartite_projection_ms": 0.140375, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-10", + "hif_parse_validate_ms": 4.318, + "hif_viewer_conversion_ms": 3.6924580000000002, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 0.961542, + 0.992, + 1.0310840000000001, + 0.9295, + 0.903417, + 0.8875419999999999, + 0.9185000000000001, + 1.018375, + 0.962125, + 0.922083, + 0.921625, + 1.0352919999999999, + 1.0085840000000001, + 0.944666, + 0.93375, + 0.916708, + 0.927834, + 0.9650000000000001, + 1.0129169999999998, + 0.9350419999999999 + ], + "links": 2000, + "native_parse_ms": 1.291292, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.049449920654296875 + }, + { + "dataset": "scientific", + "hulls": false, + "live": false, + "repetition": 3, + "warmup": false, + "order": 30, + "started_unix": 1791056788.127517, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "status": "ok", + "returncode": 0, + "stderr": " 0.06 real 0.05 user 0.00 sys\n 31555584 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 2140 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 17 involuntary context switches\n 1235789903 instructions retired\n 273346617 cycles elapsed\n 29639064 peak memory footprint\n", + "process_peak_rss_bytes": 31555584, + "measurements": { + "all_hulls_cpu_ms": 0.109791, + "bipartite_projection_ms": 0.194, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "diseasome", + "hif_parse_validate_ms": 3.176708, + "hif_viewer_conversion_ms": 2.420291, + "hull_vertex_cap": 24, + "hulls": 163, + "hyperedges": 903, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.942, + 2.0683339999999997, + 2.03075, + 2.078834, + 2.1336660000000003, + 1.9587910000000002, + 2.104458, + 1.897333, + 2.0526250000000004, + 2.0905, + 2.010666, + 2.038292, + 1.9930830000000002, + 1.988208, + 2.00175, + 2.0029589999999997, + 2.09575, + 2.0414169999999996, + 2.022125, + 2.072292 + ], + "links": 1550, + "native_parse_ms": 0.7788750000000001, + "tool": "hyper-core", + "vertices": 516 + }, + "process_wall_seconds": 0.0664072036743164 + }, + { + "dataset": "mathlib", + "hulls": false, + "live": false, + "repetition": 3, + "warmup": false, + "order": 31, + "started_unix": 1791056788.194795, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "status": "ok", + "returncode": 0, + "stderr": " 3.39 real 3.25 user 0.12 sys\n 1071054848 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 72491 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 333 involuntary context switches\n 51177351601 instructions retired\n 14887115567 cycles elapsed\n 663520408 peak memory footprint\n", + "process_peak_rss_bytes": 1071054848, + "measurements": { + "all_hulls_cpu_ms": 10.671292000000001, + "bipartite_projection_ms": 15.560834000000002, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "mathlib-d13f23b723b8a846827a245b89c10fc7d3f11612", + "hif_parse_validate_ms": 141.67254200000002, + "hif_viewer_conversion_ms": 230.541792, + "hull_vertex_cap": 24, + "hulls": 7550, + "hyperedges": 44906, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 102.555542, + 102.01083299999999, + 97.230125, + 95.24525, + 96.32141700000001, + 97.370709, + 97.362083, + 97.902292, + 98.45891599999999, + 97.276541, + 97.349542, + 96.88275, + 97.48975, + 97.482458, + 98.134, + 100.393541, + 101.100792, + 101.451375, + 102.319542, + 102.9935 + ], + "links": 118066, + "native_parse_ms": 36.554, + "tool": "hyper-core", + "vertices": 9384 + }, + "process_wall_seconds": 3.4026529788970947 + }, + { + "dataset": "large", + "hulls": false, + "live": false, + "repetition": 3, + "warmup": false, + "order": 32, + "started_unix": 1791056791.598367, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "status": "ok", + "returncode": 0, + "stderr": " 1.23 real 1.17 user 0.05 sys\n 445038592 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 29912 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 138 involuntary context switches\n 19196803144 instructions retired\n 5404377462 cycles elapsed\n 264569528 peak memory footprint\n", + "process_peak_rss_bytes": 445038592, + "measurements": { + "all_hulls_cpu_ms": 78.29204200000001, + "bipartite_projection_ms": 4.065917, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "large", + "hif_parse_validate_ms": 62.266417, + "hif_viewer_conversion_ms": 85.163833, + "hull_vertex_cap": 24, + "hulls": 8000, + "hyperedges": 10000, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 31.434667, + 31.592750000000002, + 31.597958000000002, + 32.635999999999996, + 32.671167000000004, + 33.254, + 32.773709, + 32.457291999999995, + 32.276334, + 31.937708, + 32.034541, + 31.515500000000003, + 31.682541, + 31.891707999999998, + 32.133916, + 31.761458, + 31.909541, + 31.195125, + 31.250291999999998, + 30.904041 + ], + "links": 66000, + "native_parse_ms": 14.159334000000001, + "tool": "hyper-core", + "vertices": 10000 + }, + "process_wall_seconds": 1.235605001449585 + }, + { + "dataset": "high-arity-100", + "hulls": false, + "live": false, + "repetition": 3, + "warmup": false, + "order": 33, + "started_unix": 1791056792.834914, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "status": "ok", + "returncode": 0, + "stderr": " 0.13 real 0.12 user 0.00 sys\n 96223232 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 6087 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 21 involuntary context switches\n 2293698118 instructions retired\n 583857628 cycles elapsed\n 58802608 peak memory footprint\n", + "process_peak_rss_bytes": 96223232, + "measurements": { + "all_hulls_cpu_ms": 37.928333, + "bipartite_projection_ms": 0.605916, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-100", + "hif_parse_validate_ms": 12.999042000000001, + "hif_viewer_conversion_ms": 16.379375000000003, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.0361669999999998, + 1.028667, + 1.13475, + 1.039458, + 1.039667, + 1.08, + 1.1297499999999998, + 1.153875, + 1.174, + 1.166708, + 1.18125, + 1.2409999999999999, + 1.144917, + 1.170833, + 1.214917, + 1.218084, + 1.243959, + 1.238334, + 1.224667, + 1.27525 + ], + "links": 20000, + "native_parse_ms": 1.481417, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.13534188270568848 + }, + { + "dataset": "high-arity-1000", + "hulls": false, + "live": false, + "repetition": 3, + "warmup": false, + "order": 34, + "started_unix": 1791056792.971201, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "status": "ok", + "returncode": 0, + "stderr": " 0.63 real 0.56 user 0.06 sys\n 710443008 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 45672 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 61 involuntary context switches\n 14011649429 instructions retired\n 2778310312 cycles elapsed\n 409682880 peak memory footprint\n", + "process_peak_rss_bytes": 710443008, + "measurements": { + "all_hulls_cpu_ms": 44.883458000000005, + "bipartite_projection_ms": 4.3415, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-1000", + "hif_parse_validate_ms": 101.019625, + "hif_viewer_conversion_ms": 147.098334, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.3754170000000001, + 1.436083, + 1.741166, + 1.967875, + 2.0058749999999996, + 1.9393340000000001, + 1.9596660000000001, + 1.779542, + 1.775584, + 1.6974589999999998, + 1.676042, + 1.614333, + 1.5179589999999998, + 1.00975, + 1.254583, + 1.75, + 1.929291, + 2.167583, + 2.082458, + 1.9889999999999999 + ], + "links": 200000, + "native_parse_ms": 4.907792000000001, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.6379399299621582 + }, + { + "dataset": "large", + "hulls": false, + "live": false, + "repetition": 4, + "warmup": false, + "order": 35, + "started_unix": 1791056793.610097, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "status": "ok", + "returncode": 0, + "stderr": " 1.22 real 1.17 user 0.05 sys\n 444284928 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 29867 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 121 involuntary context switches\n 19195153681 instructions retired\n 5395310146 cycles elapsed\n 265159376 peak memory footprint\n", + "process_peak_rss_bytes": 444284928, + "measurements": { + "all_hulls_cpu_ms": 78.34037500000001, + "bipartite_projection_ms": 4.0865, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "large", + "hif_parse_validate_ms": 59.940041, + "hif_viewer_conversion_ms": 84.216333, + "hull_vertex_cap": 24, + "hulls": 8000, + "hyperedges": 10000, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 31.243083000000002, + 31.39675, + 32.061791, + 32.347208, + 32.943208, + 32.716417, + 32.850209, + 32.404292, + 32.226040999999995, + 31.984083000000002, + 32.058915999999996, + 31.737291, + 32.093208, + 32.072875, + 31.6825, + 31.974000000000004, + 31.636083000000003, + 31.634375, + 31.120874999999998, + 30.693082999999998 + ], + "links": 66000, + "native_parse_ms": 14.375875, + "tool": "hyper-core", + "vertices": 10000 + }, + "process_wall_seconds": 1.2312450408935547 + }, + { + "dataset": "high-arity-100", + "hulls": false, + "live": false, + "repetition": 4, + "warmup": false, + "order": 36, + "started_unix": 1791056794.842355, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "status": "ok", + "returncode": 0, + "stderr": " 0.13 real 0.12 user 0.00 sys\n 83099648 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 5286 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 17 involuntary context switches\n 2288355185 instructions retired\n 572586153 cycles elapsed\n 58753432 peak memory footprint\n", + "process_peak_rss_bytes": 83099648, + "measurements": { + "all_hulls_cpu_ms": 37.988291000000004, + "bipartite_projection_ms": 0.594875, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-100", + "hif_parse_validate_ms": 13.327167, + "hif_viewer_conversion_ms": 15.998292000000001, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.0354999999999999, + 1.044458, + 1.058292, + 1.070459, + 1.151875, + 1.060167, + 1.069416, + 1.086625, + 1.215875, + 1.155875, + 1.188417, + 1.1916660000000001, + 1.189458, + 1.250209, + 1.221542, + 1.247042, + 1.2897500000000002, + 1.24875, + 1.250125, + 1.2162080000000002 + ], + "links": 20000, + "native_parse_ms": 1.491666, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.13343501091003418 + }, + { + "dataset": "high-arity-1000", + "hulls": false, + "live": false, + "repetition": 4, + "warmup": false, + "order": 37, + "started_unix": 1791056794.976795, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "status": "ok", + "returncode": 0, + "stderr": " 0.63 real 0.56 user 0.06 sys\n 710557696 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 45679 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 65 involuntary context switches\n 14009232954 instructions retired\n 2773311261 cycles elapsed\n 409781160 peak memory footprint\n", + "process_peak_rss_bytes": 710557696, + "measurements": { + "all_hulls_cpu_ms": 44.833042, + "bipartite_projection_ms": 4.383916, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-1000", + "hif_parse_validate_ms": 100.322625, + "hif_viewer_conversion_ms": 145.52812500000002, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.426916, + 1.405084, + 1.7910840000000001, + 1.939333, + 1.950208, + 1.950292, + 1.9455, + 1.773333, + 1.673041, + 1.629208, + 1.7842500000000001, + 1.6515, + 1.590959, + 1.0119580000000001, + 1.235041, + 1.678, + 1.935875, + 2.211959, + 2.102542, + 2.048958 + ], + "links": 200000, + "native_parse_ms": 4.778042, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.6370000839233398 + }, + { + "dataset": "mathlib", + "hulls": false, + "live": false, + "repetition": 4, + "warmup": false, + "order": 38, + "started_unix": 1791056795.6148422, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "status": "ok", + "returncode": 0, + "stderr": " 3.40 real 3.26 user 0.12 sys\n 1070383104 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 72450 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 1 voluntary context switches\n 347 involuntary context switches\n 51173176765 instructions retired\n 14905788753 cycles elapsed\n 663979184 peak memory footprint\n", + "process_peak_rss_bytes": 1070383104, + "measurements": { + "all_hulls_cpu_ms": 10.773333, + "bipartite_projection_ms": 15.355125000000001, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "mathlib-d13f23b723b8a846827a245b89c10fc7d3f11612", + "hif_parse_validate_ms": 141.10008399999998, + "hif_viewer_conversion_ms": 228.18, + "hull_vertex_cap": 24, + "hulls": 7550, + "hyperedges": 44906, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 102.764292, + 101.736875, + 97.36220800000001, + 95.317667, + 97.151083, + 97.5205, + 97.07325, + 97.53279099999999, + 97.435625, + 97.49079099999999, + 97.304666, + 97.213416, + 97.787583, + 97.864125, + 98.903542, + 99.94575, + 100.79920800000001, + 101.005042, + 102.345875, + 102.198084 + ], + "links": 118066, + "native_parse_ms": 36.161209, + "tool": "hyper-core", + "vertices": 9384 + }, + "process_wall_seconds": 3.4137518405914307 + }, + { + "dataset": "moderate", + "hulls": false, + "live": false, + "repetition": 4, + "warmup": false, + "order": 39, + "started_unix": 1791056799.0296829, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "status": "ok", + "returncode": 0, + "stderr": " 0.16 real 0.15 user 0.00 sys\n 84688896 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 5501 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 1 voluntary context switches\n 23 involuntary context switches\n 2753074925 instructions retired\n 695233037 cycles elapsed\n 57770416 peak memory footprint\n", + "process_peak_rss_bytes": 84688896, + "measurements": { + "all_hulls_cpu_ms": 15.589209, + "bipartite_projection_ms": 0.621125, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "moderate", + "hif_parse_validate_ms": 11.719125, + "hif_viewer_conversion_ms": 12.630625, + "hull_vertex_cap": 24, + "hulls": 1600, + "hyperedges": 2000, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 3.551167, + 3.54975, + 3.661042, + 3.469541, + 3.538458, + 3.562916, + 3.512708, + 3.503084, + 3.6335, + 3.449334, + 3.511334, + 3.608917, + 3.522458, + 3.5077920000000002, + 3.588791, + 3.4863329999999997, + 3.530083, + 3.416125, + 3.4845, + 3.6009160000000002 + ], + "links": 13200, + "native_parse_ms": 2.1414590000000002, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.16592717170715332 + }, + { + "dataset": "high-arity-10", + "hulls": false, + "live": false, + "repetition": 4, + "warmup": false, + "order": 40, + "started_unix": 1791056799.196673, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "status": "ok", + "returncode": 0, + "stderr": " 0.04 real 0.03 user 0.00 sys\n 32849920 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 2218 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 1 voluntary context switches\n 12 involuntary context switches\n 810524152 instructions retired\n 183153316 cycles elapsed\n 30933400 peak memory footprint\n", + "process_peak_rss_bytes": 32849920, + "measurements": { + "all_hulls_cpu_ms": 1.284417, + "bipartite_projection_ms": 0.1525, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "high-arity-10", + "hif_parse_validate_ms": 3.959333, + "hif_viewer_conversion_ms": 3.44525, + "hull_vertex_cap": 24, + "hulls": 200, + "hyperedges": 200, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 0.8921250000000001, + 0.887875, + 0.877167, + 0.883125, + 0.898417, + 0.882875, + 0.9015839999999999, + 0.901042, + 0.897416, + 0.896875, + 1.018084, + 0.881042, + 0.8710840000000001, + 0.890416, + 0.9065, + 0.919125, + 0.9217080000000001, + 0.907541, + 0.911833, + 0.926625 + ], + "links": 2000, + "native_parse_ms": 1.162875, + "tool": "hyper-core", + "vertices": 1000 + }, + "process_wall_seconds": 0.045739173889160156 + }, + { + "dataset": "scientific", + "hulls": false, + "live": false, + "repetition": 4, + "warmup": false, + "order": 41, + "started_unix": 1791056799.243497, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "status": "ok", + "returncode": 0, + "stderr": " 0.06 real 0.06 user 0.00 sys\n 31588352 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 2142 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 0 messages sent\n 0 messages received\n 0 signals received\n 0 voluntary context switches\n 15 involuntary context switches\n 1237064454 instructions retired\n 275425953 cycles elapsed\n 29671832 peak memory footprint\n", + "process_peak_rss_bytes": 31588352, + "measurements": { + "all_hulls_cpu_ms": 0.10483400000000001, + "bipartite_projection_ms": 0.188583, + "correctness": "passed document roundtrip, every converted string ID and member, node metadata and exact projected incidences", + "dataset": "diseasome", + "hif_parse_validate_ms": 3.139917, + "hif_viewer_conversion_ms": 2.453459, + "hull_vertex_cap": 24, + "hulls": 163, + "hyperedges": 903, + "layout_settings": "LayoutConfig::default,5warmup20steps,seeded common XYZ", + "layout_step_ms": [ + 1.996875, + 2.013541, + 2.107208, + 2.09675, + 2.180292, + 1.965791, + 2.128833, + 1.9737079999999998, + 2.1193750000000002, + 2.097208, + 1.9654579999999997, + 2.0735829999999997, + 2.0209580000000003, + 2.07625, + 1.985083, + 2.004292, + 2.104084, + 2.068458, + 2.079958, + 2.050041 + ], + "links": 1550, + "native_parse_ms": 0.7675, + "tool": "hyper-core", + "vertices": 516 + }, + "process_wall_seconds": 0.0664529800415039 + } +] diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/datasets-manifest.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/datasets-manifest.json new file mode 100644 index 0000000..77cd0de --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/datasets-manifest.json @@ -0,0 +1,207 @@ +[ + { + "dataset": "correctness", + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8, + "empty_edges": 1, + "isolated_nodes": 2, + "provenance": { + "id": "correctness", + "title": "correctness", + "attrs": { + "license": "CC0-1.0", + "benchmark_seed": 20261003 + } + }, + "sha256": { + "correctness.json": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "correctness.hif.json": "cf2000d1fafe2b98f5cc27936b4314bacc39df0998bad7f920a90b27c295ed74", + "correctness.incidence.json": "31f598742003d72967c156056c7f3ea8631d9293274723430c812d02747a794f", + "correctness.updated.json": "b3f999c5f882b8cf1eab8f927c161850433898120b3f2ce2b971ff9ca1d5b5e0", + "correctness.updated.incidence.json": "f964da790c51e47b20f5f6c653c0580ac4f1386ced684cc5e35329003705e811" + } + }, + { + "dataset": "moderate", + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16, + "empty_edges": 0, + "isolated_nodes": 0, + "provenance": { + "id": "moderate", + "title": "moderate", + "attrs": { + "license": "CC0-1.0", + "benchmark_seed": 20261003 + } + }, + "sha256": { + "moderate.json": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "moderate.hif.json": "cf03c4bcccaedda5b648f90b23a23be5e502cf4232a8000546a690a1666fc70b", + "moderate.incidence.json": "509a4af00f02459948acbe31d3f858277b7d5b3e628577b497b43f5679fa80fe", + "moderate.updated.json": "714ac7349cfa36bb9a4044a87df9a0330e1ce86005283f47ee38d0017fc34e00", + "moderate.updated.incidence.json": "af2f15be0dff64cdd5d87daae81bbf87ad75b2a94bfbf2f9fb538347c840a693" + } + }, + { + "dataset": "large", + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16, + "empty_edges": 0, + "isolated_nodes": 11, + "provenance": { + "id": "large", + "title": "large", + "attrs": { + "license": "CC0-1.0", + "benchmark_seed": 20261003 + } + }, + "sha256": { + "large.json": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "large.hif.json": "4d9ed034aafa0e856fc72f19b8379d543c2c79d9af7623aaf59cf65f4ef0286e", + "large.incidence.json": "140fa8444d7df3226e90f96b14a64212dce7644f5294b6f1fb7ae615684bf174", + "large.updated.json": "8cc913644f1a809cd305df7c7b6585260cf35cd7dd1d269702f147957582f017", + "large.updated.incidence.json": "732042295f9acc6fa05e0207a7c490dfe2df4ea17ae6f8b5ec5d0ec254fbeda0" + } + }, + { + "dataset": "high-arity-10", + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10, + "empty_edges": 0, + "isolated_nodes": 126, + "provenance": { + "id": "high-arity-10", + "title": "high-arity-10", + "attrs": { + "license": "CC0-1.0", + "benchmark_seed": 20261003 + } + }, + "sha256": { + "high-arity-10.json": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "high-arity-10.hif.json": "ae03a2d2dc83b745c59b5ea46b8ac434c4c33dd53733ecc14487d2053352c56a", + "high-arity-10.incidence.json": "8558e933ab370429244084db265d93909fe68d538ebc543c4d6733d6ae5d57ee", + "high-arity-10.updated.json": "f464573486df6f9c2c04527f6d2a752f537255f47cd72d17d803ca11de8741c2", + "high-arity-10.updated.incidence.json": "6ab4e5f9908c61e6bc74a21987ec78100976dd89280a73f07299f0602383dc6e" + } + }, + { + "dataset": "high-arity-100", + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100, + "empty_edges": 0, + "isolated_nodes": 0, + "provenance": { + "id": "high-arity-100", + "title": "high-arity-100", + "attrs": { + "license": "CC0-1.0", + "benchmark_seed": 20261003 + } + }, + "sha256": { + "high-arity-100.json": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "high-arity-100.hif.json": "ada3c3c8274b3c5653cc1863e1754d12b3dcf616344a708dd9d8f5ffc1a05774", + "high-arity-100.incidence.json": "cfa115631dbcf4bce45c4988321bf2e4f008ad4bd74db8ea74b56be8ea7526b2", + "high-arity-100.updated.json": "a1b42ec4ee3554edb8520cc7c4c0e568cfe38519038b118ad84258c7a3f7d32a", + "high-arity-100.updated.incidence.json": "d7165eccca36718220a183eb4801c682278299773417c0caba072490a201e312" + } + }, + { + "dataset": "high-arity-1000", + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000, + "empty_edges": 0, + "isolated_nodes": 0, + "provenance": { + "id": "high-arity-1000", + "title": "high-arity-1000", + "attrs": { + "license": "CC0-1.0", + "benchmark_seed": 20261003 + } + }, + "sha256": { + "high-arity-1000.json": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "high-arity-1000.hif.json": "896c657c69bc6a5c4abebaff51a75f6bec1071be347e024b47003113a74a9602", + "high-arity-1000.incidence.json": "772fa1cacad08ce8e08674351dcf926ef2071011a6d2e7741d77401c72477d58", + "high-arity-1000.updated.json": "e52856cc8eab5c50e5f7ccbf6c538d60ce84ebd97086f9b6d2eeed7694527f99", + "high-arity-1000.updated.incidence.json": "0f0c32cd04bf4708b42eb05e391914f47b3d2b26a3224d77bbeea21875d3b867" + } + }, + { + "dataset": "scientific", + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11, + "empty_edges": 0, + "isolated_nodes": 0, + "provenance": { + "id": "diseasome", + "title": "Diseasome", + "attrs": { + "license": "CC-BY-4.0", + "source": "https://doi.org/10.5281/zenodo.21909416", + "source_metadata": { + "name": "Diseasome" + }, + "benchmark_seed": 20261003 + } + }, + "sha256": { + "scientific.json": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "scientific.hif.json": "28dd17deac2707e215dc58acde26b93adc1d34ec78b0c6c8ac66077e61cb6315", + "scientific.incidence.json": "3f470f655701325c6407b30026e408060aef2a28a356bba496e041542c8a6b2b", + "scientific.updated.json": "2e2d973fcda04c0022302fc08e486668498bd97a95b5e0b19129be5fcd9e3516", + "scientific.updated.incidence.json": "fb763310014b88c982eedec9f6063b9bf9ba8983d96d7f5bec07f197bbfa0257" + } + }, + { + "dataset": "mathlib", + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532, + "empty_edges": 0, + "isolated_nodes": 3, + "provenance": { + "id": "mathlib-d13f23b723b8a846827a245b89c10fc7d3f11612", + "title": "Mathlib v4.34.1 \u00b7 source dependency atlas", + "attrs": { + "repository": "https://github.com/leanprover-community/mathlib4", + "release": "v4.34.1", + "commit": "d13f23b723b8a846827a245b89c10fc7d3f11612", + "source_sha256": "dde69137f0279fdb1ec0b40e16dd5252c0a13aa8550c9508e71ea811565347f3", + "source_files": 9112, + "external_modules": 272, + "imports": 37356, + "dependency_groups": 7550, + "max_group_arity": 8532, + "semantics": "Undirected import pairs; one source + direct imports set per file with >=2 imports. Direction is retained in attrs. External dependencies are stubs.", + "benchmark_seed": 20261003 + } + }, + "sha256": { + "mathlib.json": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "mathlib.hif.json": "8d272f9de87de2a94e4107b35730175f02bc7f71bc284a5992c73cdb9cdb3005", + "mathlib.incidence.json": "867d398d4f44b522670f6e1700e922125aed10e029322690218e0adc7700df57", + "mathlib.updated.json": "d02a87fa099f99218371847f7a168ba9a21986ae0faf942b9e083e7f9467370a", + "mathlib.updated.incidence.json": "73def033e50da3da4d42a949232bdc6ea6511e0a84caad94818febdadaa6f9b9" + } + } +] diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/driver-notes.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/driver-notes.json new file mode 100644 index 0000000..ed973f2 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/driver-notes.json @@ -0,0 +1,7 @@ +{ + "browser": "Final105 unique jobs: original70 Cytoscape/3D cases plus35 corrected Sigma cases. Original browser closed after evidence was saved; its lingering Node/esbuild worker was then terminated. Explicit esbuild shutdown was added before the corrected run.", + "core": "Final rows use exact ID/member/metadata and projected-incidence assertions. Count-only diagnostics are excluded.", + "scientific": "After timing, only orchestration stale-file/error handling and duplicate-incidence validation were hardened. Named construct and figure operations are unchanged.", + "sigma": "Original full-refresh camera timings are retained only as excluded diagnostics. All35 were replaced by public scheduleRender cached-redraw observations in a separate block. Consolidation verifies input hashes, protocol, browser and package versions.", + "hypergodot": "HiDPI-disabled window was clamped to1920x917 and excluded. Fullscreen smoke did not match. Final five repetitions restore stock HiDPI and verify actual render texture1920x1080 with exact memberships." +} diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/graphia-metadata.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/graphia-metadata.json new file mode 100644 index 0000000..d298e36 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/graphia-metadata.json @@ -0,0 +1,11 @@ +{ + "tool": "graphia", + "executable": "/tmp/hyper-benchmark-competitors/Graphia.app/Contents/MacOS/Graphia", + "timeout_seconds": 180, + "executable_sha256": "93094f94b6b4093bc35705b6aeea8037a7d65ba75dd6c008e30a7d42d8d8fa82", + "conditions": "M5Pro24GiB macOS27; lowpowermode0; no parallel benchmark/build; ordinary desktop background uncontrolled; display sleep disabled; idle sleep prevented; thermal telemetry unavailable; variable display cadence", + "peak_memory": "macOS /usr/bin/time -l whole-process RSS high-water in bytes", + "fresh_process_per_repetition": true, + "repetitions": 5, + "seed": 20261003 +} diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/graphia-raw.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/graphia-raw.json new file mode 100644 index 0000000..5e9a915 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/graphia-raw.json @@ -0,0 +1,416 @@ +[ + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 4.231719375000001, + "process_peak_rss_bytes": 111558656, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/scientific.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 4.22 real 0.29 user 0.08 sys\n 111558656 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 9448 page reclaims\n 839 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 156 voluntary context switches\n 1588 involuntary context switches\n 5028989177 instructions retired\n 1412710350 cycles elapsed\n 45138952 peak memory footprint\n", + "tool": "Graphia", + "dataset": "scientific", + "repetition": -1, + "warmup": true, + "dataset_sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 1419, + "memberships": 1550 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 1.5332331670000006, + "process_peak_rss_bytes": 674185216, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/mathlib.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 1.52 real 1.34 user 0.12 sys\n 674185216 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 45570 page reclaims\n 1 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 62 voluntary context switches\n 1259 involuntary context switches\n 25889175831 instructions retired\n 6304757463 cycles elapsed\n 550438328 peak memory footprint\n", + "tool": "Graphia", + "dataset": "mathlib", + "repetition": -1, + "warmup": true, + "dataset_sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 54290, + "memberships": 118066 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 0.37782641599999955, + "process_peak_rss_bytes": 136658944, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/moderate.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 0.37 real 0.29 user 0.03 sys\n 136658944 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 11706 page reclaims\n 8 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 8 voluntary context switches\n 1004 involuntary context switches\n 5719288876 instructions retired\n 1420784451 cycles elapsed\n 70124552 peak memory footprint\n", + "tool": "Graphia", + "dataset": "moderate", + "repetition": -1, + "warmup": true, + "dataset_sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 3000, + "memberships": 13200 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 1.4874919999999996, + "process_peak_rss_bytes": 689225728, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/mathlib.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 1.48 real 1.32 user 0.09 sys\n 689225728 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 46488 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 126 voluntary context switches\n 1096 involuntary context switches\n 25863171296 instructions retired\n 6151915355 cycles elapsed\n 566265272 peak memory footprint\n", + "tool": "Graphia", + "dataset": "mathlib", + "repetition": 0, + "warmup": false, + "dataset_sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 54290, + "memberships": 118066 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 0.3713696249999998, + "process_peak_rss_bytes": 137068544, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/moderate.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 0.36 real 0.29 user 0.03 sys\n 137068544 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 11741 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 11 voluntary context switches\n 1001 involuntary context switches\n 5738724275 instructions retired\n 1422303405 cycles elapsed\n 70534152 peak memory footprint\n", + "tool": "Graphia", + "dataset": "moderate", + "repetition": 0, + "warmup": false, + "dataset_sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 3000, + "memberships": 13200 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 0.324835457999999, + "process_peak_rss_bytes": 111542272, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/scientific.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 0.32 real 0.24 user 0.03 sys\n 111542272 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 10182 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 3 voluntary context switches\n 922 involuntary context switches\n 4801344125 instructions retired\n 1212986636 cycles elapsed\n 45073416 peak memory footprint\n", + "tool": "Graphia", + "dataset": "scientific", + "repetition": 0, + "warmup": false, + "dataset_sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 1419, + "memberships": 1550 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 0.3671234590000001, + "process_peak_rss_bytes": 136495104, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/moderate.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 0.36 real 0.29 user 0.03 sys\n 136495104 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 11710 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 8 voluntary context switches\n 979 involuntary context switches\n 5723731564 instructions retired\n 1416241488 cycles elapsed\n 69927920 peak memory footprint\n", + "tool": "Graphia", + "dataset": "moderate", + "repetition": 1, + "warmup": false, + "dataset_sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 3000, + "memberships": 13200 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 1.489542083, + "process_peak_rss_bytes": 690012160, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/mathlib.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 1.48 real 1.32 user 0.09 sys\n 690012160 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 46545 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 123 voluntary context switches\n 1079 involuntary context switches\n 25872743177 instructions retired\n 6178549402 cycles elapsed\n 567854496 peak memory footprint\n", + "tool": "Graphia", + "dataset": "mathlib", + "repetition": 1, + "warmup": false, + "dataset_sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 54290, + "memberships": 118066 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 0.3328365420000008, + "process_peak_rss_bytes": 111362048, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/scientific.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 0.32 real 0.24 user 0.03 sys\n 111362048 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 10173 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 6 voluntary context switches\n 934 involuntary context switches\n 4792869435 instructions retired\n 1217109778 cycles elapsed\n 44925936 peak memory footprint\n", + "tool": "Graphia", + "dataset": "scientific", + "repetition": 1, + "warmup": false, + "dataset_sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 1419, + "memberships": 1550 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 0.34016204199999933, + "process_peak_rss_bytes": 111689728, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/scientific.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 0.33 real 0.24 user 0.03 sys\n 111689728 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 10192 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 8 voluntary context switches\n 933 involuntary context switches\n 4801672135 instructions retired\n 1213583205 cycles elapsed\n 45270000 peak memory footprint\n", + "tool": "Graphia", + "dataset": "scientific", + "repetition": 2, + "warmup": false, + "dataset_sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 1419, + "memberships": 1550 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 0.3908611670000006, + "process_peak_rss_bytes": 137265152, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/moderate.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 0.38 real 0.29 user 0.03 sys\n 137265152 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 11753 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 4 voluntary context switches\n 1007 involuntary context switches\n 5718855464 instructions retired\n 1420333906 cycles elapsed\n 70747168 peak memory footprint\n", + "tool": "Graphia", + "dataset": "moderate", + "repetition": 2, + "warmup": false, + "dataset_sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 3000, + "memberships": 13200 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 1.4905674999999992, + "process_peak_rss_bytes": 677036032, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/mathlib.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 1.48 real 1.32 user 0.10 sys\n 677036032 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 45780 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 43 voluntary context switches\n 1111 involuntary context switches\n 25832163286 instructions retired\n 6183039697 cycles elapsed\n 553993680 peak memory footprint\n", + "tool": "Graphia", + "dataset": "mathlib", + "repetition": 2, + "warmup": false, + "dataset_sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 54290, + "memberships": 118066 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 0.3983062920000009, + "process_peak_rss_bytes": 137084928, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/moderate.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 0.39 real 0.29 user 0.03 sys\n 137084928 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 11694 page reclaims\n 48 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 4 voluntary context switches\n 996 involuntary context switches\n 5752121152 instructions retired\n 1432673259 cycles elapsed\n 70566944 peak memory footprint\n", + "tool": "Graphia", + "dataset": "moderate", + "repetition": 3, + "warmup": false, + "dataset_sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 3000, + "memberships": 13200 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 0.3323542909999979, + "process_peak_rss_bytes": 111509504, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/scientific.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 0.32 real 0.24 user 0.03 sys\n 111509504 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 10181 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 5 voluntary context switches\n 922 involuntary context switches\n 4794643842 instructions retired\n 1213015426 cycles elapsed\n 44991472 peak memory footprint\n", + "tool": "Graphia", + "dataset": "scientific", + "repetition": 3, + "warmup": false, + "dataset_sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 1419, + "memberships": 1550 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 1.5047855420000005, + "process_peak_rss_bytes": 688029696, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/mathlib.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 1.50 real 1.33 user 0.11 sys\n 688029696 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 46452 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 47 voluntary context switches\n 1142 involuntary context switches\n 25891464222 instructions retired\n 6244619084 cycles elapsed\n 564954528 peak memory footprint\n", + "tool": "Graphia", + "dataset": "mathlib", + "repetition": 3, + "warmup": false, + "dataset_sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 54290, + "memberships": 118066 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 0.461615124999998, + "process_peak_rss_bytes": 137166848, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/moderate.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 0.45 real 0.31 user 0.05 sys\n 137166848 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 11748 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 5 voluntary context switches\n 2065 involuntary context switches\n 5721510773 instructions retired\n 1533775018 cycles elapsed\n 70599712 peak memory footprint\n", + "tool": "Graphia", + "dataset": "moderate", + "repetition": 4, + "warmup": false, + "dataset_sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 3000, + "memberships": 13200 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 0.3922522910000019, + "process_peak_rss_bytes": 111607808, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/scientific.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 0.38 real 0.26 user 0.04 sys\n 111607808 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 10191 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 5 voluntary context switches\n 1609 involuntary context switches\n 4779904758 instructions retired\n 1295059093 cycles elapsed\n 45204488 peak memory footprint\n", + "tool": "Graphia", + "dataset": "scientific", + "repetition": 4, + "warmup": false, + "dataset_sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 1419, + "memberships": 1550 + } + }, + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 1.6109835419999996, + "process_peak_rss_bytes": 681738240, + "stdout": "Wrote /Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/graphia/inputs/mathlib.graphia successfully.\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Graphia.app: 1 (Operation not permitted)\nLoading dynamic plugins from /private/tmp/hyper-benchmark-competitors/Graphia.app/Contents/PlugIns...\n ...Correlation (libcorrelation.dylib)\n ...Generic (libgeneric.dylib)\n ...WebSearch (libwebsearch.dylib)\nLoading static plugins...\n ...none found\n 1.60 real 1.38 user 0.12 sys\n 681738240 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 46035 page reclaims\n 0 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 8 messages sent\n 4 messages received\n 0 signals received\n 50 voluntary context switches\n 3165 involuntary context switches\n 25882362662 instructions retired\n 6449255999 cycles elapsed\n 556041632 peak memory footprint\n", + "tool": "Graphia", + "dataset": "mathlib", + "repetition": 4, + "warmup": false, + "dataset_sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "correctness": { + "exact_incidence_and_node_attributes": true, + "document_metadata_retained": false, + "typed_native_display_fields_retained": false, + "saved_as_directed": true, + "coordinates_are_attributes_not_layout": true, + "coordinate_attributes_retained": true, + "vertices_and_hubs": 54290, + "memberships": 118066 + } + } +] diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-clamped-window-diagnostic-metadata.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-clamped-window-diagnostic-metadata.json new file mode 100644 index 0000000..389bed4 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-clamped-window-diagnostic-metadata.json @@ -0,0 +1,11 @@ +{ + "tool": "hypergodot", + "executable": "/tmp/hyper-benchmark-competitors/Godot.app/Contents/MacOS/Godot", + "timeout_seconds": 180, + "executable_sha256": "c7cccbf8fb143e34e02fd6521e09be2c2b974f0d5db080b19071c9c570718ccf", + "conditions": "M5Pro24GiB macOS27; lowpowermode0; no parallel benchmark/build; ordinary desktop background uncontrolled; display sleep disabled; idle sleep prevented; thermal telemetry unavailable; variable display cadence", + "peak_memory": "macOS /usr/bin/time -l whole-process RSS high-water in bytes", + "fresh_process_per_repetition": false, + "repetitions": 5, + "seed": 20261003 +} diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-clamped-window-diagnostic.json.gz b/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-clamped-window-diagnostic.json.gz new file mode 100644 index 0000000..fa0c2b1 Binary files /dev/null and b/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-clamped-window-diagnostic.json.gz differ diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-fullscreen-unmatched-diagnostic.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-fullscreen-unmatched-diagnostic.json new file mode 100644 index 0000000..e66b0fd --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-fullscreen-unmatched-diagnostic.json @@ -0,0 +1,67 @@ +{ + "adaptations": [ + "remove missing unused MaterialIconsDB autoload", + "reversible safe token IDs and line-order hyperedge IDs", + "unit weights and one baseline group", + "fixed XY projection of canonical 3D positions", + "override startup layout; production read_file/create_nodes/populate_edge_data/drawing", + "scripted camera and wall-clock process-boundary frame intervals", + "fullscreen to avoid macOS working-area window clamp" + ], + "allow_hidpi": false, + "color_seed": 20261003, + "dataset": "/Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/datasets/moderate.json", + "edge_width": 1.0, + "exact_parser_memberships_and_rendered_group_count": true, + "godot_version": { + "build": "official", + "hash": "ed1daf0bf001b61586d9930840f2f1394092c079", + "hex": 263938, + "major": 4, + "minor": 7, + "patch": 2, + "status": "stable", + "string": "4.7.2-stable (official)", + "timestamp": 0 + }, + "hyperedges": 2000.0, + "matched_requested_viewport": false, + "measured_frames": 2, + "memberships": 13200.0, + "metric": "wall-clock frame intervals, not GPU timestamps", + "node_radius": 30.0, + "point_count": 4.0, + "renderer": "forward_plus", + "renderer_driver": "metal", + "repetitions": [ + { + "frame_ms": [ + 8.428, + 8.336 + ], + "repetition": 0 + } + ], + "screen_dpi": 256, + "screen_scale": 1.0, + "screen_size": [ + 1512, + 982 + ], + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "source_commit": "49d03cd7540d2900cc8624048973ef430ff4e009", + "startup_construction_ms": 318.639, + "tool": "HyperGodot", + "vertices": 1000.0, + "viewport_size": [ + 1512.0, + 949.0 + ], + "vsync": "disabled by upstream project", + "warmup_frames": 120, + "window_mode": 3, + "window_size": [ + 1512, + 949 + ] +} \ No newline at end of file diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-hidpi-untimed-smoke.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-hidpi-untimed-smoke.json new file mode 100644 index 0000000..d4747dc --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-hidpi-untimed-smoke.json @@ -0,0 +1,71 @@ +{ + "adaptations": [ + "remove missing unused MaterialIconsDB autoload", + "reversible safe token IDs and line-order hyperedge IDs", + "unit weights and one baseline group", + "fixed XY projection of canonical 3D positions", + "override startup layout; production read_file/create_nodes/populate_edge_data/drawing", + "scripted camera and wall-clock process-boundary frame intervals", + "stock HiDPI enabled; physical viewport verified" + ], + "allow_hidpi": true, + "color_seed": 20261003, + "dataset": "/Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/datasets/moderate.json", + "edge_width": 1.0, + "exact_parser_memberships_and_rendered_group_count": true, + "godot_version": { + "build": "official", + "hash": "ed1daf0bf001b61586d9930840f2f1394092c079", + "hex": 263938, + "major": 4, + "minor": 7, + "patch": 2, + "status": "stable", + "string": "4.7.2-stable (official)", + "timestamp": 0 + }, + "hyperedges": 2000.0, + "matched_requested_viewport": true, + "measured_frames": 2, + "memberships": 13200.0, + "metric": "wall-clock frame intervals, not GPU timestamps", + "node_radius": 30.0, + "point_count": 4.0, + "render_target_size": [ + 1920.0, + 1080.0 + ], + "renderer": "forward_plus", + "renderer_driver": "metal", + "repetitions": [ + { + "frame_ms": [ + 6.772, + 3.957 + ], + "repetition": 0 + } + ], + "screen_dpi": 256, + "screen_scale": 2.0, + "screen_size": [ + 3024, + 1964 + ], + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "source_commit": "49d03cd7540d2900cc8624048973ef430ff4e009", + "startup_construction_ms": 190.184, + "tool": "HyperGodot", + "vertices": 1000.0, + "viewport_size": [ + 1920.0, + 1080.0 + ], + "vsync": "disabled by upstream project", + "warmup_frames": 120, + "window_mode": 0, + "window_size": [ + 1920, + 1080 + ] +} \ No newline at end of file diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-metadata.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-metadata.json new file mode 100644 index 0000000..dffad42 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-metadata.json @@ -0,0 +1,11 @@ +{ + "tool": "hypergodot", + "executable": "/tmp/hyper-benchmark-competitors/Godot.app/Contents/MacOS/Godot", + "timeout_seconds": 180, + "executable_sha256": "c7cccbf8fb143e34e02fd6521e09be2c2b974f0d5db080b19071c9c570718ccf", + "conditions": "M5Pro24GiB macOS27; lowpowermode0; no parallel benchmark/build; ordinary desktop background uncontrolled; idle sleep prevented; thermal telemetry unavailable; stock HiDPI enabled; physical render target verified1920x1080", + "peak_memory": "macOS /usr/bin/time -l whole-process RSS high-water in bytes", + "fresh_process_per_repetition": false, + "repetitions": 5, + "seed": 20261003 +} diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-raw.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-raw.json new file mode 100644 index 0000000..002855a --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/hypergodot-raw.json @@ -0,0 +1,1602 @@ +[ + { + "status": "ok", + "returncode": 0, + "process_wall_seconds": 19.184621915999998, + "process_peak_rss_bytes": 352894976, + "stdout": "Godot Engine v4.7.2.stable.official.ed1daf0bf - https://godotengine.org\nMetal 4.0 - Forward+ - Using Device #0: Apple - Apple M5 Pro (Apple9)\nRequested V-Sync mode: Disabled\n\nProject FPS: 119 (8.40 mspf)\nProject FPS: 120 (8.33 mspf)\nProject FPS: 120 (8.33 mspf)\nProject FPS: 119 (8.40 mspf)\nProject FPS: 119 (8.40 mspf)\nProject FPS: 118 (8.47 mspf)\nProject FPS: 119 (8.40 mspf)\nProject FPS: 119 (8.40 mspf)\nProject FPS: 118 (8.47 mspf)\nProject FPS: 119 (8.40 mspf)\nProject FPS: 196 (5.10 mspf)\nProject FPS: 120 (8.33 mspf)\nProject FPS: 119 (8.40 mspf)\nProject FPS: 120 (8.33 mspf)\nProject FPS: 117 (8.54 mspf)\n", + "stderr": "sandbox_extension_issue_file_to_process failed for /tmp/hyper-benchmark-competitors/Godot.app: 1 (Operation not permitted)\n 19.18 real 7.95 user 1.39 sys\n 352894976 maximum resident set size\n 0 average shared memory size\n 0 average unshared data size\n 0 average unshared stack size\n 23634 page reclaims\n 4689 page faults\n 0 swaps\n 0 block input operations\n 0 block output operations\n 3 messages sent\n 0 messages received\n 0 signals received\n 138 voluntary context switches\n 53759 involuntary context switches\n 142703056176 instructions retired\n 30290869492 cycles elapsed\n 517375176 peak memory footprint\n", + "tool": "HyperGodot", + "dataset": "moderate", + "warmup": false, + "measurements": { + "adaptations": [ + "remove missing unused MaterialIconsDB autoload", + "reversible safe token IDs and line-order hyperedge IDs", + "unit weights and one baseline group", + "fixed XY projection of canonical 3D positions", + "override startup layout; production read_file/create_nodes/populate_edge_data/drawing", + "scripted camera and wall-clock process-boundary frame intervals", + "stock HiDPI enabled; physical viewport verified" + ], + "allow_hidpi": true, + "color_seed": 20261003, + "dataset": "/Users/bregy/.codex/worktrees/ef9d/hyper/target/benchmark/datasets/moderate.json", + "edge_width": 1.0, + "exact_parser_memberships_and_rendered_group_count": true, + "godot_version": { + "build": "official", + "hash": "ed1daf0bf001b61586d9930840f2f1394092c079", + "hex": 263938, + "major": 4, + "minor": 7, + "patch": 2, + "status": "stable", + "string": "4.7.2-stable (official)", + "timestamp": 0 + }, + "hyperedges": 2000.0, + "matched_requested_viewport": true, + "measured_frames": 300, + "memberships": 13200.0, + "metric": "wall-clock frame intervals, not GPU timestamps", + "node_radius": 30.0, + "point_count": 4.0, + "render_target_size": [ + 1920.0, + 1080.0 + ], + "renderer": "forward_plus", + "renderer_driver": "metal", + "repetitions": [ + { + "frame_ms": [ + 8.144, + 8.373, + 8.382, + 8.344, + 8.249, + 8.586, + 8.056, + 8.489, + 8.257, + 8.341, + 8.281, + 8.562, + 8.304, + 8.306, + 8.845, + 7.756, + 8.294, + 8.356, + 8.284, + 8.354, + 8.433, + 8.329, + 8.207, + 8.461, + 11.656, + 5.554, + 7.799, + 8.322, + 8.464, + 8.324, + 8.243, + 8.424, + 8.247, + 8.383, + 8.271, + 8.441, + 8.261, + 8.341, + 8.269, + 8.413, + 8.286, + 8.302, + 8.507, + 23.287, + 6.516, + 5.596, + 6.23, + 7.959, + 8.395, + 8.252, + 8.397, + 8.654, + 8.354, + 8.216, + 8.361, + 8.328, + 8.387, + 8.216, + 8.476, + 8.151, + 8.417, + 8.317, + 8.409, + 8.215, + 8.301, + 8.243, + 8.489, + 8.253, + 8.404, + 8.243, + 8.308, + 8.313, + 8.411, + 8.406, + 8.294, + 8.36, + 8.363, + 11.825, + 5.255, + 7.786, + 8.302, + 8.521, + 8.203, + 8.353, + 8.603, + 8.147, + 8.277, + 8.384, + 8.384, + 8.27, + 8.251, + 8.486, + 8.324, + 8.207, + 8.391, + 8.196, + 8.732, + 8.025, + 8.506, + 8.115, + 20.222, + 5.362, + 4.639, + 10.364, + 4.025, + 5.326, + 8.249, + 8.294, + 8.362, + 8.284, + 8.379, + 8.591, + 8.404, + 8.186, + 8.452, + 8.176, + 8.506, + 8.214, + 8.477, + 8.261, + 8.39, + 8.235, + 8.443, + 8.23, + 8.444, + 8.239, + 8.314, + 8.234, + 8.442, + 8.222, + 8.412, + 8.45, + 8.348, + 8.759, + 7.623, + 8.452, + 8.343, + 8.348, + 8.349, + 8.313, + 8.395, + 8.2, + 8.494, + 11.976, + 5.693, + 10.469, + 7.131, + 9.552, + 6.108, + 9.566, + 8.351, + 9.504, + 6.936, + 8.333, + 8.53, + 8.208, + 5.997, + 8.718, + 8.439, + 8.162, + 8.417, + 8.269, + 21.304, + 4.618, + 3.967, + 10.517, + 3.531, + 5.831, + 8.321, + 8.334, + 8.345, + 8.508, + 8.313, + 8.168, + 8.462, + 8.423, + 8.227, + 8.398, + 8.405, + 8.304, + 8.341, + 8.329, + 8.227, + 8.565, + 8.14, + 8.31, + 8.436, + 8.241, + 8.177, + 8.608, + 8.399, + 8.38, + 8.051, + 8.352, + 8.224, + 8.437, + 8.417, + 8.263, + 8.348, + 8.266, + 8.404, + 8.156, + 8.301, + 8.377, + 8.343, + 8.292, + 8.478, + 8.275, + 8.369, + 8.223, + 8.422, + 8.34, + 8.375, + 8.298, + 8.35, + 8.308, + 8.543, + 8.111, + 8.563, + 8.334, + 18.888, + 5.569, + 4.973, + 10.862, + 4.221, + 5.212, + 8.312, + 8.293, + 8.368, + 8.179, + 13.804, + 4.001, + 7.085, + 8.426, + 8.38, + 8.401, + 8.44, + 8.097, + 8.808, + 8.325, + 8.441, + 11.75, + 4.895, + 8.329, + 8.225, + 8.313, + 8.401, + 8.133, + 8.669, + 8.201, + 8.395, + 8.3, + 8.422, + 8.382, + 7.709, + 8.529, + 8.407, + 8.382, + 8.322, + 8.222, + 8.475, + 8.152, + 8.569, + 8.139, + 8.43, + 8.416, + 8.278, + 8.236, + 8.386, + 8.297, + 8.445, + 8.267, + 8.425, + 8.283, + 8.357, + 8.215, + 8.444, + 17.647, + 7.205, + 6.729, + 9.028, + 5.906, + 14.409, + 5.573, + 5.166, + 10.476, + 3.876, + 5.48, + 8.396, + 8.149, + 8.423, + 8.226, + 8.373, + 8.269, + 8.761, + 8.215, + 8.438, + 8.129, + 8.462, + 8.256 + ], + "repetition": 0 + }, + { + "frame_ms": [ + 8.396, + 8.262, + 8.392, + 8.334, + 8.118, + 8.723, + 7.973, + 8.414, + 8.233, + 8.462, + 8.211, + 8.497, + 8.246, + 8.48, + 8.149, + 8.437, + 8.214, + 8.415, + 8.269, + 8.349, + 8.401, + 8.44, + 8.128, + 8.401, + 8.518, + 17.588, + 6.641, + 5.411, + 10.735, + 4.905, + 4.561, + 8.394, + 8.188, + 8.49, + 8.231, + 8.456, + 8.3, + 8.287, + 8.452, + 19.105, + 5.203, + 4.063, + 11.913, + 3.782, + 5.551, + 8.388, + 8.262, + 8.372, + 8.336, + 8.957, + 13.418, + 4.784, + 6.12, + 8.393, + 8.372, + 8.323, + 8.268, + 8.376, + 8.265, + 8.475, + 8.267, + 8.347, + 8.223, + 8.598, + 8.118, + 8.509, + 8.211, + 8.539, + 8.169, + 8.496, + 8.358, + 8.316, + 9.042, + 15.6, + 6.697, + 6.115, + 5.492, + 6.851, + 8.402, + 8.502, + 8.261, + 8.394, + 8.324, + 8.403, + 8.201, + 8.318, + 8.245, + 8.535, + 8.121, + 8.626, + 8.224, + 8.352, + 8.404, + 8.308, + 8.288, + 8.385, + 8.343, + 8.37, + 8.21, + 8.436, + 8.375, + 22.453, + 6.574, + 5.313, + 7.031, + 8.278, + 8.308, + 8.316, + 8.367, + 8.662, + 8.344, + 8.114, + 8.548, + 8.042, + 8.523, + 11.911, + 4.819, + 8.276, + 8.433, + 8.239, + 13.45, + 5.8, + 5.728, + 8.39, + 8.331, + 8.199, + 8.389, + 8.196, + 8.475, + 8.194, + 8.386, + 8.247, + 8.402, + 8.256, + 8.361, + 8.453, + 8.246, + 8.297, + 8.324, + 8.276, + 8.457, + 8.244, + 8.409, + 8.323, + 13.648, + 6.688, + 5.373, + 7.473, + 8.449, + 8.397, + 8.503, + 8.158, + 8.542, + 8.321, + 8.303, + 8.182, + 8.457, + 8.275, + 18.956, + 5.457, + 3.916, + 12.118, + 3.505, + 5.773, + 8.386, + 8.296, + 8.365, + 8.364, + 18.617, + 5.877, + 4.266, + 11.88, + 3.552, + 5.674, + 8.442, + 8.214, + 8.352, + 8.329, + 8.384, + 8.4, + 8.697, + 8.253, + 8.273, + 8.141, + 8.376, + 8.252, + 8.493, + 8.282, + 8.406, + 8.344, + 8.271, + 9.659, + 7.146, + 8.151, + 8.37, + 8.388, + 8.361, + 8.265, + 8.449, + 8.117, + 8.522, + 8.308, + 8.362, + 8.22, + 8.462, + 8.152, + 8.613, + 8.287, + 8.352, + 8.233, + 8.548, + 8.169, + 8.555, + 7.975, + 8.657, + 8.231, + 8.553, + 7.887, + 8.534, + 8.416, + 21.8, + 5.204, + 5.899, + 8.462, + 8.336, + 8.267, + 8.263, + 8.361, + 8.494, + 8.55, + 8.119, + 8.347, + 8.297, + 8.265, + 8.278, + 8.62, + 8.067, + 8.367, + 8.343, + 8.4, + 8.207, + 8.361, + 8.118, + 8.404, + 8.304, + 8.331, + 8.211, + 8.838, + 8.133, + 8.506, + 8.269, + 8.192, + 8.317, + 8.37, + 8.193, + 8.37, + 15.944, + 11.367, + 6.054, + 8.127, + 8.401, + 8.288, + 8.694, + 8.094, + 8.279, + 8.149, + 8.458, + 8.419, + 7.91, + 8.62, + 8.589, + 8.139, + 8.456, + 8.272, + 8.375, + 9.229, + 16.425, + 7.371, + 5.277, + 10.777, + 4.827, + 4.611, + 8.267, + 8.343, + 8.359, + 8.407, + 8.07, + 8.538, + 8.641, + 7.962, + 8.619, + 8.274, + 8.465, + 8.119, + 8.547, + 8.045, + 8.397, + 8.243, + 8.4, + 8.314 + ], + "repetition": 1 + }, + { + "frame_ms": [ + 8.486, + 8.312, + 8.34, + 8.28, + 8.347, + 8.474, + 8.226, + 8.307, + 9.244, + 7.378, + 8.307, + 8.313, + 8.382, + 8.435, + 8.279, + 8.247, + 8.199, + 8.594, + 8.088, + 8.503, + 8.129, + 8.536, + 8.099, + 8.415, + 8.265, + 8.466, + 8.367, + 16.129, + 6.907, + 9.198, + 5.749, + 5.758, + 6.386, + 8.382, + 8.253, + 8.335, + 8.408, + 16.525, + 7.69, + 6.507, + 9.556, + 3.544, + 5.698, + 8.41, + 8.389, + 8.391, + 8.241, + 8.394, + 8.536, + 8.35, + 8.197, + 8.493, + 8.16, + 8.508, + 8.182, + 8.503, + 8.238, + 8.425, + 8.139, + 8.371, + 8.211, + 8.427, + 8.418, + 8.309, + 8.29, + 8.511, + 8.151, + 8.452, + 8.191, + 8.628, + 8.238, + 8.281, + 8.162, + 8.628, + 8.19, + 8.44, + 8.178, + 8.476, + 8.399, + 8.288, + 8.263, + 8.283, + 8.274, + 8.433, + 8.19, + 8.526, + 8.222, + 16.231, + 5.397, + 10.711, + 4.685, + 4.806, + 8.09, + 8.604, + 8.27, + 22.902, + 4.486, + 5.646, + 8.345, + 8.277, + 8.338, + 8.309, + 8.722, + 8.252, + 15.116, + 4.032, + 5.838, + 8.183, + 8.274, + 8.645, + 8.408, + 8.194, + 8.438, + 8.115, + 8.529, + 8.154, + 8.364, + 8.229, + 8.352, + 8.236, + 8.376, + 8.344, + 8.448, + 8.178, + 8.448, + 8.857, + 7.549, + 8.438, + 8.454, + 8.126, + 8.489, + 8.275, + 8.428, + 8.316, + 8.457, + 8.16, + 8.448, + 8.234, + 8.443, + 8.219, + 8.415, + 8.538, + 7.971, + 8.498, + 8.329, + 8.28, + 8.394, + 8.273, + 8.404, + 8.295, + 8.383, + 8.257, + 8.49, + 8.411, + 8.386, + 8.209, + 22.378, + 5.705, + 5.552, + 8.155, + 8.094, + 8.345, + 8.192, + 8.393, + 8.33, + 8.557, + 8.26, + 8.421, + 8.279, + 8.394, + 8.25, + 8.422, + 8.177, + 8.527, + 8.224, + 8.422, + 8.201, + 8.438, + 8.129, + 8.455, + 8.296, + 8.296, + 8.275, + 8.405, + 8.373, + 8.278, + 8.34, + 8.346, + 8.364, + 8.316, + 8.238, + 8.397, + 8.256, + 8.575, + 8.288, + 8.199, + 8.284, + 8.404, + 8.334, + 8.291, + 8.342, + 8.627, + 8.058, + 8.672, + 8.052, + 8.226, + 8.476, + 8.318, + 8.341, + 8.373, + 8.077, + 8.721, + 8.007, + 24.363, + 5.218, + 4.718, + 7.44, + 10.989, + 5.719, + 8.157, + 8.397, + 8.347, + 8.578, + 13.953, + 4.991, + 5.833, + 8.238, + 8.279, + 8.365, + 8.471, + 8.47, + 8.471, + 8.207, + 8.272, + 8.353, + 8.246, + 8.443, + 8.454, + 8.265, + 8.367, + 8.151, + 8.346, + 8.448, + 8.259, + 8.029, + 10.215, + 6.758, + 9.319, + 7.434, + 8.309, + 8.241, + 8.36, + 8.379, + 8.36, + 8.353, + 8.292, + 8.388, + 8.295, + 8.343, + 8.239, + 8.35, + 8.268, + 8.633, + 8.315, + 8.322, + 8.192, + 8.443, + 8.233, + 8.291, + 8.428, + 8.447, + 8.166, + 8.32, + 8.391, + 23.915, + 7.187, + 5.82, + 5.36, + 7.534, + 8.395, + 8.487, + 8.134, + 8.267, + 8.425, + 8.275, + 8.368, + 8.368, + 8.248, + 8.312, + 8.346, + 8.417, + 8.34, + 8.49, + 8.147, + 8.428, + 8.272, + 8.331, + 8.425, + 8.229, + 8.481 + ], + "repetition": 2 + }, + { + "frame_ms": [ + 3.342, + 3.407, + 4.252, + 3.742, + 3.337, + 3.33, + 3.379, + 4.229, + 3.723, + 3.393, + 3.337, + 4.155, + 3.783, + 3.327, + 3.358, + 3.312, + 3.356, + 3.356, + 3.431, + 3.361, + 3.385, + 3.338, + 4.661, + 3.738, + 3.387, + 3.391, + 3.362, + 3.384, + 3.334, + 3.398, + 3.356, + 3.398, + 3.376, + 3.36, + 3.406, + 3.359, + 3.363, + 3.447, + 3.374, + 3.335, + 3.435, + 3.341, + 3.447, + 3.389, + 3.414, + 3.382, + 4.25, + 3.735, + 3.368, + 3.398, + 3.363, + 3.467, + 3.394, + 4.203, + 3.719, + 3.39, + 3.436, + 3.372, + 3.459, + 3.399, + 3.395, + 3.432, + 3.351, + 3.449, + 3.421, + 3.354, + 3.408, + 3.412, + 3.414, + 3.414, + 3.475, + 3.396, + 3.384, + 3.446, + 3.409, + 3.407, + 3.412, + 3.46, + 3.432, + 3.357, + 3.495, + 3.391, + 3.437, + 3.429, + 3.436, + 3.379, + 4.21, + 3.767, + 3.456, + 3.435, + 3.385, + 3.921, + 3.815, + 3.466, + 3.43, + 3.413, + 4.107, + 3.776, + 7.2, + 7.936, + 7.846, + 7.526, + 3.774, + 3.393, + 4.383, + 3.732, + 3.481, + 3.402, + 4.511, + 3.719, + 3.446, + 3.486, + 3.458, + 4.201, + 3.733, + 3.474, + 3.473, + 4.542, + 3.769, + 7.211, + 4.057, + 3.48, + 3.45, + 4.466, + 13.49, + 8.508, + 8.513, + 8.349, + 8.307, + 8.355, + 8.266, + 8.463, + 8.333, + 8.37, + 8.234, + 8.475, + 8.317, + 8.93, + 7.833, + 8.334, + 8.344, + 8.203, + 8.377, + 8.316, + 8.388, + 8.319, + 8.352, + 8.389, + 8.3, + 8.269, + 8.412, + 8.452, + 8.161, + 8.435, + 8.234, + 8.456, + 8.193, + 8.384, + 8.252, + 8.424, + 8.31, + 8.427, + 8.211, + 8.39, + 8.291, + 8.392, + 8.373, + 8.337, + 8.232, + 19.668, + 4.611, + 4.228, + 11.782, + 3.444, + 5.803, + 8.562, + 8.507, + 8.393, + 8.334, + 7.944, + 8.699, + 10.687, + 5.681, + 8.414, + 8.159, + 8.35, + 8.31, + 8.539, + 8.069, + 8.288, + 8.539, + 8.141, + 8.527, + 8.161, + 8.459, + 8.181, + 8.455, + 8.228, + 8.471, + 8.314, + 8.281, + 8.264, + 8.362, + 8.307, + 8.34, + 8.271, + 8.317, + 8.689, + 8.0, + 8.622, + 7.991, + 8.404, + 8.309, + 8.598, + 8.053, + 8.641, + 8.026, + 8.567, + 8.071, + 8.638, + 7.976, + 8.636, + 8.069, + 8.367, + 8.256, + 8.306, + 8.418, + 14.033, + 7.188, + 4.0, + 8.125, + 8.604, + 8.07, + 8.553, + 8.098, + 8.29, + 8.338, + 8.347, + 8.374, + 8.596, + 8.071, + 8.505, + 8.156, + 8.501, + 8.127, + 8.729, + 7.936, + 8.332, + 8.344, + 8.28, + 8.383, + 8.621, + 8.009, + 8.378, + 8.329, + 11.358, + 6.984, + 6.758, + 8.233, + 8.54, + 8.885, + 10.222, + 6.716, + 7.22, + 8.351, + 8.425, + 8.228, + 8.371, + 8.411, + 8.547, + 8.072, + 8.531, + 8.203, + 8.984, + 8.083, + 7.984, + 8.502, + 8.259, + 8.423, + 8.353, + 8.305, + 8.282, + 8.492, + 8.328, + 8.463, + 9.439, + 7.496, + 8.15, + 8.32, + 17.136, + 7.177, + 6.708, + 9.324, + 6.236, + 5.366, + 6.349, + 8.175, + 8.315, + 8.418, + 8.557 + ], + "repetition": 3 + }, + { + "frame_ms": [ + 8.034, + 8.495, + 8.193, + 8.515, + 8.286, + 8.411, + 8.079, + 8.407, + 8.396, + 8.22, + 11.506, + 5.8, + 7.735, + 8.457, + 8.35, + 8.326, + 8.46, + 8.157, + 8.348, + 8.338, + 8.288, + 8.471, + 8.227, + 8.322, + 8.351, + 8.359, + 9.363, + 7.379, + 8.291, + 8.355, + 8.214, + 8.49, + 8.21, + 8.322, + 8.433, + 8.384, + 8.309, + 8.315, + 8.436, + 8.263, + 8.295, + 8.237, + 8.419, + 8.358, + 9.025, + 7.735, + 18.674, + 5.693, + 5.771, + 10.274, + 4.867, + 4.744, + 8.288, + 8.275, + 8.246, + 8.676, + 8.234, + 8.336, + 12.66, + 4.336, + 7.73, + 8.387, + 9.412, + 7.22, + 8.718, + 8.345, + 8.245, + 8.326, + 8.3, + 8.376, + 8.257, + 8.344, + 8.186, + 8.402, + 8.379, + 8.228, + 8.43, + 8.377, + 8.257, + 8.338, + 8.43, + 8.18, + 9.505, + 7.218, + 8.369, + 8.269, + 8.306, + 8.407, + 8.36, + 8.311, + 8.263, + 8.371, + 8.495, + 8.175, + 8.265, + 8.44, + 8.304, + 8.324, + 8.283, + 8.32, + 8.4, + 8.279, + 8.327, + 8.385, + 10.345, + 6.359, + 8.427, + 8.134, + 17.401, + 26.671, + 4.314, + 5.048, + 4.946, + 8.118, + 8.522, + 8.181, + 8.26, + 8.418, + 8.357, + 16.284, + 6.951, + 15.72, + 3.818, + 7.086, + 8.291, + 8.214, + 8.402, + 8.313, + 8.367, + 8.457, + 8.38, + 8.302, + 8.284, + 8.392, + 8.404, + 8.3, + 8.443, + 8.185, + 8.334, + 8.253, + 8.48, + 8.206, + 8.377, + 8.277, + 8.444, + 8.235, + 8.364, + 8.264, + 8.36, + 8.439, + 8.221, + 8.29, + 8.616, + 8.205, + 8.429, + 8.281, + 8.457, + 8.304, + 8.423, + 8.268, + 8.418, + 8.263, + 8.43, + 16.965, + 7.384, + 6.892, + 9.15, + 6.405, + 5.56, + 5.906, + 8.107, + 8.277, + 8.335, + 8.336, + 8.413, + 11.562, + 5.052, + 8.275, + 8.362, + 8.424, + 8.599, + 8.199, + 8.41, + 8.399, + 8.304, + 8.393, + 8.453, + 9.542, + 7.006, + 8.465, + 8.184, + 8.467, + 8.351, + 8.299, + 8.288, + 8.18, + 8.361, + 8.266, + 8.334, + 8.309, + 8.3, + 8.282, + 8.478, + 8.515, + 8.173, + 8.38, + 8.242, + 8.387, + 8.358, + 8.168, + 8.394, + 8.339, + 8.414, + 8.284, + 8.334, + 8.358, + 8.284, + 8.282, + 8.334, + 8.36, + 8.368, + 8.351, + 8.278, + 8.382, + 8.283, + 19.108, + 6.419, + 6.167, + 8.859, + 4.996, + 4.541, + 8.203, + 8.241, + 8.37, + 8.779, + 14.348, + 9.777, + 3.858, + 12.12, + 3.544, + 12.283, + 5.399, + 4.962, + 8.384, + 8.411, + 8.239, + 8.508, + 8.286, + 8.374, + 8.199, + 8.449, + 8.395, + 8.402, + 8.256, + 8.341, + 8.373, + 8.245, + 8.216, + 8.404, + 8.226, + 8.322, + 8.425, + 8.291, + 8.434, + 8.239, + 8.351, + 8.478, + 8.288, + 8.23, + 8.493, + 8.277, + 8.388, + 8.255, + 8.395, + 8.317, + 8.339, + 8.296, + 8.325, + 8.241, + 8.475, + 8.371, + 8.246, + 8.379, + 12.586, + 22.231, + 6.512, + 5.981, + 4.936, + 6.045, + 8.345, + 8.259, + 8.355, + 8.33, + 9.192, + 13.543, + 5.021, + 5.522, + 8.019, + 8.315, + 8.442 + ], + "repetition": 4 + } + ], + "screen_dpi": 256, + "screen_scale": 2.0, + "screen_size": [ + 3024, + 1964 + ], + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "source_commit": "49d03cd7540d2900cc8624048973ef430ff4e009", + "startup_construction_ms": 188.342, + "tool": "HyperGodot", + "vertices": 1000.0, + "viewport_size": [ + 1920.0, + 1080.0 + ], + "vsync": "disabled by upstream project", + "warmup_frames": 120, + "window_mode": 0, + "window_size": [ + 1920, + 1080 + ] + } + } +] diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/native-interactive-metadata.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-interactive-metadata.json new file mode 100644 index 0000000..68a1480 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-interactive-metadata.json @@ -0,0 +1,141 @@ +{ + "track": "native", + "seed": 20261003, + "repetitions": 5, + "fresh_process_per_run": true, + "warmup": "120 render frames per run", + "schedule": [ + [ + 0, + "mathlib", + true, + false + ], + [ + 0, + "mathlib", + true, + true + ], + [ + 0, + "moderate", + true, + true + ], + [ + 0, + "moderate", + true, + false + ], + [ + 1, + "mathlib", + true, + true + ], + [ + 1, + "mathlib", + true, + false + ], + [ + 1, + "moderate", + true, + false + ], + [ + 1, + "moderate", + true, + true + ], + [ + 2, + "mathlib", + true, + false + ], + [ + 2, + "moderate", + true, + false + ], + [ + 2, + "mathlib", + true, + true + ], + [ + 2, + "moderate", + true, + true + ], + [ + 3, + "moderate", + true, + false + ], + [ + 3, + "mathlib", + true, + false + ], + [ + 3, + "moderate", + true, + true + ], + [ + 3, + "mathlib", + true, + true + ], + [ + 4, + "moderate", + true, + true + ], + [ + 4, + "mathlib", + true, + false + ], + [ + 4, + "mathlib", + true, + true + ], + [ + 4, + "moderate", + true, + false + ] + ], + "commit": "33c34621a898b6c9d1d11ad4ddae3ad29ccf1855", + "binary_sha256": "19fcc7d83c98a0bf5669d47cc6883cf51d0e45d6341e6d0656000d20a3b39cee", + "conditions": "M5Pro24GiB macOS27; lowpowermode0; no parallel benchmark/build; ordinary desktop background uncontrolled; display sleep disabled; idle sleep prevented; thermal telemetry unavailable; variable display cadence", + "peak_memory": "/usr/bin/time -l process RSS high-water; bytes on macOS; includes startup", + "build_profile": "release", + "projection": "star", + "labels": true, + "only_hulls": true, + "live_datasets": [ + "moderate", + "mathlib" + ] +} diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/native-interactive-raw.json.gz b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-interactive-raw.json.gz new file mode 100644 index 0000000..db6710d Binary files /dev/null and b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-interactive-raw.json.gz differ diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/native-metadata.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-metadata.json new file mode 100644 index 0000000..1b767ea --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-metadata.json @@ -0,0 +1,500 @@ +{ + "track": "native", + "seed": 20261003, + "repetitions": 5, + "fresh_process_per_run": true, + "warmup": "120 render frames per run", + "schedule": [ + [ + 0, + "scientific", + false, + false + ], + [ + 0, + "mathlib", + false, + false + ], + [ + 0, + "mathlib", + true, + false + ], + [ + 0, + "scientific", + true, + false + ], + [ + 0, + "high-arity-100", + false, + false + ], + [ + 0, + "large", + true, + false + ], + [ + 0, + "moderate", + false, + true + ], + [ + 0, + "moderate", + true, + false + ], + [ + 0, + "high-arity-1000", + false, + false + ], + [ + 0, + "high-arity-10", + false, + false + ], + [ + 0, + "high-arity-100", + true, + false + ], + [ + 0, + "large", + false, + false + ], + [ + 0, + "high-arity-10", + true, + false + ], + [ + 0, + "moderate", + true, + true + ], + [ + 0, + "high-arity-1000", + true, + false + ], + [ + 0, + "moderate", + false, + false + ], + [ + 1, + "moderate", + false, + true + ], + [ + 1, + "scientific", + true, + false + ], + [ + 1, + "high-arity-100", + true, + false + ], + [ + 1, + "large", + true, + false + ], + [ + 1, + "high-arity-10", + false, + false + ], + [ + 1, + "mathlib", + true, + false + ], + [ + 1, + "high-arity-10", + true, + false + ], + [ + 1, + "moderate", + false, + false + ], + [ + 1, + "high-arity-1000", + false, + false + ], + [ + 1, + "high-arity-1000", + true, + false + ], + [ + 1, + "large", + false, + false + ], + [ + 1, + "scientific", + false, + false + ], + [ + 1, + "moderate", + true, + false + ], + [ + 1, + "mathlib", + false, + false + ], + [ + 1, + "high-arity-100", + false, + false + ], + [ + 1, + "moderate", + true, + true + ], + [ + 2, + "moderate", + true, + false + ], + [ + 2, + "mathlib", + true, + false + ], + [ + 2, + "mathlib", + false, + false + ], + [ + 2, + "high-arity-100", + true, + false + ], + [ + 2, + "high-arity-100", + false, + false + ], + [ + 2, + "large", + true, + false + ], + [ + 2, + "moderate", + true, + true + ], + [ + 2, + "scientific", + true, + false + ], + [ + 2, + "high-arity-1000", + false, + false + ], + [ + 2, + "large", + false, + false + ], + [ + 2, + "high-arity-10", + false, + false + ], + [ + 2, + "moderate", + false, + false + ], + [ + 2, + "moderate", + false, + true + ], + [ + 2, + "scientific", + false, + false + ], + [ + 2, + "high-arity-10", + true, + false + ], + [ + 2, + "high-arity-1000", + true, + false + ], + [ + 3, + "high-arity-10", + true, + false + ], + [ + 3, + "moderate", + false, + false + ], + [ + 3, + "high-arity-100", + false, + false + ], + [ + 3, + "moderate", + true, + false + ], + [ + 3, + "high-arity-1000", + false, + false + ], + [ + 3, + "moderate", + false, + true + ], + [ + 3, + "moderate", + true, + true + ], + [ + 3, + "large", + false, + false + ], + [ + 3, + "large", + true, + false + ], + [ + 3, + "high-arity-100", + true, + false + ], + [ + 3, + "mathlib", + true, + false + ], + [ + 3, + "high-arity-10", + false, + false + ], + [ + 3, + "mathlib", + false, + false + ], + [ + 3, + "scientific", + true, + false + ], + [ + 3, + "high-arity-1000", + true, + false + ], + [ + 3, + "scientific", + false, + false + ], + [ + 4, + "mathlib", + true, + false + ], + [ + 4, + "high-arity-100", + false, + false + ], + [ + 4, + "moderate", + true, + true + ], + [ + 4, + "moderate", + false, + true + ], + [ + 4, + "high-arity-100", + true, + false + ], + [ + 4, + "high-arity-10", + true, + false + ], + [ + 4, + "high-arity-10", + false, + false + ], + [ + 4, + "mathlib", + false, + false + ], + [ + 4, + "scientific", + false, + false + ], + [ + 4, + "moderate", + false, + false + ], + [ + 4, + "scientific", + true, + false + ], + [ + 4, + "high-arity-1000", + false, + false + ], + [ + 4, + "moderate", + true, + false + ], + [ + 4, + "large", + false, + false + ], + [ + 4, + "large", + true, + false + ], + [ + 4, + "high-arity-1000", + true, + false + ] + ], + "commit": "33c34621a898b6c9d1d11ad4ddae3ad29ccf1855", + "binary_sha256": "19fcc7d83c98a0bf5669d47cc6883cf51d0e45d6341e6d0656000d20a3b39cee", + "conditions": "M5Pro 24GiB macOS27; lowpowermode0; no parallel benchmark/build; ordinary desktop background uncontrolled; thermal telemetry unavailable; variable display cadence", + "peak_memory": "/usr/bin/time -l process RSS high-water; bytes on macOS; includes startup", + "build_profile": "release", + "projection": "bipartite", + "labels": false, + "only_hulls": false, + "live_datasets": [ + "moderate" + ] +} diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/native-raw.json.gz b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-raw.json.gz new file mode 100644 index 0000000..dba55cb Binary files /dev/null and b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-raw.json.gz differ diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/native-star-metadata.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-star-metadata.json new file mode 100644 index 0000000..5afe171 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-star-metadata.json @@ -0,0 +1,140 @@ +{ + "track": "native", + "seed": 20261003, + "repetitions": 5, + "fresh_process_per_run": true, + "warmup": "120 render frames per run", + "schedule": [ + [ + 0, + "mathlib", + true, + false + ], + [ + 0, + "mathlib", + true, + true + ], + [ + 0, + "mathlib", + false, + true + ], + [ + 0, + "mathlib", + false, + false + ], + [ + 1, + "mathlib", + true, + true + ], + [ + 1, + "mathlib", + true, + false + ], + [ + 1, + "mathlib", + false, + false + ], + [ + 1, + "mathlib", + false, + true + ], + [ + 2, + "mathlib", + true, + false + ], + [ + 2, + "mathlib", + false, + false + ], + [ + 2, + "mathlib", + true, + true + ], + [ + 2, + "mathlib", + false, + true + ], + [ + 3, + "mathlib", + false, + false + ], + [ + 3, + "mathlib", + true, + false + ], + [ + 3, + "mathlib", + false, + true + ], + [ + 3, + "mathlib", + true, + true + ], + [ + 4, + "mathlib", + false, + true + ], + [ + 4, + "mathlib", + true, + false + ], + [ + 4, + "mathlib", + true, + true + ], + [ + 4, + "mathlib", + false, + false + ] + ], + "commit": "33c34621a898b6c9d1d11ad4ddae3ad29ccf1855", + "binary_sha256": "19fcc7d83c98a0bf5669d47cc6883cf51d0e45d6341e6d0656000d20a3b39cee", + "conditions": "M5Pro 24GiB macOS27; lowpowermode0; no parallel benchmark/build; ordinary desktop background uncontrolled; display sleep disabled; idle sleep prevented; thermal telemetry unavailable; variable display cadence", + "peak_memory": "/usr/bin/time -l process RSS high-water; bytes on macOS; includes startup", + "build_profile": "release", + "projection": "star", + "labels": false, + "only_hulls": false, + "live_datasets": [ + "mathlib" + ] +} diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/native-star-raw.json.gz b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-star-raw.json.gz new file mode 100644 index 0000000..31096e6 Binary files /dev/null and b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-star-raw.json.gz differ diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/native-untimed-render-validation.png b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-untimed-render-validation.png new file mode 100644 index 0000000..9c17509 Binary files /dev/null and b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-untimed-render-validation.png differ diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/native-untimed-smoke.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-untimed-smoke.json new file mode 100644 index 0000000..102b9ec --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/native-untimed-smoke.json @@ -0,0 +1,70 @@ +{ + "dataset": "correctness", + "dpr": 1, + "frame_interval_ms": [ + 1.423792, + 1.7121250000000001, + 1.430375, + 1.217917, + 1.32375, + 1.3180830000000001, + 1.2965419999999999, + 1.326625, + 1.274958, + 1.486375, + 1.692833, + 1.40825 + ], + "frames": 12, + "hud": true, + "hull_opacity": 0.28, + "hull_topology_interval": 8, + "hull_vertex_cap": 24, + "hull_wireframe": false, + "hulls": true, + "label_policy": "default Capped; count threshold250; hovered/selected exemptions", + "labels": true, + "layout_step_ms": [ + 0.008583, + 0.007458, + 0.011916000000000001, + 0.0067079999999999996, + 0.010625, + 0.007625, + 0.006416, + 0.006542, + 0.006333, + 0.023708, + 0.006791, + 0.007583, + 0.00625, + 0.005875, + 0.010916, + 0.006375 + ], + "line_budget": 0, + "line_opacity": 0.1, + "live_layout": true, + "live_solver_steps_per_frame": 1, + "measurement": "CPU wall intervals; no GPU timer, hardware input or presentation timestamp", + "native_parse_ms": 0.247708, + "node_size": 2, + "nodes": 30, + "picking": true, + "present_mode": "AutoVsync", + "profile": "release", + "projection": "star", + "projection_ms": 0.06658299999999999, + "rendered_lines": 2, + "scene_links": 0, + "tool": "hyper", + "update_commit_ms": [ + 0.011042 + ], + "update_next_frame_ms": 1.400584, + "warmup_frames": 120, + "window": [ + 1920, + 1080 + ] +} \ No newline at end of file diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-consolidation.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-consolidation.json new file mode 100644 index 0000000..280d782 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-consolidation.json @@ -0,0 +1,12 @@ +{ + "source": "scientific-raw.json", + "main_source": "target/benchmark/science/results/raw.json", + "replacement_source": "target/benchmark/science/hnx-final/raw.json", + "replaced_cases": "All HNX construct and plot repetitions (including warmup) for correctness, moderate and scientific; clean repeat after fixing alpha configuration. Other cases retain original observations.", + "excluded_diagnostics": [ + "discarded-checker-run", + "discarded-validation-overhead" + ], + "measured_rows": 165, + "warmup_rows": 33 +} diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-hnx-repeat-metadata.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-hnx-repeat-metadata.json new file mode 100644 index 0000000..0a6208f --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-hnx-repeat-metadata.json @@ -0,0 +1,338 @@ +{ + "seed": 20261003, + "warmup_per_combination": 1, + "repetitions": 5, + "untimed_validation": false, + "platform": "macOS-27.0-arm64-arm-64bit", + "python": "3.12.13 (main, May 10 2026, 19:20:41) [Clang 22.1.3 ]", + "pins": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "installed_packages": { + "numpy": "2.5.3", + "scikit-learn": "1.9.1", + "cloudpickle": "3.1.2", + "kiwisolver": "1.5.1", + "xgi": "0.10.2", + "fonttools": "4.66.1", + "packaging": "26.3", + "networkx": "3.7", + "joblib": "1.6.0", + "hypergraphx": "1.8.0", + "requests": "2.34.2", + "texttable": "1.7.0", + "pytz": "2026.4", + "tzdata": "2026.5", + "contourpy": "1.4.0", + "tqdm": "4.70.1", + "urllib3": "2.8.0", + "python-dateutil": "2.9.0.post0", + "xopen": "2.1.0", + "matplotlib": "3.11.2", + "certifi": "2026.7.22", + "cycler": "0.12.1", + "bitsets": "0.9.1", + "hypernetx": "2.4.3", + "pandas": "2.3.3", + "backports.zstd": "1.7.0", + "threadpoolctl": "3.7.0", + "graphviz": "0.21", + "concepts": "0.9.2", + "fastjsonschema": "2.22.2", + "decorator": "5.3.1", + "HeapDict": "1.0.1", + "charset-normalizer": "3.5.2", + "pillow": "12.3.0", + "pyparsing": "3.3.3", + "igraph": "1.0.0", + "six": "1.17.0", + "scipy": "1.18.1", + "narwhals": "2.26.0", + "idna": "3.20" + }, + "documentation": { + "hypernetx": "https://hypernetx.readthedocs.io/en/latest/classes/classes.html", + "xgi": "https://xgi.readthedocs.io/en/stable/api/core/xgi.core.hypergraph.Hypergraph.html", + "hypergraphx": "https://hypergraphx.readthedocs.io/en/latest/api/hypergraphx.core.html" + }, + "schedule": [ + { + "order": 0, + "repetition": -1, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 1, + "repetition": -1, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 2, + "repetition": -1, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 3, + "repetition": -1, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 4, + "repetition": -1, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 5, + "repetition": -1, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 6, + "repetition": 0, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 7, + "repetition": 0, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 8, + "repetition": 0, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 9, + "repetition": 0, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 10, + "repetition": 0, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 11, + "repetition": 0, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 12, + "repetition": 1, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 13, + "repetition": 1, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 14, + "repetition": 1, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 15, + "repetition": 1, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 16, + "repetition": 1, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 17, + "repetition": 1, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 18, + "repetition": 2, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 19, + "repetition": 2, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 20, + "repetition": 2, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 21, + "repetition": 2, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 22, + "repetition": 2, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 23, + "repetition": 2, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 24, + "repetition": 3, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 25, + "repetition": 3, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 26, + "repetition": 3, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 27, + "repetition": 3, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 28, + "repetition": 3, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 29, + "repetition": 3, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 30, + "repetition": 4, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 31, + "repetition": 4, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 32, + "repetition": 4, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 33, + "repetition": 4, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 34, + "repetition": 4, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 35, + "repetition": 4, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "plot" + } + ], + "settings": { + "resolution": [ + 1920, + 1080 + ], + "dpi": 100, + "backend": "Agg", + "labels": false, + "layout": "none; canonical XYZ projected to XY", + "thread_limit": 1, + "hyperedge_alpha": 0.15, + "hypergraphx_smoothing_refinements": 12, + "hypergraphx_polygon_expansion_triangle": 2.5, + "hypergraphx_polygon_expansion_other": 1.8 + }, + "limitations": [ + "Different documented polygon/hull artists; no identical raster workload claim", + "No static figure vs interactive FPS ranking", + "Peak RSS includes imports", + "power/thermal/background conditions recorded by coordinator", + "HGX canonical edge IDs stored as metadata; native IDs differ" + ] +} \ No newline at end of file diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-independent-validation.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-independent-validation.json new file mode 100644 index 0000000..aa4ad16 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-independent-validation.json @@ -0,0 +1,750 @@ +[ + { + "dataset": "correctness", + "tool": "hypernetx", + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n13", + "n29" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 14, + "native_multiplicity_preserved": false, + "missing_edges": [ + "g0" + ], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "correctness", + "tool": "xgi", + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "correctness", + "tool": "hypergraphx", + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "high-arity-10", + "tool": "hypernetx", + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n103", + "n108", + "n117", + "n124", + "n133", + "n153", + "n157", + "n165", + "n180", + "n186", + "n189", + "n193", + "n196", + "n197", + "n203", + "n221", + "n222", + "n259", + "n263", + "n270", + "n280", + "n287", + "n302", + "n304", + "n307", + "n314", + "n33", + "n335", + "n359", + "n363", + "n364", + "n369", + "n380", + "n381", + "n394", + "n398", + "n404", + "n407", + "n41", + "n412", + "n413", + "n419", + "n432", + "n435", + "n436", + "n438", + "n448", + "n449", + "n452", + "n461", + "n463", + "n465", + "n472", + "n473", + "n475", + "n48", + "n482", + "n484", + "n489", + "n495", + "n508", + "n527", + "n536", + "n542", + "n546", + "n551", + "n564", + "n57", + "n571", + "n575", + "n576", + "n578", + "n60", + "n614", + "n619", + "n622", + "n623", + "n630", + "n632", + "n636", + "n639", + "n659", + "n662", + "n667", + "n674", + "n675", + "n70", + "n700", + "n714", + "n72", + "n723", + "n728", + "n733", + "n737", + "n744", + "n748", + "n758", + "n763", + "n772", + "n787", + "n789", + "n790", + "n804", + "n805", + "n81", + "n819", + "n828", + "n831", + "n84", + "n840", + "n86", + "n869", + "n871", + "n88", + "n892", + "n899", + "n904", + "n905", + "n92", + "n920", + "n922", + "n938", + "n953", + "n96", + "n967", + "n99" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "high-arity-10", + "tool": "xgi", + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "high-arity-10", + "tool": "hypergraphx", + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "high-arity-100", + "tool": "hypernetx", + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "high-arity-100", + "tool": "xgi", + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "high-arity-100", + "tool": "hypergraphx", + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "high-arity-1000", + "tool": "hypernetx", + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "high-arity-1000", + "tool": "xgi", + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "high-arity-1000", + "tool": "hypergraphx", + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 1, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "large", + "tool": "hypernetx", + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n1320", + "n1441", + "n4022", + "n4917", + "n5125", + "n5131", + "n5826", + "n6824", + "n7144", + "n7458", + "n7825" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "large", + "tool": "xgi", + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "large", + "tool": "hypergraphx", + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "mathlib", + "tool": "hypernetx", + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "MathlibTest.Tactic.Grind.AC", + "MathlibTest.Tactic.Grind.CC", + "MathlibTest.nomatch" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "mathlib", + "tool": "xgi", + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "mathlib", + "tool": "hypergraphx", + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "moderate", + "tool": "hypernetx", + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "moderate", + "tool": "xgi", + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "moderate", + "tool": "hypergraphx", + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "scientific", + "tool": "hypernetx", + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "scientific", + "tool": "xgi", + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + }, + { + "dataset": "scientific", + "tool": "hypergraphx", + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "input_unmutated": true, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 481, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + } + } +] diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-metadata.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-metadata.json new file mode 100644 index 0000000..8d5f0d8 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-metadata.json @@ -0,0 +1,1472 @@ +{ + "seed": 20261003, + "warmup_per_combination": 1, + "repetitions": 5, + "untimed_validation": false, + "platform": "macOS-27.0-arm64-arm-64bit", + "python": "3.12.13 (main, May 10 2026, 19:20:41) [Clang 22.1.3 ]", + "pins": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "installed_packages": { + "numpy": "2.5.3", + "scikit-learn": "1.9.1", + "cloudpickle": "3.1.2", + "kiwisolver": "1.5.1", + "xgi": "0.10.2", + "fonttools": "4.66.1", + "packaging": "26.3", + "networkx": "3.7", + "joblib": "1.6.0", + "hypergraphx": "1.8.0", + "requests": "2.34.2", + "texttable": "1.7.0", + "pytz": "2026.4", + "tzdata": "2026.5", + "contourpy": "1.4.0", + "tqdm": "4.70.1", + "urllib3": "2.8.0", + "python-dateutil": "2.9.0.post0", + "xopen": "2.1.0", + "matplotlib": "3.11.2", + "certifi": "2026.7.22", + "cycler": "0.12.1", + "bitsets": "0.9.1", + "hypernetx": "2.4.3", + "pandas": "2.3.3", + "backports.zstd": "1.7.0", + "threadpoolctl": "3.7.0", + "graphviz": "0.21", + "concepts": "0.9.2", + "fastjsonschema": "2.22.2", + "decorator": "5.3.1", + "HeapDict": "1.0.1", + "charset-normalizer": "3.5.2", + "pillow": "12.3.0", + "pyparsing": "3.3.3", + "igraph": "1.0.0", + "six": "1.17.0", + "scipy": "1.18.1", + "narwhals": "2.26.0", + "idna": "3.20" + }, + "documentation": { + "hypernetx": "https://hypernetx.readthedocs.io/en/latest/classes/classes.html", + "xgi": "https://xgi.readthedocs.io/en/stable/api/core/xgi.core.hypergraph.Hypergraph.html", + "hypergraphx": "https://hypergraphx.readthedocs.io/en/latest/api/hypergraphx.core.html" + }, + "schedule": [ + { + "order": 0, + "repetition": -1, + "dataset": "high-arity-1000", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 1, + "repetition": -1, + "dataset": "high-arity-100", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 2, + "repetition": -1, + "dataset": "correctness", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 3, + "repetition": -1, + "dataset": "high-arity-10", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 4, + "repetition": -1, + "dataset": "high-arity-1000", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 5, + "repetition": -1, + "dataset": "high-arity-10", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 6, + "repetition": -1, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 7, + "repetition": -1, + "dataset": "moderate", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 8, + "repetition": -1, + "dataset": "large", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 9, + "repetition": -1, + "dataset": "high-arity-10", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 10, + "repetition": -1, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 11, + "repetition": -1, + "dataset": "correctness", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 12, + "repetition": -1, + "dataset": "scientific", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 13, + "repetition": -1, + "dataset": "mathlib", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 14, + "repetition": -1, + "dataset": "mathlib", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 15, + "repetition": -1, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 16, + "repetition": -1, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 17, + "repetition": -1, + "dataset": "scientific", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 18, + "repetition": -1, + "dataset": "scientific", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 19, + "repetition": -1, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 20, + "repetition": -1, + "dataset": "moderate", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 21, + "repetition": -1, + "dataset": "high-arity-1000", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 22, + "repetition": -1, + "dataset": "mathlib", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 23, + "repetition": -1, + "dataset": "correctness", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 24, + "repetition": -1, + "dataset": "large", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 25, + "repetition": -1, + "dataset": "high-arity-100", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 26, + "repetition": -1, + "dataset": "large", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 27, + "repetition": -1, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 28, + "repetition": -1, + "dataset": "correctness", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 29, + "repetition": -1, + "dataset": "scientific", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 30, + "repetition": -1, + "dataset": "high-arity-100", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 31, + "repetition": -1, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 32, + "repetition": -1, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 33, + "repetition": 0, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 34, + "repetition": 0, + "dataset": "correctness", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 35, + "repetition": 0, + "dataset": "high-arity-1000", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 36, + "repetition": 0, + "dataset": "scientific", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 37, + "repetition": 0, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 38, + "repetition": 0, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 39, + "repetition": 0, + "dataset": "moderate", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 40, + "repetition": 0, + "dataset": "high-arity-1000", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 41, + "repetition": 0, + "dataset": "mathlib", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 42, + "repetition": 0, + "dataset": "moderate", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 43, + "repetition": 0, + "dataset": "scientific", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 44, + "repetition": 0, + "dataset": "high-arity-100", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 45, + "repetition": 0, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 46, + "repetition": 0, + "dataset": "scientific", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 47, + "repetition": 0, + "dataset": "high-arity-10", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 48, + "repetition": 0, + "dataset": "large", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 49, + "repetition": 0, + "dataset": "scientific", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 50, + "repetition": 0, + "dataset": "high-arity-10", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 51, + "repetition": 0, + "dataset": "large", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 52, + "repetition": 0, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 53, + "repetition": 0, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 54, + "repetition": 0, + "dataset": "high-arity-100", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 55, + "repetition": 0, + "dataset": "correctness", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 56, + "repetition": 0, + "dataset": "mathlib", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 57, + "repetition": 0, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 58, + "repetition": 0, + "dataset": "high-arity-100", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 59, + "repetition": 0, + "dataset": "correctness", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 60, + "repetition": 0, + "dataset": "mathlib", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 61, + "repetition": 0, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 62, + "repetition": 0, + "dataset": "high-arity-10", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 63, + "repetition": 0, + "dataset": "large", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 64, + "repetition": 0, + "dataset": "correctness", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 65, + "repetition": 0, + "dataset": "high-arity-1000", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 66, + "repetition": 1, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 67, + "repetition": 1, + "dataset": "scientific", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 68, + "repetition": 1, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 69, + "repetition": 1, + "dataset": "large", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 70, + "repetition": 1, + "dataset": "high-arity-10", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 71, + "repetition": 1, + "dataset": "high-arity-100", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 72, + "repetition": 1, + "dataset": "large", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 73, + "repetition": 1, + "dataset": "moderate", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 74, + "repetition": 1, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 75, + "repetition": 1, + "dataset": "high-arity-100", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 76, + "repetition": 1, + "dataset": "scientific", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 77, + "repetition": 1, + "dataset": "high-arity-10", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 78, + "repetition": 1, + "dataset": "mathlib", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 79, + "repetition": 1, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 80, + "repetition": 1, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 81, + "repetition": 1, + "dataset": "mathlib", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 82, + "repetition": 1, + "dataset": "correctness", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 83, + "repetition": 1, + "dataset": "large", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 84, + "repetition": 1, + "dataset": "high-arity-1000", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 85, + "repetition": 1, + "dataset": "high-arity-10", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 86, + "repetition": 1, + "dataset": "high-arity-100", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 87, + "repetition": 1, + "dataset": "high-arity-1000", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 88, + "repetition": 1, + "dataset": "correctness", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 89, + "repetition": 1, + "dataset": "correctness", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 90, + "repetition": 1, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 91, + "repetition": 1, + "dataset": "moderate", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 92, + "repetition": 1, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 93, + "repetition": 1, + "dataset": "scientific", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 94, + "repetition": 1, + "dataset": "correctness", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 95, + "repetition": 1, + "dataset": "mathlib", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 96, + "repetition": 1, + "dataset": "scientific", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 97, + "repetition": 1, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 98, + "repetition": 1, + "dataset": "high-arity-1000", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 99, + "repetition": 2, + "dataset": "high-arity-100", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 100, + "repetition": 2, + "dataset": "moderate", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 101, + "repetition": 2, + "dataset": "scientific", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 102, + "repetition": 2, + "dataset": "high-arity-10", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 103, + "repetition": 2, + "dataset": "mathlib", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 104, + "repetition": 2, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 105, + "repetition": 2, + "dataset": "high-arity-100", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 106, + "repetition": 2, + "dataset": "correctness", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 107, + "repetition": 2, + "dataset": "scientific", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 108, + "repetition": 2, + "dataset": "mathlib", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 109, + "repetition": 2, + "dataset": "high-arity-1000", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 110, + "repetition": 2, + "dataset": "scientific", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 111, + "repetition": 2, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 112, + "repetition": 2, + "dataset": "high-arity-1000", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 113, + "repetition": 2, + "dataset": "high-arity-10", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 114, + "repetition": 2, + "dataset": "high-arity-10", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 115, + "repetition": 2, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 116, + "repetition": 2, + "dataset": "large", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 117, + "repetition": 2, + "dataset": "correctness", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 118, + "repetition": 2, + "dataset": "correctness", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 119, + "repetition": 2, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 120, + "repetition": 2, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 121, + "repetition": 2, + "dataset": "high-arity-1000", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 122, + "repetition": 2, + "dataset": "scientific", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 123, + "repetition": 2, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 124, + "repetition": 2, + "dataset": "correctness", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 125, + "repetition": 2, + "dataset": "high-arity-100", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 126, + "repetition": 2, + "dataset": "moderate", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 127, + "repetition": 2, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 128, + "repetition": 2, + "dataset": "large", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 129, + "repetition": 2, + "dataset": "mathlib", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 130, + "repetition": 2, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 131, + "repetition": 2, + "dataset": "large", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 132, + "repetition": 3, + "dataset": "high-arity-1000", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 133, + "repetition": 3, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 134, + "repetition": 3, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 135, + "repetition": 3, + "dataset": "high-arity-100", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 136, + "repetition": 3, + "dataset": "correctness", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 137, + "repetition": 3, + "dataset": "high-arity-1000", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 138, + "repetition": 3, + "dataset": "scientific", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 139, + "repetition": 3, + "dataset": "scientific", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 140, + "repetition": 3, + "dataset": "moderate", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 141, + "repetition": 3, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 142, + "repetition": 3, + "dataset": "high-arity-10", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 143, + "repetition": 3, + "dataset": "correctness", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 144, + "repetition": 3, + "dataset": "scientific", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 145, + "repetition": 3, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 146, + "repetition": 3, + "dataset": "high-arity-10", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 147, + "repetition": 3, + "dataset": "large", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 148, + "repetition": 3, + "dataset": "correctness", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 149, + "repetition": 3, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 150, + "repetition": 3, + "dataset": "high-arity-100", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 151, + "repetition": 3, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 152, + "repetition": 3, + "dataset": "high-arity-100", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 153, + "repetition": 3, + "dataset": "large", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 154, + "repetition": 3, + "dataset": "scientific", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 155, + "repetition": 3, + "dataset": "correctness", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 156, + "repetition": 3, + "dataset": "large", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 157, + "repetition": 3, + "dataset": "mathlib", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 158, + "repetition": 3, + "dataset": "mathlib", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 159, + "repetition": 3, + "dataset": "high-arity-1000", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 160, + "repetition": 3, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 161, + "repetition": 3, + "dataset": "mathlib", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 162, + "repetition": 3, + "dataset": "moderate", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 163, + "repetition": 3, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 164, + "repetition": 3, + "dataset": "high-arity-10", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 165, + "repetition": 4, + "dataset": "mathlib", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 166, + "repetition": 4, + "dataset": "high-arity-100", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 167, + "repetition": 4, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 168, + "repetition": 4, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 169, + "repetition": 4, + "dataset": "moderate", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 170, + "repetition": 4, + "dataset": "high-arity-10", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 171, + "repetition": 4, + "dataset": "scientific", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 172, + "repetition": 4, + "dataset": "high-arity-10", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 173, + "repetition": 4, + "dataset": "correctness", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 174, + "repetition": 4, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "plot" + }, + { + "order": 175, + "repetition": 4, + "dataset": "scientific", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 176, + "repetition": 4, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 177, + "repetition": 4, + "dataset": "large", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 178, + "repetition": 4, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 179, + "repetition": 4, + "dataset": "high-arity-1000", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 180, + "repetition": 4, + "dataset": "correctness", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 181, + "repetition": 4, + "dataset": "high-arity-1000", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 182, + "repetition": 4, + "dataset": "scientific", + "tool": "hypergraphx", + "mode": "plot" + }, + { + "order": 183, + "repetition": 4, + "dataset": "moderate", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 184, + "repetition": 4, + "dataset": "high-arity-100", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 185, + "repetition": 4, + "dataset": "high-arity-100", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 186, + "repetition": 4, + "dataset": "high-arity-1000", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 187, + "repetition": 4, + "dataset": "correctness", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 188, + "repetition": 4, + "dataset": "correctness", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 189, + "repetition": 4, + "dataset": "correctness", + "tool": "xgi", + "mode": "plot" + }, + { + "order": 190, + "repetition": 4, + "dataset": "scientific", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 191, + "repetition": 4, + "dataset": "mathlib", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 192, + "repetition": 4, + "dataset": "mathlib", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 193, + "repetition": 4, + "dataset": "large", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 194, + "repetition": 4, + "dataset": "large", + "tool": "hypernetx", + "mode": "construct" + }, + { + "order": 195, + "repetition": 4, + "dataset": "scientific", + "tool": "xgi", + "mode": "construct" + }, + { + "order": 196, + "repetition": 4, + "dataset": "high-arity-10", + "tool": "hypergraphx", + "mode": "construct" + }, + { + "order": 197, + "repetition": 4, + "dataset": "moderate", + "tool": "hypernetx", + "mode": "construct" + } + ], + "settings": { + "resolution": [ + 1920, + 1080 + ], + "dpi": 100, + "backend": "Agg", + "labels": false, + "layout": "none; canonical XYZ projected to XY", + "thread_limit": 1, + "hyperedge_alpha": 0.15, + "hypergraphx_smoothing_refinements": 12, + "hypergraphx_polygon_expansion_triangle": 2.5, + "hypergraphx_polygon_expansion_other": 1.8 + }, + "limitations": [ + "Different documented polygon/hull artists; no identical raster workload claim", + "No static figure vs interactive FPS ranking", + "Peak RSS includes imports", + "power/thermal/background conditions recorded by coordinator", + "HGX canonical edge IDs stored as metadata; native IDs differ" + ] +} \ No newline at end of file diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-raw.json b/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-raw.json new file mode 100644 index 0000000..c0daa54 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-raw.json @@ -0,0 +1,12740 @@ +[ + { + "tool": "hypergraphx", + "dataset": "high-arity-1000", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 9.511458, + "file_read": 0.443458, + "json_parse": 4.676959, + "canonical_validate": 4.605375, + "adapter_construct": 15.931083 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 1, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 94027776, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 0, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.920875, + "file_read": 0.333041, + "json_parse": 1.07825, + "canonical_validate": 0.8745, + "adapter_construct": 6.60475 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 75677696, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 1, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "hypergraphx", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.361208, + "file_read": 2.205625, + "json_parse": 0.084834, + "canonical_validate": 0.011333, + "adapter_construct": 0.057208, + "artist_setup": 1170.772291, + "agg_raster_draw": 29.656916, + "pixel_buffer_copy": 2.133875, + "png_export": 20.469917 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/correctness-hypergraphx-plot--1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 130547712, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 2, + "repetition": -1, + "mode": "plot", + "warmup": true + }, + { + "tool": "hypernetx", + "dataset": "high-arity-10", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 1058.28475, + "file_read": 0.266292, + "json_parse": 0.656292, + "canonical_validate": 0.143083, + "adapter_construct": 35.817208 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n103", + "n108", + "n117", + "n124", + "n133", + "n153", + "n157", + "n165", + "n180", + "n186", + "n189", + "n193", + "n196", + "n197", + "n203", + "n221", + "n222", + "n259", + "n263", + "n270", + "n280", + "n287", + "n302", + "n304", + "n307", + "n314", + "n33", + "n335", + "n359", + "n363", + "n364", + "n369", + "n380", + "n381", + "n394", + "n398", + "n404", + "n407", + "n41", + "n412", + "n413", + "n419", + "n432", + "n435", + "n436", + "n438", + "n448", + "n449", + "n452", + "n461", + "n463", + "n465", + "n472", + "n473", + "n475", + "n48", + "n482", + "n484", + "n489", + "n495", + "n508", + "n527", + "n536", + "n542", + "n546", + "n551", + "n564", + "n57", + "n571", + "n575", + "n576", + "n578", + "n60", + "n614", + "n619", + "n622", + "n623", + "n630", + "n632", + "n636", + "n639", + "n659", + "n662", + "n667", + "n674", + "n675", + "n70", + "n700", + "n714", + "n72", + "n723", + "n728", + "n733", + "n737", + "n744", + "n748", + "n758", + "n763", + "n772", + "n787", + "n789", + "n790", + "n804", + "n805", + "n81", + "n819", + "n828", + "n831", + "n84", + "n840", + "n86", + "n869", + "n871", + "n88", + "n892", + "n899", + "n904", + "n905", + "n92", + "n920", + "n922", + "n938", + "n953", + "n96", + "n967", + "n99" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 212189184, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 3, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "xgi", + "dataset": "high-arity-1000", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 279.320708, + "file_read": 0.17275, + "json_parse": 4.703625, + "canonical_validate": 4.735, + "adapter_construct": 18.02725 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 149618688, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 4, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "xgi", + "dataset": "high-arity-10", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 262.459375, + "file_read": 0.050041, + "json_parse": 0.682834, + "canonical_validate": 0.143708, + "adapter_construct": 0.740416 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 111099904, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 5, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "xgi", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 268.237, + "file_read": 0.255084, + "json_parse": 1.302042, + "canonical_validate": 0.695167, + "adapter_construct": 4.209125 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 115294208, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 7, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "hypergraphx", + "dataset": "large", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.055292, + "file_read": 2.589333, + "json_parse": 10.449209, + "canonical_validate": 3.948667, + "adapter_construct": 34.313833 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 109330432, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 8, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-10", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.124083, + "file_read": 0.050875, + "json_parse": 0.708791, + "canonical_validate": 0.147417, + "adapter_construct": 1.048125 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 72777728, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 9, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "xgi", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 265.003416, + "file_read": 0.031458, + "json_parse": 0.033125, + "canonical_validate": 0.010333, + "adapter_construct": 0.057333 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 109281280, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 11, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "xgi", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 264.023958, + "file_read": 0.036917, + "json_parse": 0.49625, + "canonical_validate": 0.154625, + "adapter_construct": 1.274875, + "artist_setup": 24.055958, + "agg_raster_draw": 26.766, + "pixel_buffer_copy": 1.627584, + "png_export": 30.618375 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/scientific-xgi-plot--1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 138493952, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 12, + "repetition": -1, + "mode": "plot", + "warmup": true + }, + { + "tool": "hypernetx", + "dataset": "mathlib", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 791.931167, + "file_read": 4.174125, + "json_parse": 64.171291, + "canonical_validate": 12.366417, + "adapter_construct": 1749.276542 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "MathlibTest.Tactic.Grind.AC", + "MathlibTest.Tactic.Grind.CC", + "MathlibTest.nomatch" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 682590208, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 13, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "hypergraphx", + "dataset": "mathlib", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 5.727417, + "file_read": 1.8425, + "json_parse": 56.025583, + "canonical_validate": 12.3175, + "adapter_construct": 105.8205 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 249004032, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 14, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "hypergraphx", + "dataset": "scientific", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 6.8385, + "file_read": 0.046333, + "json_parse": 0.522042, + "canonical_validate": 0.158083, + "adapter_construct": 1.092125 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 481, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 72564736, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 17, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "hypergraphx", + "dataset": "scientific", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 6.885333, + "file_read": 0.050166, + "json_parse": 0.510042, + "canonical_validate": 0.156542, + "adapter_construct": 1.081584, + "artist_setup": 13298.598125, + "agg_raster_draw": 376.812959, + "pixel_buffer_copy": 1.613625, + "png_export": 38.258167 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 481, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/scientific-hypergraphx-plot--1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 190693376, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 18, + "repetition": -1, + "mode": "plot", + "warmup": true + }, + { + "tool": "hypergraphx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 5.665125, + "file_read": 0.059834, + "json_parse": 1.354, + "canonical_validate": 0.709042, + "adapter_construct": 5.492916 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 76283904, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 19, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "xgi", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 277.540417, + "file_read": 0.048667, + "json_parse": 1.28275, + "canonical_validate": 0.691167, + "adapter_construct": 4.165875, + "artist_setup": 207.714709, + "agg_raster_draw": 455.250209, + "pixel_buffer_copy": 1.456125, + "png_export": 35.656458 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/moderate-xgi-plot--1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 150913024, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 20, + "repetition": -1, + "mode": "plot", + "warmup": true + }, + { + "tool": "hypernetx", + "dataset": "high-arity-1000", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 802.325042, + "file_read": 0.543042, + "json_parse": 4.58, + "canonical_validate": 5.015375, + "adapter_construct": 1836.6335 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 361201664, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 21, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "xgi", + "dataset": "mathlib", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 264.348, + "file_read": 2.754291, + "json_parse": 54.273041, + "canonical_validate": 13.049583, + "adapter_construct": 156.386041 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 293879808, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 22, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "xgi", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 263.968875, + "file_read": 0.215958, + "json_parse": 0.034625, + "canonical_validate": 0.009375, + "adapter_construct": 0.062791, + "artist_setup": 6.777084, + "agg_raster_draw": 4.100291, + "pixel_buffer_copy": 1.490125, + "png_export": 18.348041 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/correctness-xgi-plot--1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 135479296, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 23, + "repetition": -1, + "mode": "plot", + "warmup": true + }, + { + "tool": "xgi", + "dataset": "large", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 268.692875, + "file_read": 2.735458, + "json_parse": 10.469417, + "canonical_validate": 3.867125, + "adapter_construct": 51.690667 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 155648000, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 24, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "hypernetx", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 817.541125, + "file_read": 2.239417, + "json_parse": 1.092042, + "canonical_validate": 0.905375, + "adapter_construct": 194.754625 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 222937088, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 25, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "hypernetx", + "dataset": "large", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 803.890041, + "file_read": 0.251125, + "json_parse": 10.645708, + "canonical_validate": 3.839917, + "adapter_construct": 817.1175 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n1320", + "n1441", + "n4022", + "n4917", + "n5125", + "n5131", + "n5826", + "n6824", + "n7144", + "n7458", + "n7825" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 309149696, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 26, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "status": "watchdog_timeout", + "timeout_seconds": 120, + "order": 27, + "repetition": -1, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "plot", + "warmup": true + }, + { + "tool": "hypergraphx", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 11.070833, + "file_read": 0.249667, + "json_parse": 0.069666, + "canonical_validate": 0.011333, + "adapter_construct": 0.0575 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 70926336, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 28, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "xgi", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 407.313208, + "file_read": 0.253833, + "json_parse": 0.604334, + "canonical_validate": 0.170959, + "adapter_construct": 1.864792 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 111132672, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 29, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "xgi", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 296.105375, + "file_read": 2.306708, + "json_parse": 1.107541, + "canonical_validate": 0.881125, + "adapter_construct": 3.028167 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 116375552, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 30, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "hypergraphx", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 7.113292, + "file_read": 0.081167, + "json_parse": 0.071667, + "canonical_validate": 0.012417, + "adapter_construct": 0.060625 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 71204864, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 34, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-1000", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 7.222958, + "file_read": 0.62225, + "json_parse": 5.402333, + "canonical_validate": 5.064209, + "adapter_construct": 15.910833 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 1, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 94306304, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 35, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "scientific", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 7.054458, + "file_read": 0.113, + "json_parse": 0.562542, + "canonical_validate": 0.23425, + "adapter_construct": 1.306583, + "artist_setup": 16375.33825, + "agg_raster_draw": 435.581709, + "pixel_buffer_copy": 1.467167, + "png_export": 43.370084 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 481, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/scientific-hypergraphx-plot-0.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 185073664, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 36, + "repetition": 0, + "mode": "plot", + "warmup": false + }, + { + "status": "watchdog_timeout", + "timeout_seconds": 120, + "order": 37, + "repetition": 0, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "plot", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 279.993917, + "file_read": 0.269875, + "json_parse": 1.360709, + "canonical_validate": 0.705375, + "adapter_construct": 4.386834 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 115228672, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 39, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-1000", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 271.909334, + "file_read": 4.177583, + "json_parse": 4.669666, + "canonical_validate": 4.549959, + "adapter_construct": 18.057167 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 149585920, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 40, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "mathlib", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 811.367792, + "file_read": 5.996084, + "json_parse": 56.807541, + "canonical_validate": 12.13775, + "adapter_construct": 1780.965708 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "MathlibTest.Tactic.Grind.AC", + "MathlibTest.Tactic.Grind.CC", + "MathlibTest.nomatch" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 682524672, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 41, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 275.288375, + "file_read": 0.066292, + "json_parse": 1.361625, + "canonical_validate": 0.744083, + "adapter_construct": 4.606375, + "artist_setup": 214.565458, + "agg_raster_draw": 452.400541, + "pixel_buffer_copy": 1.417958, + "png_export": 34.294458 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/moderate-xgi-plot-0.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 150863872, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 42, + "repetition": 0, + "mode": "plot", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 274.38325, + "file_read": 2.255916, + "json_parse": 0.511625, + "canonical_validate": 0.154791, + "adapter_construct": 1.280875 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 111476736, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 43, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 274.472125, + "file_read": 4.0545, + "json_parse": 1.148833, + "canonical_validate": 0.870625, + "adapter_construct": 2.429875 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 116195328, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 44, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 9.48975, + "file_read": 0.10275, + "json_parse": 1.3735, + "canonical_validate": 0.705083, + "adapter_construct": 5.99925 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 76185600, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 45, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 270.613333, + "file_read": 0.061583, + "json_parse": 0.518833, + "canonical_validate": 0.159833, + "adapter_construct": 1.669583, + "artist_setup": 25.148208, + "agg_raster_draw": 27.586875, + "pixel_buffer_copy": 1.540417, + "png_export": 31.17975 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/scientific-xgi-plot-0.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 138805248, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 46, + "repetition": 0, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-10", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 827.251708, + "file_read": 0.380833, + "json_parse": 0.658125, + "canonical_validate": 0.145708, + "adapter_construct": 32.406458 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n103", + "n108", + "n117", + "n124", + "n133", + "n153", + "n157", + "n165", + "n180", + "n186", + "n189", + "n193", + "n196", + "n197", + "n203", + "n221", + "n222", + "n259", + "n263", + "n270", + "n280", + "n287", + "n302", + "n304", + "n307", + "n314", + "n33", + "n335", + "n359", + "n363", + "n364", + "n369", + "n380", + "n381", + "n394", + "n398", + "n404", + "n407", + "n41", + "n412", + "n413", + "n419", + "n432", + "n435", + "n436", + "n438", + "n448", + "n449", + "n452", + "n461", + "n463", + "n465", + "n472", + "n473", + "n475", + "n48", + "n482", + "n484", + "n489", + "n495", + "n508", + "n527", + "n536", + "n542", + "n546", + "n551", + "n564", + "n57", + "n571", + "n575", + "n576", + "n578", + "n60", + "n614", + "n619", + "n622", + "n623", + "n630", + "n632", + "n636", + "n639", + "n659", + "n662", + "n667", + "n674", + "n675", + "n70", + "n700", + "n714", + "n72", + "n723", + "n728", + "n733", + "n737", + "n744", + "n748", + "n758", + "n763", + "n772", + "n787", + "n789", + "n790", + "n804", + "n805", + "n81", + "n819", + "n828", + "n831", + "n84", + "n840", + "n86", + "n869", + "n871", + "n88", + "n892", + "n899", + "n904", + "n905", + "n92", + "n920", + "n922", + "n938", + "n953", + "n96", + "n967", + "n99" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 212336640, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 47, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "large", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 821.981375, + "file_read": 0.629625, + "json_parse": 11.179084, + "canonical_validate": 4.19175, + "adapter_construct": 824.811 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n1320", + "n1441", + "n4022", + "n4917", + "n5125", + "n5131", + "n5826", + "n6824", + "n7144", + "n7458", + "n7825" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 309837824, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 48, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "scientific", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 6.417375, + "file_read": 0.05725, + "json_parse": 0.557875, + "canonical_validate": 0.161542, + "adapter_construct": 1.108792 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 481, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 72843264, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 49, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-10", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.256209, + "file_read": 0.089542, + "json_parse": 0.671208, + "canonical_validate": 0.153375, + "adapter_construct": 1.01675 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 72712192, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 50, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "large", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 275.338291, + "file_read": 0.27625, + "json_parse": 11.280917, + "canonical_validate": 4.149459, + "adapter_construct": 53.333666 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 155762688, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 51, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 840.271958, + "file_read": 0.090708, + "json_parse": 1.012458, + "canonical_validate": 0.885375, + "adapter_construct": 193.156208 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 225361920, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 54, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 313.629667, + "file_read": 0.048708, + "json_parse": 0.033833, + "canonical_validate": 0.009541, + "adapter_construct": 0.067459 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 110280704, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 55, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "mathlib", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.886417, + "file_read": 1.93625, + "json_parse": 58.050584, + "canonical_validate": 12.564, + "adapter_construct": 107.802792 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 247562240, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 56, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.111625, + "file_read": 0.093875, + "json_parse": 1.089209, + "canonical_validate": 0.874834, + "adapter_construct": 6.682958 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 75776000, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 58, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 278.903625, + "file_read": 0.035917, + "json_parse": 0.033084, + "canonical_validate": 0.008958, + "adapter_construct": 0.058125, + "artist_setup": 6.813042, + "agg_raster_draw": 3.862541, + "pixel_buffer_copy": 1.674666, + "png_export": 18.194583 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/correctness-xgi-plot-0.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 135626752, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 59, + "repetition": 0, + "mode": "plot", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "mathlib", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 274.656875, + "file_read": 1.998375, + "json_parse": 56.027625, + "canonical_validate": 12.875291, + "adapter_construct": 164.849541 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 295157760, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 60, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-10", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 272.490417, + "file_read": 2.252375, + "json_parse": 0.675667, + "canonical_validate": 0.144458, + "adapter_construct": 0.727709 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 111378432, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 62, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "large", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 7.07125, + "file_read": 2.714792, + "json_parse": 10.844625, + "canonical_validate": 3.94675, + "adapter_construct": 32.690333 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 109477888, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 63, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.301333, + "file_read": 0.07575, + "json_parse": 0.063125, + "canonical_validate": 0.011333, + "adapter_construct": 0.05575, + "artist_setup": 1138.57875, + "agg_raster_draw": 30.414417, + "pixel_buffer_copy": 1.5165, + "png_export": 20.759375 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/correctness-hypergraphx-plot-0.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 131497984, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 64, + "repetition": 0, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-1000", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 851.445833, + "file_read": 2.413042, + "json_parse": 6.3545, + "canonical_validate": 4.910292, + "adapter_construct": 1804.511417 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 358891520, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 65, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 272.373042, + "file_read": 0.042875, + "json_parse": 0.492708, + "canonical_validate": 0.156, + "adapter_construct": 1.688709 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 111230976, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 67, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "status": "watchdog_timeout", + "timeout_seconds": 120, + "order": 68, + "repetition": 1, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "plot", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "large", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 14.663333, + "file_read": 0.8645, + "json_parse": 11.655541, + "canonical_validate": 4.421542, + "adapter_construct": 37.283083 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 109576192, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 69, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-10", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 415.83225, + "file_read": 0.265875, + "json_parse": 0.683167, + "canonical_validate": 0.1435, + "adapter_construct": 0.738834 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 111099904, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 70, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.501292, + "file_read": 4.068292, + "json_parse": 1.144125, + "canonical_validate": 0.890125, + "adapter_construct": 6.578459 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 75808768, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 71, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "large", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 284.234791, + "file_read": 0.261417, + "json_parse": 11.685292, + "canonical_validate": 4.224125, + "adapter_construct": 55.028042 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 155729920, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 72, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 282.17175, + "file_read": 0.475208, + "json_parse": 1.3685, + "canonical_validate": 0.703334, + "adapter_construct": 4.7165 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 115343360, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 73, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 286.005833, + "file_read": 0.069, + "json_parse": 1.044333, + "canonical_validate": 0.874833, + "adapter_construct": 2.603958 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 116211712, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 75, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "scientific", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 6.540083, + "file_read": 0.085834, + "json_parse": 0.536083, + "canonical_validate": 0.169959, + "adapter_construct": 1.127667, + "artist_setup": 13858.237916, + "agg_raster_draw": 407.662, + "pixel_buffer_copy": 2.215375, + "png_export": 40.778666 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 481, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/scientific-hypergraphx-plot-1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 190382080, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 76, + "repetition": 1, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-10", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 963.2795, + "file_read": 0.27925, + "json_parse": 0.651375, + "canonical_validate": 0.148042, + "adapter_construct": 35.747125 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n103", + "n108", + "n117", + "n124", + "n133", + "n153", + "n157", + "n165", + "n180", + "n186", + "n189", + "n193", + "n196", + "n197", + "n203", + "n221", + "n222", + "n259", + "n263", + "n270", + "n280", + "n287", + "n302", + "n304", + "n307", + "n314", + "n33", + "n335", + "n359", + "n363", + "n364", + "n369", + "n380", + "n381", + "n394", + "n398", + "n404", + "n407", + "n41", + "n412", + "n413", + "n419", + "n432", + "n435", + "n436", + "n438", + "n448", + "n449", + "n452", + "n461", + "n463", + "n465", + "n472", + "n473", + "n475", + "n48", + "n482", + "n484", + "n489", + "n495", + "n508", + "n527", + "n536", + "n542", + "n546", + "n551", + "n564", + "n57", + "n571", + "n575", + "n576", + "n578", + "n60", + "n614", + "n619", + "n622", + "n623", + "n630", + "n632", + "n636", + "n639", + "n659", + "n662", + "n667", + "n674", + "n675", + "n70", + "n700", + "n714", + "n72", + "n723", + "n728", + "n733", + "n737", + "n744", + "n748", + "n758", + "n763", + "n772", + "n787", + "n789", + "n790", + "n804", + "n805", + "n81", + "n819", + "n828", + "n831", + "n84", + "n840", + "n86", + "n869", + "n871", + "n88", + "n892", + "n899", + "n904", + "n905", + "n92", + "n920", + "n922", + "n938", + "n953", + "n96", + "n967", + "n99" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 212287488, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 77, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "mathlib", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 885.215458, + "file_read": 10.072833, + "json_parse": 66.901541, + "canonical_validate": 13.445542, + "adapter_construct": 1946.621333 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "MathlibTest.Tactic.Grind.AC", + "MathlibTest.Tactic.Grind.CC", + "MathlibTest.nomatch" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 682131456, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 78, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 9.13125, + "file_read": 2.269667, + "json_parse": 1.623083, + "canonical_validate": 0.720666, + "adapter_construct": 6.181709 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 76087296, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 80, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "mathlib", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 305.62225, + "file_read": 2.040375, + "json_parse": 60.567708, + "canonical_validate": 12.9715, + "adapter_construct": 203.470125 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 293470208, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 81, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.467875, + "file_read": 2.195167, + "json_parse": 0.07625, + "canonical_validate": 0.012375, + "adapter_construct": 0.057834 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 70811648, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 82, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "large", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 872.4695, + "file_read": 2.581209, + "json_parse": 11.657959, + "canonical_validate": 4.130917, + "adapter_construct": 876.699333 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n1320", + "n1441", + "n4022", + "n4917", + "n5125", + "n5131", + "n5826", + "n6824", + "n7144", + "n7458", + "n7825" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 309952512, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 83, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-1000", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 867.030792, + "file_read": 4.1695, + "json_parse": 4.8025, + "canonical_validate": 4.942208, + "adapter_construct": 1925.910833 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 359120896, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 84, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-10", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 7.011042, + "file_read": 2.280375, + "json_parse": 0.729583, + "canonical_validate": 0.152541, + "adapter_construct": 1.030958 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 72892416, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 85, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 890.412, + "file_read": 0.393417, + "json_parse": 1.07475, + "canonical_validate": 0.893583, + "adapter_construct": 203.545875 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 223068160, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 86, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-1000", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 289.595375, + "file_read": 0.19825, + "json_parse": 5.053333, + "canonical_validate": 4.874209, + "adapter_construct": 25.7835 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 149635072, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 87, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 283.202042, + "file_read": 0.047459, + "json_parse": 0.034833, + "canonical_validate": 0.0095, + "adapter_construct": 0.057583 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 109428736, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 88, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 284.31775, + "file_read": 0.051208, + "json_parse": 0.0355, + "canonical_validate": 0.00975, + "adapter_construct": 0.061125, + "artist_setup": 7.118625, + "agg_raster_draw": 4.125834, + "pixel_buffer_copy": 1.542291, + "png_export": 19.73225 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/correctness-xgi-plot-1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 135495680, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 89, + "repetition": 1, + "mode": "plot", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 298.746083, + "file_read": 0.06325, + "json_parse": 1.369208, + "canonical_validate": 0.799083, + "adapter_construct": 4.753333, + "artist_setup": 220.023667, + "agg_raster_draw": 468.276916, + "pixel_buffer_copy": 1.617333, + "png_export": 34.222667 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/moderate-xgi-plot-1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 151126016, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 91, + "repetition": 1, + "mode": "plot", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 297.467917, + "file_read": 2.312458, + "json_parse": 0.5585, + "canonical_validate": 0.166334, + "adapter_construct": 1.372583, + "artist_setup": 23.412958, + "agg_raster_draw": 29.148333, + "pixel_buffer_copy": 1.591291, + "png_export": 33.691 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/scientific-xgi-plot-1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 139509760, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 93, + "repetition": 1, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 9.209125, + "file_read": 0.091291, + "json_parse": 0.110583, + "canonical_validate": 0.0115, + "adapter_construct": 0.0565, + "artist_setup": 1196.066375, + "agg_raster_draw": 32.85975, + "pixel_buffer_copy": 1.558458, + "png_export": 22.880875 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/correctness-hypergraphx-plot-1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 132907008, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 94, + "repetition": 1, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "mathlib", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 7.487916, + "file_read": 4.367958, + "json_parse": 63.760042, + "canonical_validate": 14.445959, + "adapter_construct": 128.936666 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 249790464, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 95, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "scientific", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 6.883916, + "file_read": 0.085417, + "json_parse": 0.5595, + "canonical_validate": 0.162917, + "adapter_construct": 1.0975 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 481, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 72548352, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 96, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-1000", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 6.7415, + "file_read": 2.6015, + "json_parse": 5.15575, + "canonical_validate": 4.725667, + "adapter_construct": 16.075166 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 1, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 94224384, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 98, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 287.071208, + "file_read": 2.267417, + "json_parse": 1.118125, + "canonical_validate": 0.876333, + "adapter_construct": 3.383792 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 116064256, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 99, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 285.89325, + "file_read": 0.098625, + "json_parse": 1.598334, + "canonical_validate": 0.723208, + "adapter_construct": 4.732334, + "artist_setup": 220.773125, + "agg_raster_draw": 471.990083, + "pixel_buffer_copy": 2.253291, + "png_export": 39.779959 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/moderate-xgi-plot-2.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 150994944, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 100, + "repetition": 2, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "scientific", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 6.587167, + "file_read": 0.07175, + "json_parse": 0.518292, + "canonical_validate": 0.1615, + "adapter_construct": 1.090792, + "artist_setup": 13764.899333, + "agg_raster_draw": 396.785417, + "pixel_buffer_copy": 1.346375, + "png_export": 38.761041 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 481, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/scientific-hypergraphx-plot-2.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 191823872, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 101, + "repetition": 2, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-10", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.339625, + "file_read": 0.376084, + "json_parse": 0.729167, + "canonical_validate": 0.149667, + "adapter_construct": 1.0235 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 73089024, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 102, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "mathlib", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 1140.046417, + "file_read": 8.097792, + "json_parse": 65.664083, + "canonical_validate": 12.818041, + "adapter_construct": 1900.112542 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "MathlibTest.Tactic.Grind.AC", + "MathlibTest.Tactic.Grind.CC", + "MathlibTest.nomatch" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 682524672, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 103, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 869.687041, + "file_read": 2.273167, + "json_parse": 1.05225, + "canonical_validate": 0.869167, + "adapter_construct": 199.844 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 223084544, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 105, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 299.051959, + "file_read": 0.25675, + "json_parse": 0.0615, + "canonical_validate": 0.010709, + "adapter_construct": 0.109584 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 109641728, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 106, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 287.690167, + "file_read": 2.242875, + "json_parse": 0.517417, + "canonical_validate": 0.159708, + "adapter_construct": 1.403625, + "artist_setup": 23.393625, + "agg_raster_draw": 29.113958, + "pixel_buffer_copy": 1.599625, + "png_export": 33.236709 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/scientific-xgi-plot-2.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 138592256, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 107, + "repetition": 2, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "mathlib", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.503708, + "file_read": 2.072791, + "json_parse": 60.058542, + "canonical_validate": 13.776959, + "adapter_construct": 124.682667 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 249004032, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 108, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-1000", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 291.23925, + "file_read": 2.555083, + "json_parse": 4.822375, + "canonical_validate": 4.70875, + "adapter_construct": 24.042917 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 149422080, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 109, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 285.495208, + "file_read": 0.105208, + "json_parse": 0.566083, + "canonical_validate": 0.162834, + "adapter_construct": 1.325541 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 111001600, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 110, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-1000", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 900.420291, + "file_read": 0.153791, + "json_parse": 5.027625, + "canonical_validate": 4.824459, + "adapter_construct": 1901.2335 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 358334464, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 112, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-10", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 893.616292, + "file_read": 0.097375, + "json_parse": 0.656375, + "canonical_validate": 0.15525, + "adapter_construct": 32.927667 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n103", + "n108", + "n117", + "n124", + "n133", + "n153", + "n157", + "n165", + "n180", + "n186", + "n189", + "n193", + "n196", + "n197", + "n203", + "n221", + "n222", + "n259", + "n263", + "n270", + "n280", + "n287", + "n302", + "n304", + "n307", + "n314", + "n33", + "n335", + "n359", + "n363", + "n364", + "n369", + "n380", + "n381", + "n394", + "n398", + "n404", + "n407", + "n41", + "n412", + "n413", + "n419", + "n432", + "n435", + "n436", + "n438", + "n448", + "n449", + "n452", + "n461", + "n463", + "n465", + "n472", + "n473", + "n475", + "n48", + "n482", + "n484", + "n489", + "n495", + "n508", + "n527", + "n536", + "n542", + "n546", + "n551", + "n564", + "n57", + "n571", + "n575", + "n576", + "n578", + "n60", + "n614", + "n619", + "n622", + "n623", + "n630", + "n632", + "n636", + "n639", + "n659", + "n662", + "n667", + "n674", + "n675", + "n70", + "n700", + "n714", + "n72", + "n723", + "n728", + "n733", + "n737", + "n744", + "n748", + "n758", + "n763", + "n772", + "n787", + "n789", + "n790", + "n804", + "n805", + "n81", + "n819", + "n828", + "n831", + "n84", + "n840", + "n86", + "n869", + "n871", + "n88", + "n892", + "n899", + "n904", + "n905", + "n92", + "n920", + "n922", + "n938", + "n953", + "n96", + "n967", + "n99" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 212467712, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 113, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-10", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 292.130875, + "file_read": 0.046708, + "json_parse": 0.669875, + "canonical_validate": 0.14475, + "adapter_construct": 0.751541 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 111312896, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 114, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "status": "watchdog_timeout", + "timeout_seconds": 120, + "order": 115, + "repetition": 2, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "large", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 1339.703833, + "file_read": 0.613333, + "json_parse": 13.250916, + "canonical_validate": 4.503083, + "adapter_construct": 935.187459 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n1320", + "n1441", + "n4022", + "n4917", + "n5125", + "n5131", + "n5826", + "n6824", + "n7144", + "n7458", + "n7825" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 311623680, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 116, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 10.251583, + "file_read": 0.212541, + "json_parse": 0.07275, + "canonical_validate": 0.010916, + "adapter_construct": 0.056875 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 70877184, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 117, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 7.202458, + "file_read": 0.078417, + "json_parse": 0.063292, + "canonical_validate": 0.010917, + "adapter_construct": 0.05675, + "artist_setup": 1155.147541, + "agg_raster_draw": 30.17075, + "pixel_buffer_copy": 1.60375, + "png_export": 20.909042 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/correctness-hypergraphx-plot-2.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 131514368, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 118, + "repetition": 2, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-1000", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 6.725125, + "file_read": 2.662333, + "json_parse": 5.032917, + "canonical_validate": 4.687167, + "adapter_construct": 15.904042 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 1, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 93913088, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 121, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "scientific", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 6.597417, + "file_read": 0.084334, + "json_parse": 0.507917, + "canonical_validate": 0.159958, + "adapter_construct": 1.101458 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 481, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 72663040, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 122, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 304.368375, + "file_read": 0.063041, + "json_parse": 0.035333, + "canonical_validate": 0.010833, + "adapter_construct": 0.06325, + "artist_setup": 7.765333, + "agg_raster_draw": 4.298417, + "pixel_buffer_copy": 2.127292, + "png_export": 20.41175 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/correctness-xgi-plot-2.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 135512064, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 124, + "repetition": 2, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.903167, + "file_read": 0.943875, + "json_parse": 1.150667, + "canonical_validate": 0.868958, + "adapter_construct": 6.799833 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 75890688, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 125, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 280.063417, + "file_read": 2.285083, + "json_parse": 1.34325, + "canonical_validate": 0.698667, + "adapter_construct": 4.252541 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 115343360, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 126, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "large", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 280.495541, + "file_read": 2.719709, + "json_parse": 11.393375, + "canonical_validate": 4.334875, + "adapter_construct": 58.649542 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 155926528, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 128, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "mathlib", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 285.901292, + "file_read": 4.514042, + "json_parse": 62.608625, + "canonical_validate": 12.682958, + "adapter_construct": 188.831167 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 294469632, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 129, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 7.039875, + "file_read": 0.094333, + "json_parse": 1.390209, + "canonical_validate": 0.705583, + "adapter_construct": 5.815875 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 76283904, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 130, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "large", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 7.160958, + "file_read": 0.381917, + "json_parse": 12.232583, + "canonical_validate": 4.547125, + "adapter_construct": 36.980375 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 109510656, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 131, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-1000", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 7.142959, + "file_read": 0.34175, + "json_parse": 5.275875, + "canonical_validate": 4.746417, + "adapter_construct": 15.997 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 1, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 94044160, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 132, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "status": "watchdog_timeout", + "timeout_seconds": 120, + "order": 134, + "repetition": 3, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "plot", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 10.816333, + "file_read": 0.536833, + "json_parse": 1.162208, + "canonical_validate": 0.890042, + "adapter_construct": 6.586333 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 75726848, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 135, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 5.764708, + "file_read": 3.942334, + "json_parse": 0.061125, + "canonical_validate": 0.009333, + "adapter_construct": 0.055792 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 70795264, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 136, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-1000", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 1151.830875, + "file_read": 0.498125, + "json_parse": 4.602583, + "canonical_validate": 4.8055, + "adapter_construct": 1793.653417 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 358825984, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 137, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 272.816458, + "file_read": 0.27325, + "json_parse": 0.514583, + "canonical_validate": 0.158125, + "adapter_construct": 1.299, + "artist_setup": 23.956792, + "agg_raster_draw": 26.573625, + "pixel_buffer_copy": 1.609792, + "png_export": 31.118708 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/scientific-xgi-plot-3.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 138559488, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 138, + "repetition": 3, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "scientific", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 6.266875, + "file_read": 0.042291, + "json_parse": 0.527792, + "canonical_validate": 0.156541, + "adapter_construct": 1.07875 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 481, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 72663040, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 139, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 261.819084, + "file_read": 2.249042, + "json_parse": 1.441041, + "canonical_validate": 0.814791, + "adapter_construct": 4.709417, + "artist_setup": 208.436916, + "agg_raster_draw": 443.710875, + "pixel_buffer_copy": 1.568875, + "png_export": 37.701 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/moderate-xgi-plot-3.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 151044096, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 140, + "repetition": 3, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-10", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 841.494375, + "file_read": 2.307416, + "json_parse": 0.723583, + "canonical_validate": 0.158542, + "adapter_construct": 33.11825 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n103", + "n108", + "n117", + "n124", + "n133", + "n153", + "n157", + "n165", + "n180", + "n186", + "n189", + "n193", + "n196", + "n197", + "n203", + "n221", + "n222", + "n259", + "n263", + "n270", + "n280", + "n287", + "n302", + "n304", + "n307", + "n314", + "n33", + "n335", + "n359", + "n363", + "n364", + "n369", + "n380", + "n381", + "n394", + "n398", + "n404", + "n407", + "n41", + "n412", + "n413", + "n419", + "n432", + "n435", + "n436", + "n438", + "n448", + "n449", + "n452", + "n461", + "n463", + "n465", + "n472", + "n473", + "n475", + "n48", + "n482", + "n484", + "n489", + "n495", + "n508", + "n527", + "n536", + "n542", + "n546", + "n551", + "n564", + "n57", + "n571", + "n575", + "n576", + "n578", + "n60", + "n614", + "n619", + "n622", + "n623", + "n630", + "n632", + "n636", + "n639", + "n659", + "n662", + "n667", + "n674", + "n675", + "n70", + "n700", + "n714", + "n72", + "n723", + "n728", + "n733", + "n737", + "n744", + "n748", + "n758", + "n763", + "n772", + "n787", + "n789", + "n790", + "n804", + "n805", + "n81", + "n819", + "n828", + "n831", + "n84", + "n840", + "n86", + "n869", + "n871", + "n88", + "n892", + "n899", + "n904", + "n905", + "n92", + "n920", + "n922", + "n938", + "n953", + "n96", + "n967", + "n99" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 212238336, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 142, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 270.236, + "file_read": 0.030708, + "json_parse": 0.0335, + "canonical_validate": 0.009041, + "adapter_construct": 0.05725 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 109674496, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 143, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 262.516042, + "file_read": 0.037416, + "json_parse": 0.4845, + "canonical_validate": 0.154292, + "adapter_construct": 1.282 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 111050752, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 144, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.431666, + "file_read": 0.091709, + "json_parse": 1.418208, + "canonical_validate": 0.706542, + "adapter_construct": 5.894708 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 76087296, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 145, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-10", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.019792, + "file_read": 0.049334, + "json_parse": 0.717166, + "canonical_validate": 0.145084, + "adapter_construct": 1.005958 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 72695808, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 146, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "large", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 830.288041, + "file_read": 2.566209, + "json_parse": 12.275917, + "canonical_validate": 4.378125, + "adapter_construct": 805.563417 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n1320", + "n1441", + "n4022", + "n4917", + "n5125", + "n5131", + "n5826", + "n6824", + "n7144", + "n7458", + "n7825" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 309723136, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 147, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 5.830917, + "file_read": 0.038708, + "json_parse": 0.05575, + "canonical_validate": 0.009625, + "adapter_construct": 0.054625, + "artist_setup": 1149.301958, + "agg_raster_draw": 29.700917, + "pixel_buffer_copy": 1.655542, + "png_export": 20.512584 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/correctness-hypergraphx-plot-3.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 131465216, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 148, + "repetition": 3, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 796.27925, + "file_read": 0.068083, + "json_parse": 1.0075, + "canonical_validate": 0.862, + "adapter_construct": 189.605209 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 223100928, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 150, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 261.4345, + "file_read": 0.06475, + "json_parse": 1.123834, + "canonical_validate": 0.892792, + "adapter_construct": 2.629583 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 116211712, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 152, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "large", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.017834, + "file_read": 0.29175, + "json_parse": 10.79925, + "canonical_validate": 3.981875, + "adapter_construct": 32.174083 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 109379584, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 153, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "scientific", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 5.815042, + "file_read": 0.04675, + "json_parse": 0.489583, + "canonical_validate": 0.1565, + "adapter_construct": 1.09425, + "artist_setup": 13349.868709, + "agg_raster_draw": 375.25175, + "pixel_buffer_copy": 1.862667, + "png_export": 41.68275 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 481, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/scientific-hypergraphx-plot-3.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 191660032, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 154, + "repetition": 3, + "mode": "plot", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 395.923333, + "file_read": 0.187042, + "json_parse": 0.03875, + "canonical_validate": 0.009667, + "adapter_construct": 0.068542, + "artist_setup": 8.063875, + "agg_raster_draw": 3.87625, + "pixel_buffer_copy": 1.5295, + "png_export": 18.327667 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/correctness-xgi-plot-3.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 135643136, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 155, + "repetition": 3, + "mode": "plot", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "large", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 263.264208, + "file_read": 2.570875, + "json_parse": 12.750084, + "canonical_validate": 4.256, + "adapter_construct": 45.720292 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 155648000, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 156, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "mathlib", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 1051.489667, + "file_read": 8.150458, + "json_parse": 62.972667, + "canonical_validate": 13.488375, + "adapter_construct": 1771.581958 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "MathlibTest.Tactic.Grind.AC", + "MathlibTest.Tactic.Grind.CC", + "MathlibTest.nomatch" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 682524672, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 157, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "mathlib", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 9.033625, + "file_read": 1.972333, + "json_parse": 56.314667, + "canonical_validate": 12.713375, + "adapter_construct": 105.090625 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 248938496, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 158, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-1000", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 265.997125, + "file_read": 4.28625, + "json_parse": 4.650709, + "canonical_validate": 4.677333, + "adapter_construct": 16.894083 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 149520384, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 159, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "mathlib", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 271.628708, + "file_read": 1.877166, + "json_parse": 59.511416, + "canonical_validate": 13.667958, + "adapter_construct": 153.56625 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 294043648, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 161, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 273.893917, + "file_read": 0.057583, + "json_parse": 1.310542, + "canonical_validate": 0.720833, + "adapter_construct": 4.266792 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 115310592, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 162, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-10", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 281.07, + "file_read": 0.32925, + "json_parse": 0.679041, + "canonical_validate": 0.141958, + "adapter_construct": 0.748625 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 111230976, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 164, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "mathlib", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 824.66075, + "file_read": 1.8305, + "json_parse": 60.417416, + "canonical_validate": 12.971375, + "adapter_construct": 1798.545625 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "MathlibTest.Tactic.Grind.AC", + "MathlibTest.Tactic.Grind.CC", + "MathlibTest.nomatch" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 682524672, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 165, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 278.170625, + "file_read": 0.439584, + "json_parse": 1.067041, + "canonical_validate": 0.8785, + "adapter_construct": 2.4685 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 116408320, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 166, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 274.636583, + "file_read": 0.057709, + "json_parse": 1.336917, + "canonical_validate": 0.803875, + "adapter_construct": 4.595334, + "artist_setup": 210.785166, + "agg_raster_draw": 452.824667, + "pixel_buffer_copy": 1.530167, + "png_export": 38.043584 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/moderate-xgi-plot-4.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 150978560, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 169, + "repetition": 4, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-10", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 814.433458, + "file_read": 0.052292, + "json_parse": 0.634625, + "canonical_validate": 0.14775, + "adapter_construct": 30.807167 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n103", + "n108", + "n117", + "n124", + "n133", + "n153", + "n157", + "n165", + "n180", + "n186", + "n189", + "n193", + "n196", + "n197", + "n203", + "n221", + "n222", + "n259", + "n263", + "n270", + "n280", + "n287", + "n302", + "n304", + "n307", + "n314", + "n33", + "n335", + "n359", + "n363", + "n364", + "n369", + "n380", + "n381", + "n394", + "n398", + "n404", + "n407", + "n41", + "n412", + "n413", + "n419", + "n432", + "n435", + "n436", + "n438", + "n448", + "n449", + "n452", + "n461", + "n463", + "n465", + "n472", + "n473", + "n475", + "n48", + "n482", + "n484", + "n489", + "n495", + "n508", + "n527", + "n536", + "n542", + "n546", + "n551", + "n564", + "n57", + "n571", + "n575", + "n576", + "n578", + "n60", + "n614", + "n619", + "n622", + "n623", + "n630", + "n632", + "n636", + "n639", + "n659", + "n662", + "n667", + "n674", + "n675", + "n70", + "n700", + "n714", + "n72", + "n723", + "n728", + "n733", + "n737", + "n744", + "n748", + "n758", + "n763", + "n772", + "n787", + "n789", + "n790", + "n804", + "n805", + "n81", + "n819", + "n828", + "n831", + "n84", + "n840", + "n86", + "n869", + "n871", + "n88", + "n892", + "n899", + "n904", + "n905", + "n92", + "n920", + "n922", + "n938", + "n953", + "n96", + "n967", + "n99" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 212172800, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 170, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-10", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 273.958708, + "file_read": 0.050125, + "json_parse": 0.662, + "canonical_validate": 0.144458, + "adapter_construct": 0.750292 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 111083520, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 172, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 9.6, + "file_read": 0.048791, + "json_parse": 0.061625, + "canonical_validate": 0.009792, + "adapter_construct": 0.05425 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 70828032, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 173, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 274.083, + "file_read": 0.061292, + "json_parse": 0.579375, + "canonical_validate": 0.154709, + "adapter_construct": 1.317291, + "artist_setup": 24.145291, + "agg_raster_draw": 28.11425, + "pixel_buffer_copy": 1.517958, + "png_export": 34.993084 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/scientific-xgi-plot-4.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 138674176, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 175, + "repetition": 4, + "mode": "plot", + "warmup": false + }, + { + "status": "watchdog_timeout", + "timeout_seconds": 120, + "order": 176, + "repetition": 4, + "dataset": "moderate", + "tool": "hypergraphx", + "mode": "plot", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "large", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 453.793875, + "file_read": 0.648041, + "json_parse": 11.503042, + "canonical_validate": 4.089792, + "adapter_construct": 47.447208 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 155746304, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 177, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 11.973541, + "file_read": 4.118208, + "json_parse": 1.421125, + "canonical_validate": 0.706917, + "adapter_construct": 5.681042 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 76070912, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 178, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-1000", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 1093.867708, + "file_read": 0.531291, + "json_parse": 4.772083, + "canonical_validate": 4.877208, + "adapter_construct": 1827.21875 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 358924288, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 179, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 7.001334, + "file_read": 2.176666, + "json_parse": 0.067417, + "canonical_validate": 0.010292, + "adapter_construct": 0.055666, + "artist_setup": 1132.674167, + "agg_raster_draw": 30.262417, + "pixel_buffer_copy": 1.412416, + "png_export": 22.922083 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/correctness-hypergraphx-plot-4.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 131203072, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 180, + "repetition": 4, + "mode": "plot", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "high-arity-1000", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 267.701916, + "file_read": 0.181625, + "json_parse": 4.782375, + "canonical_validate": 4.811959, + "adapter_construct": 21.3205 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 149782528, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 181, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "scientific", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 5.980459, + "file_read": 2.266791, + "json_parse": 0.512125, + "canonical_validate": 0.158917, + "adapter_construct": 1.082125, + "artist_setup": 13374.1345, + "agg_raster_draw": 385.368584, + "pixel_buffer_copy": 1.708541, + "png_export": 40.362 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 481, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/scientific-hypergraphx-plot-4.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 189939712, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 182, + "repetition": 4, + "mode": "plot", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 281.542292, + "file_read": 0.052375, + "json_parse": 1.292375, + "canonical_validate": 0.701541, + "adapter_construct": 4.562375 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 115441664, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 183, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.080791, + "file_read": 0.559625, + "json_parse": 1.098917, + "canonical_validate": 0.882125, + "adapter_construct": 6.789875 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 75726848, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 184, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "high-arity-100", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 840.130333, + "file_read": 0.098791, + "json_parse": 1.072833, + "canonical_validate": 0.875542, + "adapter_construct": 192.494125 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "947331dcf67e3c7362972d7b43eae15d97aafbc64df3fe48ff0c702530787dac", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 20000, + "maximum_arity": 100 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 223215616, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 185, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-1000", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 7.150542, + "file_read": 0.254709, + "json_parse": 4.895916, + "canonical_validate": 4.705666, + "adapter_construct": 16.039709 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "eeb20e8a28bf9bfae2518b371318e5bdf9b793eeac84acc35f5b4089508786f2", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 200000, + "maximum_arity": 1000 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 1, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 94126080, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 186, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 270.3215, + "file_read": 0.043792, + "json_parse": 0.034583, + "canonical_validate": 0.013292, + "adapter_construct": 0.058583 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 109330432, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 187, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "correctness", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 277.840209, + "file_read": 0.050833, + "json_parse": 0.036667, + "canonical_validate": 0.012208, + "adapter_construct": 0.063708, + "artist_setup": 12.919125, + "agg_raster_draw": 3.999584, + "pixel_buffer_copy": 1.547667, + "png_export": 18.307084 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 15, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/results/correctness-xgi-plot-4.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 135544832, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 189, + "repetition": 4, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "scientific", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 6.384708, + "file_read": 0.084, + "json_parse": 0.567584, + "canonical_validate": 0.168375, + "adapter_construct": 1.1915 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 481, + "native_multiplicity_preserved": false, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 72564736, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 190, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "mathlib", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 271.296542, + "file_read": 6.088625, + "json_parse": 60.900791, + "canonical_validate": 12.670875, + "adapter_construct": 166.9665 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 293797888, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 191, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "mathlib", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.380042, + "file_read": 2.116333, + "json_parse": 58.149625, + "canonical_validate": 12.571917, + "adapter_construct": 108.1725 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "696929b325b7297107f85eb811adb95460bc8bd298126f265d9f746cdce3b2e1", + "seed": 20261003, + "counts": { + "vertices": 9384, + "hyperedges": 44906, + "memberships": 118066, + "maximum_arity": 8532 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 44906, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 3, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 249053184, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 192, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "large", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.512, + "file_read": 0.636084, + "json_parse": 11.696625, + "canonical_validate": 4.093667, + "adapter_construct": 34.99625 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 109854720, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 193, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "large", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 860.484333, + "file_read": 0.269083, + "json_parse": 11.665208, + "canonical_validate": 3.971875, + "adapter_construct": 1154.394292 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "e5885423a1d56e60cab0986125439d089c292fe0eacc7778012029c0c1dcec31", + "seed": 20261003, + "counts": { + "vertices": 10000, + "hyperedges": 10000, + "memberships": 66000, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n1320", + "n1441", + "n4022", + "n4917", + "n5125", + "n5131", + "n5826", + "n6824", + "n7144", + "n7458", + "n7825" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 10000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 11, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 292782080, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 194, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "xgi", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 291.713791, + "file_read": 0.045958, + "json_parse": 0.466083, + "canonical_validate": 0.153666, + "adapter_construct": 1.364 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 111132672, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 195, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypergraphx", + "dataset": "high-arity-10", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 6.478834, + "file_read": 0.229625, + "json_parse": 0.655917, + "canonical_validate": 0.142333, + "adapter_construct": 1.058291 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "9aff0db69c344a2d1f0b9d23ad5062b5b09d306e6c04e25c080ff9179cea96a1", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 200, + "memberships": 2000, + "maximum_arity": 10 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 200, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": false, + "edge_id_storage": "canonical metadata; HGX uses member tuples", + "empty_edges_expected": 0, + "isolated_nodes_expected": 126, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": true, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 72663040, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 196, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 10343.477542, + "file_read": 0.375833, + "json_parse": 1.228875, + "canonical_validate": 0.645209, + "adapter_construct": 181.104625 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 222904320, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "Matplotlib is building the font cache; this may take a moment.\n", + "order": 0, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "hypernetx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 763.005875, + "file_read": 0.050875, + "json_parse": 1.311542, + "canonical_validate": 0.682667, + "adapter_construct": 161.531667, + "artist_setup": 2736.231958, + "agg_raster_draw": 1091.348041, + "pixel_buffer_copy": 5.53175, + "png_export": 28.826083 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/moderate-hypernetx-plot--1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 254066688, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 1, + "repetition": -1, + "mode": "plot", + "warmup": true + }, + { + "tool": "hypernetx", + "dataset": "correctness", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 751.228708, + "file_read": 2.264208, + "json_parse": 0.041375, + "canonical_validate": 0.009291, + "adapter_construct": 4.932458, + "artist_setup": 6.055583, + "agg_raster_draw": 5.359292, + "pixel_buffer_copy": 1.368542, + "png_export": 20.799833 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n13", + "n29" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 14, + "native_multiplicity_preserved": false, + "missing_edges": [ + "g0" + ], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/correctness-hypernetx-plot--1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 233046016, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 2, + "repetition": -1, + "mode": "plot", + "warmup": true + }, + { + "tool": "hypernetx", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 751.621042, + "file_read": 0.396042, + "json_parse": 0.463417, + "canonical_validate": 0.147791, + "adapter_construct": 27.468417, + "artist_setup": 493.12025, + "agg_raster_draw": 153.936584, + "pixel_buffer_copy": 1.55525, + "png_export": 34.912792 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/scientific-hypernetx-plot--1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 239042560, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 3, + "repetition": -1, + "mode": "plot", + "warmup": true + }, + { + "tool": "hypernetx", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 752.758, + "file_read": 0.039583, + "json_parse": 0.466, + "canonical_validate": 0.169125, + "adapter_construct": 27.245375 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 211992576, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 4, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "hypernetx", + "dataset": "correctness", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 755.781042, + "file_read": 0.033958, + "json_parse": 0.033584, + "canonical_validate": 0.010833, + "adapter_construct": 5.194375 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n13", + "n29" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 14, + "native_multiplicity_preserved": false, + "missing_edges": [ + "g0" + ], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 207831040, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 5, + "repetition": -1, + "mode": "construct", + "warmup": true + }, + { + "tool": "hypernetx", + "dataset": "correctness", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 757.909375, + "file_read": 0.033833, + "json_parse": 0.032375, + "canonical_validate": 0.008417, + "adapter_construct": 5.175541 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n13", + "n29" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 14, + "native_multiplicity_preserved": false, + "missing_edges": [ + "g0" + ], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 207798272, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 6, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 744.439583, + "file_read": 0.061292, + "json_parse": 1.274417, + "canonical_validate": 0.703125, + "adapter_construct": 159.922167, + "artist_setup": 2725.02975, + "agg_raster_draw": 1092.176208, + "pixel_buffer_copy": 1.465041, + "png_export": 28.13775 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/moderate-hypernetx-plot-0.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 254558208, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 7, + "repetition": 0, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 763.657834, + "file_read": 0.044875, + "json_parse": 0.444625, + "canonical_validate": 0.152, + "adapter_construct": 27.517958 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 211845120, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 8, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 762.635041, + "file_read": 0.063583, + "json_parse": 1.215542, + "canonical_validate": 0.692375, + "adapter_construct": 164.338792 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 221806592, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 9, + "repetition": 0, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "correctness", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 767.674917, + "file_read": 0.065125, + "json_parse": 0.032541, + "canonical_validate": 0.008708, + "adapter_construct": 4.977875, + "artist_setup": 6.085208, + "agg_raster_draw": 5.47725, + "pixel_buffer_copy": 1.884042, + "png_export": 21.241666 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n13", + "n29" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 14, + "native_multiplicity_preserved": false, + "missing_edges": [ + "g0" + ], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/correctness-hypernetx-plot-0.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 233062400, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 10, + "repetition": 0, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 769.612083, + "file_read": 0.057667, + "json_parse": 0.463208, + "canonical_validate": 0.165958, + "adapter_construct": 27.67325, + "artist_setup": 498.880958, + "agg_raster_draw": 154.703709, + "pixel_buffer_copy": 2.349209, + "png_export": 36.218458 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/scientific-hypernetx-plot-0.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 239337472, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 11, + "repetition": 0, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 752.170583, + "file_read": 0.0475, + "json_parse": 0.444458, + "canonical_validate": 0.160875, + "adapter_construct": 27.300875, + "artist_setup": 496.032083, + "agg_raster_draw": 155.052291, + "pixel_buffer_copy": 1.7355, + "png_export": 36.482625 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/scientific-hypernetx-plot-1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 239206400, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 12, + "repetition": 1, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 757.249958, + "file_read": 0.061, + "json_parse": 1.235708, + "canonical_validate": 0.663417, + "adapter_construct": 159.61725, + "artist_setup": 2741.89425, + "agg_raster_draw": 1087.932209, + "pixel_buffer_copy": 1.537208, + "png_export": 28.050833 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/moderate-hypernetx-plot-1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 254197760, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 13, + "repetition": 1, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 756.974916, + "file_read": 0.054292, + "json_parse": 1.341292, + "canonical_validate": 0.716167, + "adapter_construct": 162.542792 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 221626368, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 14, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "correctness", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 751.99175, + "file_read": 0.03725, + "json_parse": 0.030667, + "canonical_validate": 0.008458, + "adapter_construct": 4.92825 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n13", + "n29" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 14, + "native_multiplicity_preserved": false, + "missing_edges": [ + "g0" + ], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 207749120, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 15, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 749.28525, + "file_read": 0.042541, + "json_parse": 0.458125, + "canonical_validate": 0.166708, + "adapter_construct": 27.635375 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 211976192, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 16, + "repetition": 1, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "correctness", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 758.514083, + "file_read": 0.048417, + "json_parse": 0.033084, + "canonical_validate": 0.008958, + "adapter_construct": 4.998208, + "artist_setup": 6.104083, + "agg_raster_draw": 5.41975, + "pixel_buffer_copy": 1.461042, + "png_export": 20.911458 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n13", + "n29" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 14, + "native_multiplicity_preserved": false, + "missing_edges": [ + "g0" + ], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/correctness-hypernetx-plot-1.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 233127936, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 17, + "repetition": 1, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "correctness", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 760.757833, + "file_read": 0.039834, + "json_parse": 0.029959, + "canonical_validate": 0.008584, + "adapter_construct": 4.866708, + "artist_setup": 5.991625, + "agg_raster_draw": 5.564208, + "pixel_buffer_copy": 1.447375, + "png_export": 20.905958 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n13", + "n29" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 14, + "native_multiplicity_preserved": false, + "missing_edges": [ + "g0" + ], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/correctness-hypernetx-plot-2.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 233177088, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 18, + "repetition": 2, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 753.176083, + "file_read": 0.049, + "json_parse": 0.493167, + "canonical_validate": 0.158541, + "adapter_construct": 27.463625 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 211845120, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 19, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 751.568, + "file_read": 0.061709, + "json_parse": 1.363, + "canonical_validate": 0.658417, + "adapter_construct": 161.783542 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 221708288, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 20, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 754.032166, + "file_read": 0.058334, + "json_parse": 1.196333, + "canonical_validate": 0.678791, + "adapter_construct": 159.596166, + "artist_setup": 2717.868041, + "agg_raster_draw": 1087.49425, + "pixel_buffer_copy": 1.600667, + "png_export": 27.976084 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/moderate-hypernetx-plot-2.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 254148608, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 21, + "repetition": 2, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 753.314916, + "file_read": 0.046792, + "json_parse": 0.458959, + "canonical_validate": 0.154542, + "adapter_construct": 27.682667, + "artist_setup": 500.677833, + "agg_raster_draw": 154.852625, + "pixel_buffer_copy": 1.526416, + "png_export": 35.059709 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/scientific-hypernetx-plot-2.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 239239168, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 22, + "repetition": 2, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "correctness", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 755.6835, + "file_read": 0.033666, + "json_parse": 0.033917, + "canonical_validate": 0.008125, + "adapter_construct": 5.269834 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n13", + "n29" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 14, + "native_multiplicity_preserved": false, + "missing_edges": [ + "g0" + ], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 207749120, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 23, + "repetition": 2, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 755.231709, + "file_read": 0.049292, + "json_parse": 0.465625, + "canonical_validate": 0.159292, + "adapter_construct": 27.486583, + "artist_setup": 504.24175, + "agg_raster_draw": 154.867125, + "pixel_buffer_copy": 1.564708, + "png_export": 35.441167 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/scientific-hypernetx-plot-3.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 239288320, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 24, + "repetition": 3, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 757.944458, + "file_read": 0.062708, + "json_parse": 1.276042, + "canonical_validate": 0.702666, + "adapter_construct": 159.838125 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 221642752, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 25, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "correctness", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 759.261, + "file_read": 0.04225, + "json_parse": 0.03125, + "canonical_validate": 0.008333, + "adapter_construct": 5.0835, + "artist_setup": 5.964375, + "agg_raster_draw": 5.47525, + "pixel_buffer_copy": 1.73575, + "png_export": 21.183541 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n13", + "n29" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 14, + "native_multiplicity_preserved": false, + "missing_edges": [ + "g0" + ], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/correctness-hypernetx-plot-3.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 233193472, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 26, + "repetition": 3, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 756.206792, + "file_read": 0.061709, + "json_parse": 1.278458, + "canonical_validate": 0.706542, + "adapter_construct": 161.257875, + "artist_setup": 2701.218542, + "agg_raster_draw": 1089.548291, + "pixel_buffer_copy": 2.219875, + "png_export": 28.346292 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/moderate-hypernetx-plot-3.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 253984768, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 27, + "repetition": 3, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 770.403958, + "file_read": 0.058334, + "json_parse": 0.472583, + "canonical_validate": 0.163792, + "adapter_construct": 27.799583 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 212090880, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 28, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "correctness", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 751.852666, + "file_read": 0.037667, + "json_parse": 0.031792, + "canonical_validate": 0.008791, + "adapter_construct": 4.959375 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n13", + "n29" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 14, + "native_multiplicity_preserved": false, + "missing_edges": [ + "g0" + ], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 207683584, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 29, + "repetition": 3, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 787.502208, + "file_read": 0.081708, + "json_parse": 1.27025, + "canonical_validate": 0.696541, + "adapter_construct": 161.628417, + "artist_setup": 2709.734167, + "agg_raster_draw": 1090.619958, + "pixel_buffer_copy": 1.588708, + "png_export": 28.119 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/moderate-hypernetx-plot-4.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 254492672, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 30, + "repetition": 4, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "moderate", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 763.118916, + "file_read": 0.061, + "json_parse": 1.237583, + "canonical_validate": 0.695084, + "adapter_construct": 159.844417 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "7581070d4ca0672f5a0e44d2190d14e5c30a03ab424893bedf2622bcc9b86bca", + "seed": 20261003, + "counts": { + "vertices": 1000, + "hyperedges": 2000, + "memberships": 13200, + "maximum_arity": 16 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 2000, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 221577216, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 31, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 759.053625, + "file_read": 0.051709, + "json_parse": 0.455625, + "canonical_validate": 0.159917, + "adapter_construct": 27.489833, + "artist_setup": 503.912792, + "agg_raster_draw": 156.33925, + "pixel_buffer_copy": 1.595667, + "png_export": 35.96675 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/scientific-hypernetx-plot-4.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 239271936, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 32, + "repetition": 4, + "mode": "plot", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "scientific", + "timed": true, + "status": "ok", + "stages_ms": { + "tool_import": 753.316166, + "file_read": 0.055375, + "json_parse": 0.443166, + "canonical_validate": 0.148417, + "adapter_construct": 27.443 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "1e721ab444d6b769b2c27f6d23818eb0a847a5ad14dd039c7ff0818450e5baec", + "seed": 20261003, + "counts": { + "vertices": 516, + "hyperedges": 903, + "memberships": 1550, + "maximum_arity": 11 + }, + "correctness": { + "exact_canonical_roundtrip": true, + "missing_nodes": [], + "opaque_record_values_preserved": true, + "native_hyperedge_count": 903, + "native_multiplicity_preserved": true, + "missing_edges": [], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 0, + "isolated_nodes_expected": 0, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 211943424, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 33, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "correctness", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 778.720459, + "file_read": 0.042833, + "json_parse": 0.0315, + "canonical_validate": 0.008792, + "adapter_construct": 5.145541 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n13", + "n29" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 14, + "native_multiplicity_preserved": false, + "missing_edges": [ + "g0" + ], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "process_peak_rss_bytes": 207732736, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 34, + "repetition": 4, + "mode": "construct", + "warmup": false + }, + { + "tool": "hypernetx", + "dataset": "correctness", + "timed": true, + "status": "unsupported_records", + "stages_ms": { + "tool_import": 760.475375, + "file_read": 0.050709, + "json_parse": 0.032041, + "canonical_validate": 0.008541, + "adapter_construct": 5.059167, + "artist_setup": 6.026375, + "agg_raster_draw": 5.614083, + "pixel_buffer_copy": 2.171542, + "png_export": 21.036917 + }, + "versions": { + "hypernetx": "2.4.3", + "xgi": "0.10.2", + "hypergraphx": "1.8.0", + "matplotlib": "3.11.2", + "pillow": "12.3.0" + }, + "sha256": "337abbc12cccb553a57fb410e18f9eb28a8c9f8df5a7b4a7129be0a85c6f5062", + "seed": 20261003, + "counts": { + "vertices": 30, + "hyperedges": 15, + "memberships": 57, + "maximum_arity": 8 + }, + "correctness": { + "exact_canonical_roundtrip": false, + "missing_nodes": [ + "n13", + "n29" + ], + "opaque_record_values_preserved": false, + "native_hyperedge_count": 14, + "native_multiplicity_preserved": false, + "missing_edges": [ + "g0" + ], + "extra_nodes": [], + "extra_edges": [], + "wrong_memberships": [], + "wrong_metadata": [], + "graph_metadata_equal": true, + "native_edge_ids": true, + "edge_id_storage": "native IDs", + "empty_edges_expected": 1, + "isolated_nodes_expected": 2, + "duplicate_member_occurrences": 0, + "membership_semantics": "unordered unique-member sets; original record retained as opaque metadata" + }, + "plot_semantics": { + "empty_edges_have_no_geometry": true, + "hypergraphx_singleton_groups_have_no_group_geometry": false, + "common_coordinates": "canonical XY; Z discarded for this 2D track" + }, + "png": "target/benchmark/science/hnx-final/correctness-hypernetx-plot-4.png", + "png_dimensions": [ + 1920, + 1080 + ], + "process_peak_rss_bytes": 233209856, + "rss_scope": "whole isolated worker, includes imports and validation", + "stderr": "", + "order": 35, + "repetition": 4, + "mode": "plot", + "warmup": false + } +] diff --git a/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-requirements.txt b/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-requirements.txt new file mode 100644 index 0000000..8d4b91a --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-requirements.txt @@ -0,0 +1,40 @@ +backports-zstd==1.7.0 +bitsets==0.9.1 +certifi==2026.7.22 +charset-normalizer==3.5.2 +cloudpickle==3.1.2 +concepts==0.9.2 +contourpy==1.4.0 +cycler==0.12.1 +decorator==5.3.1 +fastjsonschema==2.22.2 +fonttools==4.66.1 +graphviz==0.21 +heapdict==1.0.1 +hypergraphx==1.8.0 +hypernetx==2.4.3 +idna==3.20 +igraph==1.0.0 +joblib==1.6.0 +kiwisolver==1.5.1 +matplotlib==3.11.2 +narwhals==2.26.0 +networkx==3.7 +numpy==2.5.3 +packaging==26.3 +pandas==2.3.3 +pillow==12.3.0 +pyparsing==3.3.3 +python-dateutil==2.9.0.post0 +pytz==2026.4 +requests==2.34.2 +scikit-learn==1.9.1 +scipy==1.18.1 +six==1.17.0 +texttable==1.7.0 +threadpoolctl==3.7.0 +tqdm==4.70.1 +tzdata==2026.5 +urllib3==2.8.0 +xgi==0.10.2 +xopen==2.1.0 diff --git a/docs/benchmarks/ecosystem-2026-10-03/methodology.md b/docs/benchmarks/ecosystem-2026-10-03/methodology.md new file mode 100644 index 0000000..5129f0b --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/methodology.md @@ -0,0 +1,56 @@ +# Method and limits + +These are exploratory measurements on one Mac, collected on October 3, 2026. They inform positioning and roadmap work. They are not a universal speed ranking, a human usability study, or release guarantees. + +## Machine and execution + +Apple M5 Pro, 15 CPU cores, 16 GPU cores, 24 GiB RAM, macOS 27 arm64; Rust 1.89, release builds, CPython 3.12, Node 24.16.0. Browser evidence records the actual Chromium revision and ANGLE Metal renderer. Hyper uses Bevy 0.18.1 and Metal. Low power mode is off. Power-source reporting says AC while the battery reports discharging. Thermal telemetry was unavailable. A late checkpoint confirmed display sleep is configured off and idle display/system sleep assertions are active. Ordinary desktop background load and variable display refresh were not controlled. + +Tracks run sequentially, without another benchmark or compilation running. Tool/dataset order is randomized with seed `20261003` inside each repetition. Tracks themselves run in blocks; block order is not randomized. Report medians of five per-run measurements, with per-run p95 ranges where available. Quantiles use linear interpolation. Average FPS is sample count divided by total elapsed interval time; it is not the inverse of median frame time. + +## Inputs + +`scripts/benchmarks/datasets.py` produces native JSON, HIF, explicit bipartite incidence JSON and a shared synthetic update for each dataset. The manifest records every file's SHA-256, counts and provenance. Fixed seeded XYZ coordinates are shared; 2D tools use XY. Native geometry uses `f32`, with corresponding numeric rounding. Layout quality is not tested by frozen-coordinate rendering. + +| Dataset | Vertices | Groups | Incidences | Largest group | +|---|---:|---:|---:|---:| +| Correctness | 30 | 15 | 57 | 8 | +| Moderate | 1,000 | 2,000 | 13,200 | 16 | +| Large | 10,000 | 10,000 | 66,000 | 16 | +| Arity 10 | 1,000 | 200 | 2,000 | 10 | +| Arity 100 | 1,000 | 200 | 20,000 | 100 | +| Arity 1,000 | 1,000 | 200 | 200,000 | 1,000 | +| Diseasome | 516 | 903 | 1,550 | 11 | +| Mathlib | 9,384 | 44,906 | 118,066 | 8,532 | + +Synthetic inputs are CC0. Diseasome comes from [XGI-DATA's pinned Zenodo record](https://doi.org/10.5281/zenodo.21909416), which declares CC-BY-4.0. Node and group IDs receive reversible `n:`/`g:` namespace prefixes. Mathlib comes from commit `d13f23b723b8a846827a245b89c10fc7d3f11612`, release v4.34.1, under the source project's Apache-2.0 license. Its source hash is `dde69137f0279fdb1ec0b40e16dd5252c0a13aa8550c9508e71ea811565347f3`. Import pairs are undirected in this visualization; source/target remain attributes. Updates are synthetic membership changes, not valid changes to Mathlib's dependency source. + +Updates replace membership in the first ceil(1% × group count) records, keeping vertex/group IDs and vertex coordinates. Hub positions become updated member centroids. All member sets and source records are checked independently. Rich/directed HIF semantics and duplicate-identity rejection are covered by the project's interoperability tests, outside the timed fixture suite. + +## Tracks + +**Scientific figures:** pinned HyperNetX 2.4.3, XGI 0.10.2, Hypergraphx 1.8.0, Matplotlib 3.11.2 and Pillow 12.3.0. Each observation uses a fresh process, with a discarded warmup and five repetitions. Imports, file read, JSON parse, canonical validation, library construction, artist setup, Agg rasterization, pixel copy and PNG compression are separate stages. Top-level scientific library imports and common Matplotlib/Pillow initialization are not charged to construction. The artist stage includes the public drawing call and any lazy drawing-module initialization. Figures are 1920 × 1080 at 100 dpi, with labels off and supplied XY positions. Hull/polygon algorithms differ: HNX uses rubber-band outlines, XGI convex hulls, and HGX its default 12 Chaikin refinement rounds. Thus these are stock figure workflows rather than identical raster workloads. No large/stress plots are attempted outside the chosen correctness, moderate and Diseasome figure cases. Whole figure workers have a 120-second timeout. + +Scientific metadata checks store original records as opaque attributes. HNX document metadata uses a custom graph attribute; HGX stores canonical edge IDs as metadata because its native identities are member tuples. Checks compare with independently parsed immutable source bytes and separately flag native node/edge identity loss. These model checks do not establish each library's own file-export fidelity or GUI inspection behavior. + +**Browser incidence rendering:** pinned 3d-force-graph 1.80.1, Cytoscape.js 3.34.3, Sigma 3.0.3 and Graphology 0.26.0. The browser is headed, with 1920 × 1080 content and DPR 1. Every node, empty-edge hub and incidence is supplied without sampling. Labels and physics are off. Each repetition has 120 warmup callbacks and 300 recorded callbacks, with scripted camera/redraw movement. A 90-second per-job budget covers load, validation, warmup, sampling, update and evidence capture. An exceeded budget is not a measured per-frame duration. Partial failures are saved and pages closed before continuing. + +3d-force-graph uses perspective spheres, resolution 8, radius parameter 2 and thin lines. Its public Three.js renderer is invoked directly with animation/interaction disabled. Sigma uses WebGL 2D and its public `scheduleRender` cached redraw path; Cytoscape uses Canvas2D and schedules redraw through `forceRender`. Their JavaScript command times represent different submission paths. rAF intervals represent callback cadence, without GPU completion or presentation timestamps. Native vs browser cadence also depends on vsync. The corrected Sigma block showed roughly 16.7 ms callback intervals on smaller cases, versus roughly 8.3 ms in some original Cytoscape cases. The browser version and machine were unchanged, but display cadence was not locked; callback-rate ratios across these blocks cannot establish engine efficiency. Three.js counters describe the last render submission; GPU duration and browser peak RSS are unmeasured. + +**Hyper core:** fresh optimized processes, five solver warmup steps, then 20 recorded steps, plus one discarded process run per dataset. Native parsing, offline HIF parsing/schema validation, viewer conversion, bipartite projection and all-group CPU hull construction are independent operations. HIF document round trips and projection memberships are verified. Hulls cap geometry at 24 selected vertices, so high-arity surfaces are approximate. The combined worker retains multiple documents for correctness checks; its RSS is not the memory needed for one parsed document. + +**Hyper native rendering:** the opt-in `benchmark` feature invokes the production renderer. Fixed input positions replace automatic startup layout. The window is physical 1920 × 1080, DPR 1, AutoVsync, 120 warmup frames and 300 recorded frames per fresh process. All links are enabled, with native line opacity 0.10 (browser line opacity is 0.35). HUD and picking systems remain installed; scripted orbit replaces camera input. Basic modes disable labels. Hull modes use ordinary filled hulls, no wireframe, with the 24-vertex cap. Live modes take one timed layout step per frame; frozen modes take none. Live hulls use production skinning between topology rebuilds every eight frames. A solver-iteration assertion rejects extra untimed steps. The stock viewer normally uses a different step count, so this is an explicit benchmark setting. Bipartite incidence and Mathlib's star/group mode are separate representations. In star mode, dyadic groups render pair lines, despite having no incidence links or spring edges in the scene model. Its live layout therefore performs different force work from bipartite layout. Star without hulls is an ablation: larger groups have no surface geometry. It cannot be treated as an equivalent full-group rendering score. Default label-enabled modes are separately identified. Their Capped policy uses a graph-count threshold of 250, with selected/hovered exemptions; it does not draw every label on these larger inputs. + +Native intervals are CPU application frame boundaries, including pipeline waits, rather than GPU duration or verified presentation. A scene replacement uses the shared 1% update in memory, excluding file-watch polling, I/O and parsing. Commit time and the next application interval are reported separately. The shared update changes 1% of groups, but adapter commit strategies differ: Cytoscape updates all supplied node data/positions and edge endpoints, Sigma diffs its Graphology records then performs a full geometry refresh, 3d-force-graph replaces its graph data, and Hyper replaces its scene while preserving positions. These timings describe the current public-API integrations and can include unchanged-record work; they do not isolate an optimal incremental algorithm. Browser update feedback is a two-rAF surrogate. Neither is hardware input-to-visible latency. RSS uses macOS `/usr/bin/time -l`, includes startup, and includes benchmark-retained source/update data and correctness work. These memory scopes differ between tracks. + +## Remaining limits + +Human task accuracy, perceptual ambiguity, layout convergence, actual input-to-visible latency, GPU timestamps and cross-platform performance remain unmeasured. Picking is installed in the native viewer, but no controlled hover/click study is performed. Frozen coordinates do not assess the readability of each tool's automatic layout. Metadata retention by an adapter is distinct from attribute inspection in the product. + +Graphia import/save and HyperGodot group drawing have separate adapters and representations. HyperGodot uses stock HiDPI and verifies its physical render texture at 1920×1080 (screen scale 2). The earlier HiDPI-disabled 1920×917 clamped-window run is an excluded diagnostic. Hyper requests 1920×1080 physical pixels and an untimed native screenshot verified that render size; individual native runs do not record an additional render-target size query. Graphia GraphML retains the incidence topology, roles, original IDs, arbitrary `attrs` and XYZ attributes, but omits document metadata and native top-level display fields (label/kind/status/weight). This is not a complete HIF-document round trip. Its GraphML stores XYZ as attributes; the tested headless path does not establish common render coordinates. HNX Widget cannot freeze its edge-hub positions through the reviewed public interface. HGPolyVis's supplied executable requires Windows. Julia's runtime was verified, but optional Julia package timings are absent. PAOHVis needs a temporal dataset/task. Gephi and egui_graphs remain references outside this automated rendering harness. + +Native whole-batch timeouts also occurred on a small fixture, so they are unresolved runtime/workflow failures, not proof of a graph-size capacity boundary. Completed frame traces remain conditional on completion. Diagnostic and discarded driver runs are excluded from published summaries. No competitor implementation is optimized or altered to improve scores. HyperGodot's disposable copy removes an unused autoload whose referenced resource is missing; that packaging repair and its coordinate instrumentation are recorded explicitly. + +## Source-audit follow-up + +`animation::node_motion` searches the entire hyperedge list for each hub, and `render::update_node_positions` calls it for each node. This is a confirmed quadratic lookup path as group count grows, including hidden hub entities. `GraphLayout::hub_status` already provides an indexed cache. This is a concrete profiling/refactoring candidate; timing attribution to that path has not been isolated, and production code was left unchanged for the benchmark baseline. diff --git a/docs/benchmarks/ecosystem-2026-10-03/summary.csv b/docs/benchmarks/ecosystem-2026-10-03/summary.csv new file mode 100644 index 0000000..864672f --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/summary.csv @@ -0,0 +1,90 @@ +adapter_construct_ms,agg_raster_draw_ms,all_hulls_cpu_ms,artist_setup_ms,attempts,average_fps,bipartite_projection_ms,canonical_validate_ms,dataset,exact_incidence_records,exact_model_records,exact_parser_memberships,failure_stages,file_read_ms,frame_p50_ms,frame_p95_ms,frame_p95_run_max_ms,frame_p95_run_min_ms,hif_parse_validate_ms,hif_viewer_conversion_ms,import_save_wall_ms,json_parse_ms,layout_step_p50_ms,load_wall_ms,matched_requested_viewport,mode,native_parse_ms,peak_rss_mib_median,pixel_buffer_copy_ms,png_export_ms,render_target_size,startup_construction_ms,statuses,successful_runs,tool,tool_import_ms,track,update_commit_ms,update_next_frame_ms,update_two_raf_ms,viewport_size,whole_session_peak_rss_mib +,,,,5,120.00480019200768,,,high-arity-10,,,,[],,8.300000000000182,8.5,8.699999999999829,8.400000000000091,,,,,,211.85579199995846,,camera-motion,,,,,,,"[""completed""]",5,3d-force-graph,,browser,0.7999999523162842,,20.300000071525574,, +,,,,5,119.99999999999997,,,high-arity-10,,,,[],,8.300000000000182,9.09999999999992,16.700000000000045,9.0,,,,,,277.26941599999554,,camera-motion,,,,,,,"[""completed""]",5,cytoscape,,browser,45.700000047683716,,96.5,, +,,,,5,59.99640021598704,,,high-arity-10,,,,[],,16.699999999999818,17.5,17.504999999999974,17.5,,,,,,162.49337499999092,,camera-motion,,,,,,,"[""completed""]",5,sigma,,browser,4.100000023841858,,10.5,, +,,,,5,24.70844040324175,,,high-arity-100,,,,[],,41.699999999998,50.0,50.39999999999973,41.799999999999365,,,,,,953.8225000000093,,camera-motion,,,,,,,"[""completed""]",5,3d-force-graph,,browser,5.800000071525574,,745,, +,,,,5,120.0,,,high-arity-100,,,,[],,8.300000000000182,9.199999999999818,9.200000000000273,9.100000000000364,,,,,,1282.8094170000404,,camera-motion,,,,,,,"[""completed""]",5,cytoscape,,browser,2450,,2771.5,, +,,,,5,59.99640021598704,,,high-arity-100,,,,[],,16.699999999999818,17.5,17.5,17.40000000000009,,,,,,663.5854169999948,,camera-motion,,,,,,,"[""completed""]",5,sigma,,browser,13.900000095367432,,32,, +,,,,5,,,,high-arity-1000,,,,"[""incremental update"", ""timed sampling""]",,,,,,,,,,,,,camera-motion,,,,,,,"[""failed""]",0,3d-force-graph,,browser,,,,, +,,,,5,,,,high-arity-1000,,,,"[""timed sampling""]",,,,,,,,,,,,,camera-motion,,,,,,,"[""failed""]",0,cytoscape,,browser,,,,, +,,,,5,43.15925766076823,,,high-arity-1000,,,,[],,16.700000000000728,50.0,50.100000000000364,50.0,,,,,,5762.138208000048,,camera-motion,,,,,,,"[""completed""]",5,sigma,,browser,167.79999995231628,,351.2999999523163,, +,,,,5,5.217109705983209,,,large,,,,"[""incremental update""]",,191.64999999999782,203.62000000000552,205.91000000000787,201.3300000000032,,,,,,3335.5768124999013,,camera-motion,,,,,,,"[""completed"", ""failed""]",2,3d-force-graph,,browser,48.34999996423721,,4161.699999988079,, +,,,,5,120.01920787479659,,,large,,,,"[""incremental update""]",,8.300000000000182,9.299999999999272,9.299999999999272,9.205000000000656,,,,,,5253.855395499966,,camera-motion,,,,,,,"[""completed"", ""failed""]",4,cytoscape,,browser,51638.90000003576,,53253.200000047684,, +,,,,5,59.99640021598705,,,large,,,,[],,16.699999999999818,17.5,17.504999999999974,17.399999999999682,,,,,,2999.3059169999906,,camera-motion,,,,,,,"[""completed""]",5,sigma,,browser,1079.8000000715256,,1160.9000000953674,, +,,,,5,7.371822437370225,,,mathlib,,,,[],,133.3000000000029,150.0,191.3150000000027,134.0,,,,,,7748.540666999994,,camera-motion,,,,,,,"[""completed""]",5,3d-force-graph,,browser,73.30000007152557,,11238.900000095367,, +,,,,5,,,,mathlib,,,,"[""incremental update"", ""timed sampling""]",,,,,,,,,,,,,camera-motion,,,,,,,"[""failed""]",0,cytoscape,,browser,,,,, +,,,,5,59.99640021598705,,,mathlib,,,,[],,16.69999999999891,17.5,17.600000000000364,17.399999999999636,,,,,,7231.883042000001,,camera-motion,,,,,,,"[""completed""]",5,sigma,,browser,13242.900000095367,,13612.400000095367,, +,,,,5,35.40240736370073,,,moderate,,,,[],,25.100000000000364,33.399999999999636,34.10500000000038,33.399999999999636,,,,,,727.1333330001216,,camera-motion,,,,,,,"[""completed""]",5,3d-force-graph,,browser,4.200000047683716,,414.7999999523163,, +,,,,5,120.0,,,moderate,,,,[],,8.300000000000182,9.205000000000656,10.0,9.199999999999818,,,,,,1014.5705409999937,,camera-motion,,,,,,,"[""completed""]",5,cytoscape,,browser,1342.6000000238419,,1586.5,, +,,,,5,59.99640021598704,,,moderate,,,,[],,16.699999999999818,17.5,17.59999999999993,17.404999999999657,,,,,,538.9120419999817,,camera-motion,,,,,,,"[""completed""]",5,sigma,,browser,42.199999928474426,,56.5,, +,,,,5,120.0,,,scientific,,,,[],,8.300000000000182,8.400000000000091,8.704999999999837,8.400000000000091,,,,,,190.25154200010002,,camera-motion,,,,,,,"[""completed""]",5,3d-force-graph,,browser,0.7999999523162842,,17.800000071525574,, +,,,,5,120.00480019200768,,,scientific,,,,[],,8.300000000000182,9.200000000000273,10.0,9.099999999999909,,,,,,247.11237500002608,,camera-motion,,,,,,,"[""completed""]",5,cytoscape,,browser,37.5,,79.19999992847443,, +,,,,5,59.99640021598705,,,scientific,,,,[],,16.699999999999818,17.5,17.5,17.399999999999636,,,,,,148.42616599996109,,camera-motion,,,,,,,"[""completed""]",5,sigma,,browser,7.5,,10.700000047683716,, +,,1.284417,,5,,0.15075,,high-arity-10,,,,,,,,,,3.872959,3.3592079999999997,,,0.899458,,,frozen,1.12825,34.5625,,,,,"[""ok""]",5,Hyper,,core,,,,, +,,37.928333,,5,,0.611,,high-arity-100,,,,,,,,,,12.999042000000001,15.998292000000001,,,1.1687705,,,frozen,1.478708,91.78125,,,,,"[""ok""]",5,Hyper,,core,,,,, +,,44.921375,,5,,4.31825,,high-arity-1000,,,,,,,,,,104.577791,147.098334,,,1.7627920000000001,,,frozen,4.778042,677.53125,,,,,"[""ok""]",5,Hyper,,core,,,,, +,,78.29204200000001,,5,,4.0865,,large,,,,,,,,,,60.409125,85.064167,,,31.9006245,,,frozen,14.190125,423.453125,,,,,"[""ok""]",5,Hyper,,core,,,,, +,,10.773333,,5,,15.366,,mathlib,,,,,,,,,,141.10008399999998,228.002417,,,97.660187,,,frozen,36.261500000000005,1020.78125,,,,,"[""ok""]",5,Hyper,,core,,,,, +,,15.588708,,5,,0.634666,,moderate,,,,,,,,,,11.522792,12.630625,,,3.5262705,,,frozen,2.123292,80.765625,,,,,"[""ok""]",5,Hyper,,core,,,,, +,,0.109791,,5,,0.185833,,scientific,,,,,,,,,,3.079375,2.420291,,,2.0560625000000003,,,frozen,0.76725,30.078125,,,,,"[""ok""]",5,Hyper,,core,,,,, +,,,,5,118.81973976100595,,,moderate,,,True,,,8.3265,10.369250000000003,11.88155,8.6895,,,,,,,True,frozen-camera-motion,,,,,"[1920.0, 1080.0]",188.342,"[""ok""]",5,HyperGodot,,desktop-group,,,,"[1920.0, 1080.0]",336.546875 +,,,,5,,,,mathlib,True,,,,,,,,,,,1490.5674999999992,,,,,headless-import-save,,656.15625,,,,,"[""ok""]",5,Graphia,,desktop-import,,,,, +,,,,5,,,,moderate,True,,,,,,,,,,,390.8611670000006,,,,,headless-import-save,,130.734375,,,,,"[""ok""]",5,Graphia,,desktop-import,,,,, +,,,,5,,,,scientific,True,,,,,,,,,,,332.8365420000008,,,,,headless-import-save,,106.375,,,,,"[""ok""]",5,Graphia,,desktop-import,,,,, +,,,,5,119.82986869988167,,,high-arity-10,,,,,,8.343771,8.9930366,17.45418335,8.610943099999998,,,,,,,,frozen,,235.171875,,,,,"[""ok"", ""watchdog_timeout""]",4,Hyper,,native,0.24362499999999998,8.400521000000001,,, +,,,,5,64.61796892456307,,,high-arity-10,,,,,,8.6670625,17.0684306,26.3379021,16.94330175,,,,,,,,frozen+hulls,,260.625,,,,,"[""ok""]",5,Hyper,,native,0.277417,16.408915999999998,,, +,,,,5,117.86454974748196,,,high-arity-100,,,,,,8.324667,9.530175,17.019593150000002,8.696767,,,,,,,,frozen,,255.796875,,,,,"[""ok""]",5,Hyper,,native,0.870208,8.27925,,, +,,,,5,49.447122452522876,,,high-arity-100,,,,,,16.717312,33.508365250000004,33.732054500000004,25.29628335,,,,,,,,frozen+hulls,,271.90625,,,,,"[""ok""]",5,Hyper,,native,0.5804579999999999,42.724375,,, +,,,,5,71.99500139281292,,,high-arity-1000,,,,,,12.6834165,17.106894099999998,20.55327295,11.7243215,,,,,,,,frozen,,411.09375,,,,,"[""ok""]",5,Hyper,,native,6.159333,18.402124999999998,,, +,,,,5,29.77707208874065,,,high-arity-1000,,,,,,33.347542000000004,41.77612115,53.862216,34.796988150000004,,,,,,,,frozen+hulls,,427.140625,,,,,"[""ok""]",5,Hyper,,native,8.971290999999999,56.79325,,, +,,,,5,16.345351441727637,,,large,,,,,,61.367645499999995,68.10254567499999,82.65074374999999,66.62374615,,,,,,,,frozen,,700.1484375,,,,,"[""ok"", ""watchdog_timeout""]",4,Hyper,,native,5.769916499999999,62.76875,,, +,,,,5,3.5191572363889527,,,large,,,,,,270.729198,375.32187310000006,455.02350145000014,347.00522440000003,,,,,,,,frozen+hulls,,1006.703125,,,,,"[""ok"", ""watchdog_timeout""]",4,Hyper,,native,6.9078955,302.924271,,, +,,,,5,,,,mathlib,,,,,,,,,,,,,,,,,frozen,,,,,,,"[""watchdog_timeout""]",0,Hyper,,native,,,,, +,,,,5,,,,mathlib,,,,,,,,,,,,,,,,,frozen+hulls,,,,,,,"[""watchdog_timeout""]",0,Hyper,,native,,,,, +,,,,5,117.14653303371168,,,moderate,,,,,,8.312187999999999,9.6822396,17.1010667,8.726020799999999,,,,,,,,frozen,,281.796875,,,,,"[""ok""]",5,Hyper,,native,0.9233330000000001,8.483708,,, +,,,,5,19.566884191655177,,,moderate,,,,,,49.975916500000004,62.078327050000034,74.2269899,51.964571450000015,,,,,,,,frozen+hulls,,358.375,,,,,"[""ok""]",5,Hyper,,native,0.7896249999999999,58.941583,,, +,,,,5,59.998014065734424,,,moderate,,,,,,16.656437500000003,17.009687200000002,17.233082699999997,9.089753500000002,,,,,3.4929794999999997,,,live,,287.140625,,,,,"[""ok""]",5,Hyper,,native,0.907625,16.757542,,, +,,,,5,41.350638667505976,,,moderate,,,,,,21.9059585,37.07194615,43.48701425,34.935810749999995,,,,,3.4389995,,,live+hulls,,384.796875,,,,,"[""ok"", ""watchdog_timeout""]",3,Hyper,,native,0.874833,25.381958,,, +,,,,5,119.37348022637987,,,scientific,,,,,,8.413896,9.082428850000001,17.76361605,8.6574399,,,,,,,,frozen,,237.25,,,,,"[""ok""]",5,Hyper,,native,0.358834,8.502666000000001,,, +,,,,5,119.59758207431508,,,scientific,,,,,,8.332979,9.15702465,17.2186851,8.698001750000001,,,,,,,,frozen+hulls,,257.5,,,,,"[""ok""]",5,Hyper,,native,0.38875,8.541542,,, +,,,,5,5.932056024739585,,,mathlib,,,,,,162.184188,219.9086247,257.63149730000004,191.63183125,,,,,,,,frozen+hulls,,880.296875,,,,,"[""ok""]",5,Hyper,,native-interactive,10.35475,195.358458,,, +,,,,5,9.081420199218897,,,mathlib,,,,,,97.1940415,171.71294405,176.77365565,166.69555655,,,,,14.230917,,,live+hulls,,935.109375,,,,,"[""ok""]",5,Hyper,,native-interactive,9.243917000000001,97.096375,,, +,,,,5,19.04790043287936,,,moderate,,,,,,50.037853999999996,64.92663155000001,95.84127980000001,53.9034247,,,,,,,,frozen+hulls,,298.53125,,,,,"[""ok""]",5,Hyper,,native-interactive,0.36612500000000003,50.895165999999996,,, +,,,,5,50.06377464047368,,,moderate,,,,,,18.000854,30.836029449999998,37.94153095,30.097470799999996,,,,,0.8554785,,,live+hulls,,321.875,,,,,"[""ok""]",5,Hyper,,native-interactive,0.334833,20.603042,,, +,,,,5,47.75056283313845,,,mathlib,,,,,,18.1558745,25.95170445,33.882346150000004,25.77922710000001,,,,,,,,frozen,,674.3125,,,,,"[""ok""]",5,Hyper,,native-star,9.548667,31.525791999999996,,, +,,,,5,5.3932981919356555,,,mathlib,,,,,,187.351604,216.6013354,253.4044726,174.73731285000002,,,,,,,,frozen+hulls,,746.03125,,,,,"[""ok""]",5,Hyper,,native-star,12.262,205.938875,,, +,,,,5,45.09819822161659,,,mathlib,,,,,,21.7779375,24.799570850000002,27.75147535,22.542967,,,,,14.1977295,,,live,,666.34375,,,,,"[""ok""]",5,Hyper,,native-star,7.620542,42.012625,,, +,,,,5,8.846815336427651,,,mathlib,,,,,,100.75751050000001,179.874916675,220.50807085000002,173.5838253,,,,,14.70263525,,,live+hulls,,1057.625,,,,,"[""ok"", ""watchdog_timeout""]",4,Hyper,,native-star,9.592875,101.556396,,, +0.056875,,,,5,,,0.010916,correctness,,True,,,0.212541,,,,,,,,0.071667,,,,construct,,67.546875,,,,,"[""ok""]",5,hypergraphx,7.113292,scientific,,,,, +0.05575,30.262417,,1149.301958,5,,,0.010917,correctness,,True,,,0.078417,,,,,,,,0.063292,,,,plot,,125.40625,1.558458,20.909042,,,"[""ok""]",5,hypergraphx,7.001334,scientific,,,,, +5.145541,,,,5,,,0.008458,correctness,,False,,,0.03725,,,,,,,,0.031792,,,,construct,,198.125,,,,,"[""unsupported_records""]",5,hypernetx,755.6835,scientific,,,,, +4.998208,5.47725,,6.026375,5,,,0.008584,correctness,,False,,,0.048417,,,,,,,,0.032041,,,,plot,,222.375,1.73575,21.036917,,,"[""unsupported_records""]",5,hypernetx,760.475375,scientific,,,,, +0.058583,,,,5,,,0.009541,correctness,,True,,,0.047459,,,,,,,,0.034583,,,,construct,,104.5625,,,,,"[""ok""]",5,xgi,283.202042,scientific,,,,, +0.06325,3.999584,,7.765333,5,,,0.00975,correctness,,True,,,0.051208,,,,,,,,0.0355,,,,plot,,129.265625,1.547667,18.327667,,,"[""ok""]",5,xgi,284.31775,scientific,,,,, +1.0235,,,,5,,,0.149667,high-arity-10,,True,,,0.229625,,,,,,,,0.717166,,,,construct,,69.34375,,,,,"[""ok""]",5,hypergraphx,6.339625,scientific,,,,, +32.927667,,,,5,,,0.148042,high-arity-10,,False,,,0.27925,,,,,,,,0.656375,,,,construct,,202.453125,,,,,"[""unsupported_records""]",5,hypernetx,841.494375,scientific,,,,, +0.748625,,,,5,,,0.144458,high-arity-10,,True,,,0.265875,,,,,,,,0.675667,,,,construct,,106.078125,,,,,"[""ok""]",5,xgi,281.07,scientific,,,,, +6.682958,,,,5,,,0.882125,high-arity-100,,True,,,0.559625,,,,,,,,1.144125,,,,construct,,72.265625,,,,,"[""ok""]",5,hypergraphx,6.501292,scientific,,,,, +193.156208,,,,5,,,0.875542,high-arity-100,,True,,,0.098791,,,,,,,,1.05225,,,,construct,,212.765625,,,,,"[""ok""]",5,hypernetx,840.271958,scientific,,,,, +2.603958,,,,5,,,0.876333,high-arity-100,,True,,,0.439584,,,,,,,,1.118125,,,,construct,,110.828125,,,,,"[""ok""]",5,xgi,278.170625,scientific,,,,, +15.997,,,,5,,,4.725667,high-arity-1000,,False,,,0.62225,,,,,,,,5.15575,,,,construct,,89.765625,,,,,"[""unsupported_records""]",5,hypergraphx,7.142959,scientific,,,,, +1827.21875,,,,5,,,4.877208,high-arity-1000,,True,,,0.531291,,,,,,,,4.8025,,,,construct,,342.265625,,,,,"[""ok""]",5,hypernetx,900.420291,scientific,,,,, +21.3205,,,,5,,,4.70875,high-arity-1000,,True,,,2.555083,,,,,,,,4.782375,,,,construct,,142.65625,,,,,"[""ok""]",5,xgi,271.909334,scientific,,,,, +34.99625,,,,5,,,4.093667,large,,True,,,0.636084,,,,,,,,11.655541,,,,construct,,104.4375,,,,,"[""ok""]",5,hypergraphx,7.07125,scientific,,,,, +876.699333,,,,5,,,4.19175,large,,False,,,0.629625,,,,,,,,11.665208,,,,construct,,295.484375,,,,,"[""unsupported_records""]",5,hypernetx,860.484333,scientific,,,,, +53.333666,,,,5,,,4.224125,large,,True,,,0.648041,,,,,,,,11.503042,,,,construct,,148.53125,,,,,"[""ok""]",5,xgi,280.495541,scientific,,,,, +108.1725,,,,5,,,12.713375,mathlib,,True,,,2.072791,,,,,,,,58.149625,,,,construct,,237.46875,,,,,"[""ok""]",5,hypergraphx,6.886417,scientific,,,,, +1798.545625,,,,5,,,12.971375,mathlib,,False,,,8.097792,,,,,,,,62.972667,,,,construct,,650.90625,,,,,"[""unsupported_records""]",5,hypernetx,885.215458,scientific,,,,, +166.9665,,,,5,,,12.875291,mathlib,,True,,,2.040375,,,,,,,,60.567708,,,,construct,,280.421875,,,,,"[""ok""]",5,xgi,274.656875,scientific,,,,, +5.894708,,,,5,,,0.706542,moderate,,True,,,0.10275,,,,,,,,1.418208,,,,construct,,72.5625,,,,,"[""ok""]",5,hypergraphx,9.13125,scientific,,,,, +,,,,5,,,,moderate,,False,,,,,,,,,,,,,,,plot,,,,,,,"[""watchdog_timeout""]",0,hypergraphx,,scientific,,,,, +161.783542,,,,5,,,0.695084,moderate,,True,,,0.061709,,,,,,,,1.276042,,,,construct,,211.375,,,,,"[""ok""]",5,hypernetx,757.944458,scientific,,,,, +159.922167,1089.548291,,2717.868041,5,,,0.696541,moderate,,True,,,0.061292,,,,,,,,1.27025,,,,plot,,242.421875,1.588708,28.119,,,"[""ok""]",5,hypernetx,756.206792,scientific,,,,, +4.386834,,,,5,,,0.703334,moderate,,True,,,0.269875,,,,,,,,1.34325,,,,construct,,110.0,,,,,"[""ok""]",5,xgi,280.063417,scientific,,,,, +4.709417,452.824667,,214.565458,5,,,0.799083,moderate,,True,,,0.066292,,,,,,,,1.369208,,,,plot,,144.0,1.568875,37.701,,,"[""ok""]",5,xgi,275.288375,scientific,,,,, +1.101458,,,,5,,,0.161542,scientific,,False,,,0.084,,,,,,,,0.557875,,,,construct,,69.296875,,,,,"[""unsupported_records""]",5,hypergraphx,6.417375,scientific,,,,, +1.09425,396.785417,,13764.899333,5,,,0.1615,scientific,,False,,,0.085834,,,,,,,,0.518292,,,,plot,,181.5625,1.708541,40.778666,,,"[""unsupported_records""]",5,hypergraphx,6.540083,scientific,,,,, +27.517958,,,,5,,,0.158541,scientific,,True,,,0.049,,,,,,,,0.458125,,,,construct,,202.125,,,,,"[""ok""]",5,hypernetx,753.316166,scientific,,,,, +27.489833,154.867125,,500.677833,5,,,0.159917,scientific,,True,,,0.049292,,,,,,,,0.458959,,,,plot,,228.1875,1.595667,35.96675,,,"[""ok""]",5,hypernetx,755.231709,scientific,,,,, +1.325541,,,,5,,,0.154791,scientific,,True,,,0.045958,,,,,,,,0.492708,,,,construct,,105.984375,,,,,"[""ok""]",5,xgi,274.38325,scientific,,,,, +1.372583,28.11425,,23.956792,5,,,0.159708,scientific,,True,,,0.27325,,,,,,,,0.518833,,,,plot,,132.25,1.591291,33.236709,,,"[""ok""]",5,xgi,274.083,scientific,,,,, diff --git a/docs/benchmarks/ecosystem-2026-10-03/summary.json b/docs/benchmarks/ecosystem-2026-10-03/summary.json new file mode 100644 index 0000000..971f741 --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/summary.json @@ -0,0 +1,1843 @@ +[ + { + "track": "browser", + "tool": "3d-force-graph", + "dataset": "high-arity-10", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 8.300000000000182, + "frame_p95_ms": 8.5, + "average_fps": 120.00480019200768, + "frame_p95_run_min_ms": 8.400000000000091, + "frame_p95_run_max_ms": 8.699999999999829, + "load_wall_ms": 211.85579199995846, + "update_commit_ms": 0.7999999523162842, + "update_two_raf_ms": 20.300000071525574, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "cytoscape", + "dataset": "high-arity-10", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 8.300000000000182, + "frame_p95_ms": 9.09999999999992, + "average_fps": 119.99999999999997, + "frame_p95_run_min_ms": 9.0, + "frame_p95_run_max_ms": 16.700000000000045, + "load_wall_ms": 277.26941599999554, + "update_commit_ms": 45.700000047683716, + "update_two_raf_ms": 96.5, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "sigma", + "dataset": "high-arity-10", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 16.699999999999818, + "frame_p95_ms": 17.5, + "average_fps": 59.99640021598704, + "frame_p95_run_min_ms": 17.5, + "frame_p95_run_max_ms": 17.504999999999974, + "load_wall_ms": 162.49337499999092, + "update_commit_ms": 4.100000023841858, + "update_two_raf_ms": 10.5, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "3d-force-graph", + "dataset": "high-arity-100", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 41.699999999998, + "frame_p95_ms": 50.0, + "average_fps": 24.70844040324175, + "frame_p95_run_min_ms": 41.799999999999365, + "frame_p95_run_max_ms": 50.39999999999973, + "load_wall_ms": 953.8225000000093, + "update_commit_ms": 5.800000071525574, + "update_two_raf_ms": 745, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "cytoscape", + "dataset": "high-arity-100", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 8.300000000000182, + "frame_p95_ms": 9.199999999999818, + "average_fps": 120.0, + "frame_p95_run_min_ms": 9.100000000000364, + "frame_p95_run_max_ms": 9.200000000000273, + "load_wall_ms": 1282.8094170000404, + "update_commit_ms": 2450, + "update_two_raf_ms": 2771.5, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "sigma", + "dataset": "high-arity-100", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 16.699999999999818, + "frame_p95_ms": 17.5, + "average_fps": 59.99640021598704, + "frame_p95_run_min_ms": 17.40000000000009, + "frame_p95_run_max_ms": 17.5, + "load_wall_ms": 663.5854169999948, + "update_commit_ms": 13.900000095367432, + "update_two_raf_ms": 32, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "3d-force-graph", + "dataset": "high-arity-1000", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 0, + "statuses": [ + "failed" + ], + "frame_p50_ms": null, + "frame_p95_ms": null, + "average_fps": null, + "frame_p95_run_min_ms": null, + "frame_p95_run_max_ms": null, + "load_wall_ms": null, + "update_commit_ms": null, + "update_two_raf_ms": null, + "failure_stages": [ + "incremental update", + "timed sampling" + ] + }, + { + "track": "browser", + "tool": "cytoscape", + "dataset": "high-arity-1000", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 0, + "statuses": [ + "failed" + ], + "frame_p50_ms": null, + "frame_p95_ms": null, + "average_fps": null, + "frame_p95_run_min_ms": null, + "frame_p95_run_max_ms": null, + "load_wall_ms": null, + "update_commit_ms": null, + "update_two_raf_ms": null, + "failure_stages": [ + "timed sampling" + ] + }, + { + "track": "browser", + "tool": "sigma", + "dataset": "high-arity-1000", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 16.700000000000728, + "frame_p95_ms": 50.0, + "average_fps": 43.15925766076823, + "frame_p95_run_min_ms": 50.0, + "frame_p95_run_max_ms": 50.100000000000364, + "load_wall_ms": 5762.138208000048, + "update_commit_ms": 167.79999995231628, + "update_two_raf_ms": 351.2999999523163, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "3d-force-graph", + "dataset": "large", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 2, + "statuses": [ + "completed", + "failed" + ], + "frame_p50_ms": 191.64999999999782, + "frame_p95_ms": 203.62000000000552, + "average_fps": 5.217109705983209, + "frame_p95_run_min_ms": 201.3300000000032, + "frame_p95_run_max_ms": 205.91000000000787, + "load_wall_ms": 3335.5768124999013, + "update_commit_ms": 48.34999996423721, + "update_two_raf_ms": 4161.699999988079, + "failure_stages": [ + "incremental update" + ] + }, + { + "track": "browser", + "tool": "cytoscape", + "dataset": "large", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 4, + "statuses": [ + "completed", + "failed" + ], + "frame_p50_ms": 8.300000000000182, + "frame_p95_ms": 9.299999999999272, + "average_fps": 120.01920787479659, + "frame_p95_run_min_ms": 9.205000000000656, + "frame_p95_run_max_ms": 9.299999999999272, + "load_wall_ms": 5253.855395499966, + "update_commit_ms": 51638.90000003576, + "update_two_raf_ms": 53253.200000047684, + "failure_stages": [ + "incremental update" + ] + }, + { + "track": "browser", + "tool": "sigma", + "dataset": "large", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 16.699999999999818, + "frame_p95_ms": 17.5, + "average_fps": 59.99640021598705, + "frame_p95_run_min_ms": 17.399999999999682, + "frame_p95_run_max_ms": 17.504999999999974, + "load_wall_ms": 2999.3059169999906, + "update_commit_ms": 1079.8000000715256, + "update_two_raf_ms": 1160.9000000953674, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "3d-force-graph", + "dataset": "mathlib", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 133.3000000000029, + "frame_p95_ms": 150.0, + "average_fps": 7.371822437370225, + "frame_p95_run_min_ms": 134.0, + "frame_p95_run_max_ms": 191.3150000000027, + "load_wall_ms": 7748.540666999994, + "update_commit_ms": 73.30000007152557, + "update_two_raf_ms": 11238.900000095367, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "cytoscape", + "dataset": "mathlib", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 0, + "statuses": [ + "failed" + ], + "frame_p50_ms": null, + "frame_p95_ms": null, + "average_fps": null, + "frame_p95_run_min_ms": null, + "frame_p95_run_max_ms": null, + "load_wall_ms": null, + "update_commit_ms": null, + "update_two_raf_ms": null, + "failure_stages": [ + "incremental update", + "timed sampling" + ] + }, + { + "track": "browser", + "tool": "sigma", + "dataset": "mathlib", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 16.69999999999891, + "frame_p95_ms": 17.5, + "average_fps": 59.99640021598705, + "frame_p95_run_min_ms": 17.399999999999636, + "frame_p95_run_max_ms": 17.600000000000364, + "load_wall_ms": 7231.883042000001, + "update_commit_ms": 13242.900000095367, + "update_two_raf_ms": 13612.400000095367, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "3d-force-graph", + "dataset": "moderate", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 25.100000000000364, + "frame_p95_ms": 33.399999999999636, + "average_fps": 35.40240736370073, + "frame_p95_run_min_ms": 33.399999999999636, + "frame_p95_run_max_ms": 34.10500000000038, + "load_wall_ms": 727.1333330001216, + "update_commit_ms": 4.200000047683716, + "update_two_raf_ms": 414.7999999523163, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "cytoscape", + "dataset": "moderate", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 8.300000000000182, + "frame_p95_ms": 9.205000000000656, + "average_fps": 120.0, + "frame_p95_run_min_ms": 9.199999999999818, + "frame_p95_run_max_ms": 10.0, + "load_wall_ms": 1014.5705409999937, + "update_commit_ms": 1342.6000000238419, + "update_two_raf_ms": 1586.5, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "sigma", + "dataset": "moderate", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 16.699999999999818, + "frame_p95_ms": 17.5, + "average_fps": 59.99640021598704, + "frame_p95_run_min_ms": 17.404999999999657, + "frame_p95_run_max_ms": 17.59999999999993, + "load_wall_ms": 538.9120419999817, + "update_commit_ms": 42.199999928474426, + "update_two_raf_ms": 56.5, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "3d-force-graph", + "dataset": "scientific", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 8.300000000000182, + "frame_p95_ms": 8.400000000000091, + "average_fps": 120.0, + "frame_p95_run_min_ms": 8.400000000000091, + "frame_p95_run_max_ms": 8.704999999999837, + "load_wall_ms": 190.25154200010002, + "update_commit_ms": 0.7999999523162842, + "update_two_raf_ms": 17.800000071525574, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "cytoscape", + "dataset": "scientific", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 8.300000000000182, + "frame_p95_ms": 9.200000000000273, + "average_fps": 120.00480019200768, + "frame_p95_run_min_ms": 9.099999999999909, + "frame_p95_run_max_ms": 10.0, + "load_wall_ms": 247.11237500002608, + "update_commit_ms": 37.5, + "update_two_raf_ms": 79.19999992847443, + "failure_stages": [] + }, + { + "track": "browser", + "tool": "sigma", + "dataset": "scientific", + "mode": "camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "completed" + ], + "frame_p50_ms": 16.699999999999818, + "frame_p95_ms": 17.5, + "average_fps": 59.99640021598705, + "frame_p95_run_min_ms": 17.399999999999636, + "frame_p95_run_max_ms": 17.5, + "load_wall_ms": 148.42616599996109, + "update_commit_ms": 7.5, + "update_two_raf_ms": 10.700000047683716, + "failure_stages": [] + }, + { + "track": "core", + "tool": "Hyper", + "dataset": "high-arity-10", + "mode": "frozen", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 34.5625, + "native_parse_ms": 1.12825, + "hif_parse_validate_ms": 3.872959, + "hif_viewer_conversion_ms": 3.3592079999999997, + "bipartite_projection_ms": 0.15075, + "all_hulls_cpu_ms": 1.284417, + "layout_step_p50_ms": 0.899458 + }, + { + "track": "core", + "tool": "Hyper", + "dataset": "high-arity-100", + "mode": "frozen", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 91.78125, + "native_parse_ms": 1.478708, + "hif_parse_validate_ms": 12.999042000000001, + "hif_viewer_conversion_ms": 15.998292000000001, + "bipartite_projection_ms": 0.611, + "all_hulls_cpu_ms": 37.928333, + "layout_step_p50_ms": 1.1687705 + }, + { + "track": "core", + "tool": "Hyper", + "dataset": "high-arity-1000", + "mode": "frozen", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 677.53125, + "native_parse_ms": 4.778042, + "hif_parse_validate_ms": 104.577791, + "hif_viewer_conversion_ms": 147.098334, + "bipartite_projection_ms": 4.31825, + "all_hulls_cpu_ms": 44.921375, + "layout_step_p50_ms": 1.7627920000000001 + }, + { + "track": "core", + "tool": "Hyper", + "dataset": "large", + "mode": "frozen", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 423.453125, + "native_parse_ms": 14.190125, + "hif_parse_validate_ms": 60.409125, + "hif_viewer_conversion_ms": 85.064167, + "bipartite_projection_ms": 4.0865, + "all_hulls_cpu_ms": 78.29204200000001, + "layout_step_p50_ms": 31.9006245 + }, + { + "track": "core", + "tool": "Hyper", + "dataset": "mathlib", + "mode": "frozen", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 1020.78125, + "native_parse_ms": 36.261500000000005, + "hif_parse_validate_ms": 141.10008399999998, + "hif_viewer_conversion_ms": 228.002417, + "bipartite_projection_ms": 15.366, + "all_hulls_cpu_ms": 10.773333, + "layout_step_p50_ms": 97.660187 + }, + { + "track": "core", + "tool": "Hyper", + "dataset": "moderate", + "mode": "frozen", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 80.765625, + "native_parse_ms": 2.123292, + "hif_parse_validate_ms": 11.522792, + "hif_viewer_conversion_ms": 12.630625, + "bipartite_projection_ms": 0.634666, + "all_hulls_cpu_ms": 15.588708, + "layout_step_p50_ms": 3.5262705 + }, + { + "track": "core", + "tool": "Hyper", + "dataset": "scientific", + "mode": "frozen", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 30.078125, + "native_parse_ms": 0.76725, + "hif_parse_validate_ms": 3.079375, + "hif_viewer_conversion_ms": 2.420291, + "bipartite_projection_ms": 0.185833, + "all_hulls_cpu_ms": 0.109791, + "layout_step_p50_ms": 2.0560625000000003 + }, + { + "track": "desktop-group", + "tool": "HyperGodot", + "dataset": "moderate", + "mode": "frozen-camera-motion", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "frame_p50_ms": 8.3265, + "frame_p95_ms": 10.369250000000003, + "frame_p95_run_min_ms": 8.6895, + "frame_p95_run_max_ms": 11.88155, + "average_fps": 118.81973976100595, + "whole_session_peak_rss_mib": 336.546875, + "exact_parser_memberships": true, + "viewport_size": [ + 1920.0, + 1080.0 + ], + "render_target_size": [ + 1920.0, + 1080.0 + ], + "matched_requested_viewport": true, + "startup_construction_ms": 188.342 + }, + { + "track": "desktop-import", + "tool": "Graphia", + "dataset": "mathlib", + "mode": "headless-import-save", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "import_save_wall_ms": 1490.5674999999992, + "peak_rss_mib_median": 656.15625, + "exact_incidence_records": true + }, + { + "track": "desktop-import", + "tool": "Graphia", + "dataset": "moderate", + "mode": "headless-import-save", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "import_save_wall_ms": 390.8611670000006, + "peak_rss_mib_median": 130.734375, + "exact_incidence_records": true + }, + { + "track": "desktop-import", + "tool": "Graphia", + "dataset": "scientific", + "mode": "headless-import-save", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "import_save_wall_ms": 332.8365420000008, + "peak_rss_mib_median": 106.375, + "exact_incidence_records": true + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "high-arity-10", + "mode": "frozen", + "attempts": 5, + "successful_runs": 4, + "statuses": [ + "ok", + "watchdog_timeout" + ], + "peak_rss_mib_median": 235.171875, + "frame_p50_ms": 8.343771, + "frame_p95_ms": 8.9930366, + "frame_p95_run_min_ms": 8.610943099999998, + "frame_p95_run_max_ms": 17.45418335, + "average_fps": 119.82986869988167, + "update_commit_ms": 0.24362499999999998, + "update_next_frame_ms": 8.400521000000001, + "layout_step_p50_ms": null + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "high-arity-10", + "mode": "frozen+hulls", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 260.625, + "frame_p50_ms": 8.6670625, + "frame_p95_ms": 17.0684306, + "frame_p95_run_min_ms": 16.94330175, + "frame_p95_run_max_ms": 26.3379021, + "average_fps": 64.61796892456307, + "update_commit_ms": 0.277417, + "update_next_frame_ms": 16.408915999999998, + "layout_step_p50_ms": null + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "high-arity-100", + "mode": "frozen", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 255.796875, + "frame_p50_ms": 8.324667, + "frame_p95_ms": 9.530175, + "frame_p95_run_min_ms": 8.696767, + "frame_p95_run_max_ms": 17.019593150000002, + "average_fps": 117.86454974748196, + "update_commit_ms": 0.870208, + "update_next_frame_ms": 8.27925, + "layout_step_p50_ms": null + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "high-arity-100", + "mode": "frozen+hulls", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 271.90625, + "frame_p50_ms": 16.717312, + "frame_p95_ms": 33.508365250000004, + "frame_p95_run_min_ms": 25.29628335, + "frame_p95_run_max_ms": 33.732054500000004, + "average_fps": 49.447122452522876, + "update_commit_ms": 0.5804579999999999, + "update_next_frame_ms": 42.724375, + "layout_step_p50_ms": null + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "high-arity-1000", + "mode": "frozen", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 411.09375, + "frame_p50_ms": 12.6834165, + "frame_p95_ms": 17.106894099999998, + "frame_p95_run_min_ms": 11.7243215, + "frame_p95_run_max_ms": 20.55327295, + "average_fps": 71.99500139281292, + "update_commit_ms": 6.159333, + "update_next_frame_ms": 18.402124999999998, + "layout_step_p50_ms": null + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "high-arity-1000", + "mode": "frozen+hulls", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 427.140625, + "frame_p50_ms": 33.347542000000004, + "frame_p95_ms": 41.77612115, + "frame_p95_run_min_ms": 34.796988150000004, + "frame_p95_run_max_ms": 53.862216, + "average_fps": 29.77707208874065, + "update_commit_ms": 8.971290999999999, + "update_next_frame_ms": 56.79325, + "layout_step_p50_ms": null + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "large", + "mode": "frozen", + "attempts": 5, + "successful_runs": 4, + "statuses": [ + "ok", + "watchdog_timeout" + ], + "peak_rss_mib_median": 700.1484375, + "frame_p50_ms": 61.367645499999995, + "frame_p95_ms": 68.10254567499999, + "frame_p95_run_min_ms": 66.62374615, + "frame_p95_run_max_ms": 82.65074374999999, + "average_fps": 16.345351441727637, + "update_commit_ms": 5.769916499999999, + "update_next_frame_ms": 62.76875, + "layout_step_p50_ms": null + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "large", + "mode": "frozen+hulls", + "attempts": 5, + "successful_runs": 4, + "statuses": [ + "ok", + "watchdog_timeout" + ], + "peak_rss_mib_median": 1006.703125, + "frame_p50_ms": 270.729198, + "frame_p95_ms": 375.32187310000006, + "frame_p95_run_min_ms": 347.00522440000003, + "frame_p95_run_max_ms": 455.02350145000014, + "average_fps": 3.5191572363889527, + "update_commit_ms": 6.9078955, + "update_next_frame_ms": 302.924271, + "layout_step_p50_ms": null + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "mathlib", + "mode": "frozen", + "attempts": 5, + "successful_runs": 0, + "statuses": [ + "watchdog_timeout" + ], + "peak_rss_mib_median": null, + "frame_p50_ms": null, + "frame_p95_ms": null, + "frame_p95_run_min_ms": null, + "frame_p95_run_max_ms": null, + "average_fps": null, + "update_commit_ms": null, + "update_next_frame_ms": null, + "layout_step_p50_ms": null + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "mathlib", + "mode": "frozen+hulls", + "attempts": 5, + "successful_runs": 0, + "statuses": [ + "watchdog_timeout" + ], + "peak_rss_mib_median": null, + "frame_p50_ms": null, + "frame_p95_ms": null, + "frame_p95_run_min_ms": null, + "frame_p95_run_max_ms": null, + "average_fps": null, + "update_commit_ms": null, + "update_next_frame_ms": null, + "layout_step_p50_ms": null + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "moderate", + "mode": "frozen", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 281.796875, + "frame_p50_ms": 8.312187999999999, + "frame_p95_ms": 9.6822396, + "frame_p95_run_min_ms": 8.726020799999999, + "frame_p95_run_max_ms": 17.1010667, + "average_fps": 117.14653303371168, + "update_commit_ms": 0.9233330000000001, + "update_next_frame_ms": 8.483708, + "layout_step_p50_ms": null + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "moderate", + "mode": "frozen+hulls", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 358.375, + "frame_p50_ms": 49.975916500000004, + "frame_p95_ms": 62.078327050000034, + "frame_p95_run_min_ms": 51.964571450000015, + "frame_p95_run_max_ms": 74.2269899, + "average_fps": 19.566884191655177, + "update_commit_ms": 0.7896249999999999, + "update_next_frame_ms": 58.941583, + "layout_step_p50_ms": null + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "moderate", + "mode": "live", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 287.140625, + "frame_p50_ms": 16.656437500000003, + "frame_p95_ms": 17.009687200000002, + "frame_p95_run_min_ms": 9.089753500000002, + "frame_p95_run_max_ms": 17.233082699999997, + "average_fps": 59.998014065734424, + "update_commit_ms": 0.907625, + "update_next_frame_ms": 16.757542, + "layout_step_p50_ms": 3.4929794999999997 + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "moderate", + "mode": "live+hulls", + "attempts": 5, + "successful_runs": 3, + "statuses": [ + "ok", + "watchdog_timeout" + ], + "peak_rss_mib_median": 384.796875, + "frame_p50_ms": 21.9059585, + "frame_p95_ms": 37.07194615, + "frame_p95_run_min_ms": 34.935810749999995, + "frame_p95_run_max_ms": 43.48701425, + "average_fps": 41.350638667505976, + "update_commit_ms": 0.874833, + "update_next_frame_ms": 25.381958, + "layout_step_p50_ms": 3.4389995 + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "scientific", + "mode": "frozen", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 237.25, + "frame_p50_ms": 8.413896, + "frame_p95_ms": 9.082428850000001, + "frame_p95_run_min_ms": 8.6574399, + "frame_p95_run_max_ms": 17.76361605, + "average_fps": 119.37348022637987, + "update_commit_ms": 0.358834, + "update_next_frame_ms": 8.502666000000001, + "layout_step_p50_ms": null + }, + { + "track": "native", + "tool": "Hyper", + "dataset": "scientific", + "mode": "frozen+hulls", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 257.5, + "frame_p50_ms": 8.332979, + "frame_p95_ms": 9.15702465, + "frame_p95_run_min_ms": 8.698001750000001, + "frame_p95_run_max_ms": 17.2186851, + "average_fps": 119.59758207431508, + "update_commit_ms": 0.38875, + "update_next_frame_ms": 8.541542, + "layout_step_p50_ms": null + }, + { + "track": "native-interactive", + "tool": "Hyper", + "dataset": "mathlib", + "mode": "frozen+hulls", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 880.296875, + "frame_p50_ms": 162.184188, + "frame_p95_ms": 219.9086247, + "frame_p95_run_min_ms": 191.63183125, + "frame_p95_run_max_ms": 257.63149730000004, + "average_fps": 5.932056024739585, + "update_commit_ms": 10.35475, + "update_next_frame_ms": 195.358458, + "layout_step_p50_ms": null + }, + { + "track": "native-interactive", + "tool": "Hyper", + "dataset": "mathlib", + "mode": "live+hulls", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 935.109375, + "frame_p50_ms": 97.1940415, + "frame_p95_ms": 171.71294405, + "frame_p95_run_min_ms": 166.69555655, + "frame_p95_run_max_ms": 176.77365565, + "average_fps": 9.081420199218897, + "update_commit_ms": 9.243917000000001, + "update_next_frame_ms": 97.096375, + "layout_step_p50_ms": 14.230917 + }, + { + "track": "native-interactive", + "tool": "Hyper", + "dataset": "moderate", + "mode": "frozen+hulls", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 298.53125, + "frame_p50_ms": 50.037853999999996, + "frame_p95_ms": 64.92663155000001, + "frame_p95_run_min_ms": 53.9034247, + "frame_p95_run_max_ms": 95.84127980000001, + "average_fps": 19.04790043287936, + "update_commit_ms": 0.36612500000000003, + "update_next_frame_ms": 50.895165999999996, + "layout_step_p50_ms": null + }, + { + "track": "native-interactive", + "tool": "Hyper", + "dataset": "moderate", + "mode": "live+hulls", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 321.875, + "frame_p50_ms": 18.000854, + "frame_p95_ms": 30.836029449999998, + "frame_p95_run_min_ms": 30.097470799999996, + "frame_p95_run_max_ms": 37.94153095, + "average_fps": 50.06377464047368, + "update_commit_ms": 0.334833, + "update_next_frame_ms": 20.603042, + "layout_step_p50_ms": 0.8554785 + }, + { + "track": "native-star", + "tool": "Hyper", + "dataset": "mathlib", + "mode": "frozen", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 674.3125, + "frame_p50_ms": 18.1558745, + "frame_p95_ms": 25.95170445, + "frame_p95_run_min_ms": 25.77922710000001, + "frame_p95_run_max_ms": 33.882346150000004, + "average_fps": 47.75056283313845, + "update_commit_ms": 9.548667, + "update_next_frame_ms": 31.525791999999996, + "layout_step_p50_ms": null + }, + { + "track": "native-star", + "tool": "Hyper", + "dataset": "mathlib", + "mode": "frozen+hulls", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 746.03125, + "frame_p50_ms": 187.351604, + "frame_p95_ms": 216.6013354, + "frame_p95_run_min_ms": 174.73731285000002, + "frame_p95_run_max_ms": 253.4044726, + "average_fps": 5.3932981919356555, + "update_commit_ms": 12.262, + "update_next_frame_ms": 205.938875, + "layout_step_p50_ms": null + }, + { + "track": "native-star", + "tool": "Hyper", + "dataset": "mathlib", + "mode": "live", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "peak_rss_mib_median": 666.34375, + "frame_p50_ms": 21.7779375, + "frame_p95_ms": 24.799570850000002, + "frame_p95_run_min_ms": 22.542967, + "frame_p95_run_max_ms": 27.75147535, + "average_fps": 45.09819822161659, + "update_commit_ms": 7.620542, + "update_next_frame_ms": 42.012625, + "layout_step_p50_ms": 14.1977295 + }, + { + "track": "native-star", + "tool": "Hyper", + "dataset": "mathlib", + "mode": "live+hulls", + "attempts": 5, + "successful_runs": 4, + "statuses": [ + "ok", + "watchdog_timeout" + ], + "peak_rss_mib_median": 1057.625, + "frame_p50_ms": 100.75751050000001, + "frame_p95_ms": 179.874916675, + "frame_p95_run_min_ms": 173.5838253, + "frame_p95_run_max_ms": 220.50807085000002, + "average_fps": 8.846815336427651, + "update_commit_ms": 9.592875, + "update_next_frame_ms": 101.556396, + "layout_step_p50_ms": 14.70263525 + }, + { + "track": "scientific", + "tool": "hypergraphx", + "dataset": "correctness", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 67.546875, + "tool_import_ms": 7.113292, + "file_read_ms": 0.212541, + "json_parse_ms": 0.071667, + "canonical_validate_ms": 0.010916, + "adapter_construct_ms": 0.056875, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypergraphx", + "dataset": "correctness", + "mode": "plot", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 125.40625, + "tool_import_ms": 7.001334, + "file_read_ms": 0.078417, + "json_parse_ms": 0.063292, + "canonical_validate_ms": 0.010917, + "adapter_construct_ms": 0.05575, + "artist_setup_ms": 1149.301958, + "agg_raster_draw_ms": 30.262417, + "pixel_buffer_copy_ms": 1.558458, + "png_export_ms": 20.909042 + }, + { + "track": "scientific", + "tool": "hypernetx", + "dataset": "correctness", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "unsupported_records" + ], + "exact_model_records": false, + "peak_rss_mib_median": 198.125, + "tool_import_ms": 755.6835, + "file_read_ms": 0.03725, + "json_parse_ms": 0.031792, + "canonical_validate_ms": 0.008458, + "adapter_construct_ms": 5.145541, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypernetx", + "dataset": "correctness", + "mode": "plot", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "unsupported_records" + ], + "exact_model_records": false, + "peak_rss_mib_median": 222.375, + "tool_import_ms": 760.475375, + "file_read_ms": 0.048417, + "json_parse_ms": 0.032041, + "canonical_validate_ms": 0.008584, + "adapter_construct_ms": 4.998208, + "artist_setup_ms": 6.026375, + "agg_raster_draw_ms": 5.47725, + "pixel_buffer_copy_ms": 1.73575, + "png_export_ms": 21.036917 + }, + { + "track": "scientific", + "tool": "xgi", + "dataset": "correctness", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 104.5625, + "tool_import_ms": 283.202042, + "file_read_ms": 0.047459, + "json_parse_ms": 0.034583, + "canonical_validate_ms": 0.009541, + "adapter_construct_ms": 0.058583, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "xgi", + "dataset": "correctness", + "mode": "plot", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 129.265625, + "tool_import_ms": 284.31775, + "file_read_ms": 0.051208, + "json_parse_ms": 0.0355, + "canonical_validate_ms": 0.00975, + "adapter_construct_ms": 0.06325, + "artist_setup_ms": 7.765333, + "agg_raster_draw_ms": 3.999584, + "pixel_buffer_copy_ms": 1.547667, + "png_export_ms": 18.327667 + }, + { + "track": "scientific", + "tool": "hypergraphx", + "dataset": "high-arity-10", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 69.34375, + "tool_import_ms": 6.339625, + "file_read_ms": 0.229625, + "json_parse_ms": 0.717166, + "canonical_validate_ms": 0.149667, + "adapter_construct_ms": 1.0235, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypernetx", + "dataset": "high-arity-10", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "unsupported_records" + ], + "exact_model_records": false, + "peak_rss_mib_median": 202.453125, + "tool_import_ms": 841.494375, + "file_read_ms": 0.27925, + "json_parse_ms": 0.656375, + "canonical_validate_ms": 0.148042, + "adapter_construct_ms": 32.927667, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "xgi", + "dataset": "high-arity-10", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 106.078125, + "tool_import_ms": 281.07, + "file_read_ms": 0.265875, + "json_parse_ms": 0.675667, + "canonical_validate_ms": 0.144458, + "adapter_construct_ms": 0.748625, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypergraphx", + "dataset": "high-arity-100", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 72.265625, + "tool_import_ms": 6.501292, + "file_read_ms": 0.559625, + "json_parse_ms": 1.144125, + "canonical_validate_ms": 0.882125, + "adapter_construct_ms": 6.682958, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypernetx", + "dataset": "high-arity-100", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 212.765625, + "tool_import_ms": 840.271958, + "file_read_ms": 0.098791, + "json_parse_ms": 1.05225, + "canonical_validate_ms": 0.875542, + "adapter_construct_ms": 193.156208, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "xgi", + "dataset": "high-arity-100", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 110.828125, + "tool_import_ms": 278.170625, + "file_read_ms": 0.439584, + "json_parse_ms": 1.118125, + "canonical_validate_ms": 0.876333, + "adapter_construct_ms": 2.603958, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypergraphx", + "dataset": "high-arity-1000", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "unsupported_records" + ], + "exact_model_records": false, + "peak_rss_mib_median": 89.765625, + "tool_import_ms": 7.142959, + "file_read_ms": 0.62225, + "json_parse_ms": 5.15575, + "canonical_validate_ms": 4.725667, + "adapter_construct_ms": 15.997, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypernetx", + "dataset": "high-arity-1000", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 342.265625, + "tool_import_ms": 900.420291, + "file_read_ms": 0.531291, + "json_parse_ms": 4.8025, + "canonical_validate_ms": 4.877208, + "adapter_construct_ms": 1827.21875, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "xgi", + "dataset": "high-arity-1000", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 142.65625, + "tool_import_ms": 271.909334, + "file_read_ms": 2.555083, + "json_parse_ms": 4.782375, + "canonical_validate_ms": 4.70875, + "adapter_construct_ms": 21.3205, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypergraphx", + "dataset": "large", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 104.4375, + "tool_import_ms": 7.07125, + "file_read_ms": 0.636084, + "json_parse_ms": 11.655541, + "canonical_validate_ms": 4.093667, + "adapter_construct_ms": 34.99625, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypernetx", + "dataset": "large", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "unsupported_records" + ], + "exact_model_records": false, + "peak_rss_mib_median": 295.484375, + "tool_import_ms": 860.484333, + "file_read_ms": 0.629625, + "json_parse_ms": 11.665208, + "canonical_validate_ms": 4.19175, + "adapter_construct_ms": 876.699333, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "xgi", + "dataset": "large", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 148.53125, + "tool_import_ms": 280.495541, + "file_read_ms": 0.648041, + "json_parse_ms": 11.503042, + "canonical_validate_ms": 4.224125, + "adapter_construct_ms": 53.333666, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypergraphx", + "dataset": "mathlib", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 237.46875, + "tool_import_ms": 6.886417, + "file_read_ms": 2.072791, + "json_parse_ms": 58.149625, + "canonical_validate_ms": 12.713375, + "adapter_construct_ms": 108.1725, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypernetx", + "dataset": "mathlib", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "unsupported_records" + ], + "exact_model_records": false, + "peak_rss_mib_median": 650.90625, + "tool_import_ms": 885.215458, + "file_read_ms": 8.097792, + "json_parse_ms": 62.972667, + "canonical_validate_ms": 12.971375, + "adapter_construct_ms": 1798.545625, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "xgi", + "dataset": "mathlib", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 280.421875, + "tool_import_ms": 274.656875, + "file_read_ms": 2.040375, + "json_parse_ms": 60.567708, + "canonical_validate_ms": 12.875291, + "adapter_construct_ms": 166.9665, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypergraphx", + "dataset": "moderate", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 72.5625, + "tool_import_ms": 9.13125, + "file_read_ms": 0.10275, + "json_parse_ms": 1.418208, + "canonical_validate_ms": 0.706542, + "adapter_construct_ms": 5.894708, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypergraphx", + "dataset": "moderate", + "mode": "plot", + "attempts": 5, + "successful_runs": 0, + "statuses": [ + "watchdog_timeout" + ], + "exact_model_records": false, + "peak_rss_mib_median": null, + "tool_import_ms": null, + "file_read_ms": null, + "json_parse_ms": null, + "canonical_validate_ms": null, + "adapter_construct_ms": null, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypernetx", + "dataset": "moderate", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 211.375, + "tool_import_ms": 757.944458, + "file_read_ms": 0.061709, + "json_parse_ms": 1.276042, + "canonical_validate_ms": 0.695084, + "adapter_construct_ms": 161.783542, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypernetx", + "dataset": "moderate", + "mode": "plot", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 242.421875, + "tool_import_ms": 756.206792, + "file_read_ms": 0.061292, + "json_parse_ms": 1.27025, + "canonical_validate_ms": 0.696541, + "adapter_construct_ms": 159.922167, + "artist_setup_ms": 2717.868041, + "agg_raster_draw_ms": 1089.548291, + "pixel_buffer_copy_ms": 1.588708, + "png_export_ms": 28.119 + }, + { + "track": "scientific", + "tool": "xgi", + "dataset": "moderate", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 110.0, + "tool_import_ms": 280.063417, + "file_read_ms": 0.269875, + "json_parse_ms": 1.34325, + "canonical_validate_ms": 0.703334, + "adapter_construct_ms": 4.386834, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "xgi", + "dataset": "moderate", + "mode": "plot", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 144.0, + "tool_import_ms": 275.288375, + "file_read_ms": 0.066292, + "json_parse_ms": 1.369208, + "canonical_validate_ms": 0.799083, + "adapter_construct_ms": 4.709417, + "artist_setup_ms": 214.565458, + "agg_raster_draw_ms": 452.824667, + "pixel_buffer_copy_ms": 1.568875, + "png_export_ms": 37.701 + }, + { + "track": "scientific", + "tool": "hypergraphx", + "dataset": "scientific", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "unsupported_records" + ], + "exact_model_records": false, + "peak_rss_mib_median": 69.296875, + "tool_import_ms": 6.417375, + "file_read_ms": 0.084, + "json_parse_ms": 0.557875, + "canonical_validate_ms": 0.161542, + "adapter_construct_ms": 1.101458, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypergraphx", + "dataset": "scientific", + "mode": "plot", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "unsupported_records" + ], + "exact_model_records": false, + "peak_rss_mib_median": 181.5625, + "tool_import_ms": 6.540083, + "file_read_ms": 0.085834, + "json_parse_ms": 0.518292, + "canonical_validate_ms": 0.1615, + "adapter_construct_ms": 1.09425, + "artist_setup_ms": 13764.899333, + "agg_raster_draw_ms": 396.785417, + "pixel_buffer_copy_ms": 1.708541, + "png_export_ms": 40.778666 + }, + { + "track": "scientific", + "tool": "hypernetx", + "dataset": "scientific", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 202.125, + "tool_import_ms": 753.316166, + "file_read_ms": 0.049, + "json_parse_ms": 0.458125, + "canonical_validate_ms": 0.158541, + "adapter_construct_ms": 27.517958, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "hypernetx", + "dataset": "scientific", + "mode": "plot", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 228.1875, + "tool_import_ms": 755.231709, + "file_read_ms": 0.049292, + "json_parse_ms": 0.458959, + "canonical_validate_ms": 0.159917, + "adapter_construct_ms": 27.489833, + "artist_setup_ms": 500.677833, + "agg_raster_draw_ms": 154.867125, + "pixel_buffer_copy_ms": 1.595667, + "png_export_ms": 35.96675 + }, + { + "track": "scientific", + "tool": "xgi", + "dataset": "scientific", + "mode": "construct", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 105.984375, + "tool_import_ms": 274.38325, + "file_read_ms": 0.045958, + "json_parse_ms": 0.492708, + "canonical_validate_ms": 0.154791, + "adapter_construct_ms": 1.325541, + "artist_setup_ms": null, + "agg_raster_draw_ms": null, + "pixel_buffer_copy_ms": null, + "png_export_ms": null + }, + { + "track": "scientific", + "tool": "xgi", + "dataset": "scientific", + "mode": "plot", + "attempts": 5, + "successful_runs": 5, + "statuses": [ + "ok" + ], + "exact_model_records": true, + "peak_rss_mib_median": 132.25, + "tool_import_ms": 274.083, + "file_read_ms": 0.27325, + "json_parse_ms": 0.518833, + "canonical_validate_ms": 0.159708, + "adapter_construct_ms": 1.372583, + "artist_setup_ms": 23.956792, + "agg_raster_draw_ms": 28.11425, + "pixel_buffer_copy_ms": 1.591291, + "png_export_ms": 33.236709 + } +] diff --git a/docs/benchmarks/ecosystem-2026-10-03/verification.json b/docs/benchmarks/ecosystem-2026-10-03/verification.json new file mode 100644 index 0000000..7b2127b --- /dev/null +++ b/docs/benchmarks/ecosystem-2026-10-03/verification.json @@ -0,0 +1,40 @@ +{ + "rust_library_tests": { + "core": 61, + "bevy_benchmark_feature": 52, + "failures": 0 + }, + "rust_hif_integration_tests": { + "passed": 9, + "failures": 0 + }, + "rust_core_doctests": { + "passed": 1, + "failures": 0 + }, + "python_harness_tests": { + "passed": 8, + "failures": 0 + }, + "scientific_independent_checks": { + "cases": 24, + "input_unmutated": true, + "unexpected_wrong_memberships_or_metadata": 0, + "known_native_identity_loss_retained": true + }, + "rustfmt": "passed", + "node_syntax_checks": [ + "browser/client.js", + "browser/run.mjs" + ], + "commands": [ + "cargo test --release --lib -p hyper-viz -p hyper-viz-bevy --features hyper-viz-bevy/benchmark --locked -j6", + "cargo test --release -p hyper-viz --locked -j6", + "target/benchmark/science/venv/bin/python -m unittest discover -s scripts/benchmarks -p test_*.py", + "target/benchmark/science/venv/bin/python scripts/benchmarks/verify_scientific.py", + "cargo fmt --all --check" + ], + "note": "Checks were run after timed workloads. Python tests rerun after final metadata/summary changes. Tests verify the harness and model behavior; measured timeouts remain failures.", + "archived_only_reaggregation": "summary.json,summary.csv,README.md match byte-for-byte", + "dataset_hashes_verified": 40 +} diff --git a/docs/ecosystem-comparison.md b/docs/ecosystem-comparison.md new file mode 100644 index 0000000..fc8952e --- /dev/null +++ b/docs/ecosystem-comparison.md @@ -0,0 +1,172 @@ +# Hyper ecosystem comparison and roadmap + +Follow-up: the [October 3 measured comparison](benchmarks/ecosystem-2026-10-03/README.md) runs a documented subset of the proposed benchmark. This research brief retains its original research-date observations; the HIF implementation and measured follow-up supersede the corresponding future-work descriptions. + +Research date: October 2, 2026, America/Lima. Project baseline: commit `024f51716406f9e33244638e313ea761e2b13ed8`. Purpose: positioning and roadmap decisions. + +**Recommendation:** position Hyper as an embeddable Rust toolkit for exploring group relationships in native 3D, with explicit hyperedge membership and integration into a host application. Prioritize interoperability, useful inspection, and responsiveness before expanding the visual effects. This is a positioning hypothesis supported by the comparison below; adoption and user preference still need validation. + +The ecosystem already offers hypergraph analysis, interactive hypergraph viewers, 3D graph components, and research on simplifying dense diagrams. Hyper's opportunity lies in the combination of its Rust API, renderer-independent scene, native viewer, and host integration. Neither hyperedge hulls nor 3D force layouts alone establish differentiation. + +## Scope and evidence + +This review covers 14 alternatives, plus Hyper: dedicated hypergraph libraries and viewers, and general graph tools users could adopt through an incidence projection. An incidence projection represents each hyperedge as a separate node connected to its members. It can preserve group identity; converting every group into pairwise connections alone can obscure it. The distinction has longstanding research support. [Ouvrard et al., 2017](https://arxiv.org/abs/1707.00115) + +Capabilities come from project documentation, repositories, research papers, and a local source audit. Community findings come from public issue reports, maintainer discussions, and Reddit accounts. These are a purposeful sample, not a representative survey. Competitor applications were not installed or benchmarked in this review. “Not documented” means the reviewed sources did not establish the capability, rather than proving its absence. Recommendations and competitive implications are explicitly judgments. + +Existing Hyper CSV measurements were independently recalculated, but the viewer was not rerun. Published performance numbers use different workloads and machines; they do not support a speed ranking. The [benchmark protocol](benchmark-protocol.md) defines a fair follow-up experiment. + +Follow-up: the [October 3 benchmark run](benchmarks/ecosystem-2026-10-03/README.md) executes the separate scientific, browser and native tracks. Its measured results supersede the performance hypotheses in this research brief; platform and adapter limitations remain explicit. + +## What Hyper currently provides + +| Area | Observed implementation | Implication for positioning | +|---|---|---| +| Integration | Bevy-free `hyper-viz`; optional Bevy viewer and plugin; JSON and builder APIs | Suitable for embedding in Rust applications with custom renderers | +| Hypergraph model | Stable vertex and hyperedge IDs; unordered memberships; optional weights and opaque attributes in the wire model | Explicit group identity, but no first-class directed incidences or temporal model | +| Layout and drawing | CPU Barnes–Hut 3D layout; bipartite, clique, and centroid projections; lines and member hulls | Multiple ways to interpret the same memberships; projection choice strongly changes workload | +| Exploration | Vertex, segment, and hull picking; lasso; local search; neighborhood isolation; selected member lists | Useful exploration foundation; task completion needs evaluation | +| Host search | Optional `SearchProvider` returns paginated rows and a result scene from outside the current view | Can explore a bounded slice of a larger host dataset; this is an integration hook, not a bundled database | +| Live updates | File watch and scene channel; positions retained by stable ID; selection and focus remapped | Existing continuity mechanisms should be documented and assessed before adding replacements | +| Inspection | Scene and inspector expose ID, kind, label, status, and members; arbitrary `attrs` and weights are not carried into scene records | A host's evidence, dates, provenance, and quantitative attributes are unavailable to the stock inspector | +| Distribution | Git/path dependencies; crates have `publish = false`; no packaged browser product | Current evaluation requires a development environment | + +Sources: [README](../README.md), [API](API.md), [schema](../crates/hyper-viz/src/schema.rs), [projection](../crates/hyper-viz/src/project.rs), [scene](../crates/hyper-viz/src/scene.rs), [inspection](../crates/hyper-viz-bevy/src/inspect.rs), [host search](../crates/hyper-viz-bevy/src/search.rs), [reload](../crates/hyper-viz-bevy/src/graph.rs), and [selection remapping](../crates/hyper-viz-bevy/src/interaction.rs). + +Two qualifications matter. The hull algorithm caps input at 24 sampled member positions. Consequently, a large rendered shell is an approximation, while the membership list remains authoritative. Convex enclosure can also include unrelated nodes spatially: geometry alone cannot certify membership. See [hull implementation](../crates/hyper-viz/src/hull.rs). Also, the README's statement that reload clears selection is inconsistent with the current remapping implementation. Correcting that documentation belongs in release preparation. + +## Dedicated hypergraph comparison + +“3D” distinguishes a freely navigable spatial viewer from a layered scientific plot. HIF is the community Hypergraph Interchange Format. + +| Project | Environment | Hyperedge representation | 3D support | Exploration and integration | Interchange and installation | Competitive implication for Hyper | +|---|---|---|---|---|---|---| +| **Hyper** | Rust library and native Bevy app | Explicit memberships; member hulls; incidence/clique/centroid projections | Native spatial viewer | Search, lasso, picking, focus; host scene updates and optional host search | `hypergraph.v1`; git/path dependencies | Candidate strength: embeddable native exploration; gaps: interoperability, metadata inspection, distribution | +| [HyperNetX](https://github.com/pnnl/HyperNetX) | Python scientific library | Explicit hypergraphs; Matplotlib visualization | Stock native 3D viewer not established | Analysis and notebook workflows; interactive widget is separate | HIF ecosystem; PyPI | Better treated as an analysis partner and data source than a library to replace | +| [HyperNetX Widget](https://github.com/pnnl/hypernetx-widget) | JavaScript and Python/Jupyter | Euler-style, bipartite, and dual views | 3D not documented | Pin/drag, brush, member expansion, tables, attribute coloring, supernodes | `hnxwidget`; documented notebook compatibility restrictions | Closest baseline for exploration features; demonstrates that selection and hull-like boundaries are existing capabilities | +| [XGI](https://github.com/xgi-org/xgi) | Python and Matplotlib | Hypergraphs, simplicial complexes, directed hypergraphs; polygons and hulls | Layered 3D plotting by edge order | Scientific analysis and configurable figures | HIF read/write and XGI-DATA; PyPI | Strong research workflow; Hyper can complement it with native interaction | +| [Hypergraphx](https://github.com/HGX-Team/hypergraphx/blob/main/README.md) | Python analysis library | Weighted, directed, temporal, multiplex and signed interactions | Native spatial viewer not established in reviewed docs | Measures, filtering, communities, dynamics and representations | HIF read/write; PyPI; dataset catalog | Much broader analysis/model semantics; avoid recreating that breadth | +| [SimpleHypergraphs.jl](https://github.com/pszufe/SimpleHypergraphs.jl) | Julia | Explicit hypergraphs with several plotting options | Comparable spatial viewer not established | Julia scientific workflow; optional HyperNetX plotting bridge | HIF tutorials; Julia package manager | Potential interoperability partner; some plotting paths require Python dependencies | +| [HyperGodot](https://github.com/ElsevierSoftwareX/SOFTX-D-25-00370) | Godot/GDScript desktop viewer | 2D convex hulls, weighted and grouped edges | Reviewed implementation uses a 2D camera | Layout choices, node dragging, incident-edge filtering, group comparison | Tagged text format; source and Windows-tested export | Direct competitor for interactive hull exploration; grouped/temporal comparison already exists elsewhere | +| [HGPolyVis](https://github.com/peterdanieloliver/HGPolyVis/blob/main/README.md) | Windows desktop research application | Polygon representation; simplification and layout methods | Comparable 3D viewer not documented | Research focuses on simplifying dense structures and their duals | CSV and `.er` layout files; Windows 11 executable | Serious baseline for readability and multiscale exploration; narrower platform availability | +| [PAOHVis](https://www.aviz.fr/Research/paohvis) | Browser research tool | Horizontal vertex rows and vertical hyperedge connections across time | 2D temporal view | Ordered temporal exploration; published case studies | CSV with time slots; web demo and local web server | Better reference for temporal interpretation than a force-layout animation | + +Additional source details: XGI's [visualization tutorial](https://xgi.readthedocs.io/en/stable/api/tutorials/focus_5.html) documents hull and multilayer drawing. HGX's [read/write API](https://hypergraphx.readthedocs.io/en/latest/api/hypergraphx.readwrite.html) documents HIF. The [HIF project](https://github.com/HIF-org/HIF-standard) specifies incidences, optional incidence weights and attributes, and head/tail direction. HyperGodot's [SoftwareX publication](https://www.sciencedirect.com/science/article/pii/S235271102500281X) identifies the code repository, GPLv3 license, and Windows-tested export; source execution uses Godot 4.3+. The publisher page was partially accessible through search; its full text was unavailable, so no quantitative findings from it are used here. + +## General graph substitutes + +These tools can display an incidence graph, but their stock node-link data models do not establish overlapping hyperedges as independently inspectable filled sets. An adapter or custom rendering work is needed. This is an architectural comparison, not a claim that such extensions are impossible. + +| Project | Environment and view | Established strengths | Work needed for the Hyper use case | Strategic implication | +|---|---|---|---|---| +| [3d-force-graph](https://github.com/vasturiano/3d-force-graph) | JavaScript, Three.js/WebGL, 3D | Custom objects, click/focus examples, node dragging, direction indicators, dynamic data | Incidence conversion; hyperedge surfaces and membership-specific interactions | Strong substitute for web distribution and generic 3D; CPU layout plus GPU rendering is not unique to Hyper | +| [Graphia](https://graphia.app/guide/section2/2_first_start_up.html) | Desktop, interactive 2D/3D | Attribute tables, search, layout export/reload; [clustering and filtering](https://graphia.app/guide/section3/2_transforms.html) | Hypergraph conversion and explicit group semantics | Strong baseline for native analytical usability; substantial inspection capabilities to learn from | +| [Gephi](https://gephi.org/) | Desktop network analysis; Gephi Lite offers a web alternative | Layout algorithms, attribute styling, filtering, metrics, publication exports | Incidence conversion or another hypergraph-specific extension | Competes for analyst attention through a complete workflow | +| [Cytoscape.js](https://js.cytoscape.org/index.html) | Browser interactive graph library | Data-driven styling, selection, graph algorithms and compound nodes | Incidence adapter and hyperedge interaction; compound hierarchy alone cannot represent arbitrary overlapping membership | Strong baseline for integrating visualization into a web application | +| [Sigma.js](https://www.sigmajs.org/) | Browser renderer with Graphology | Graph rendering, customization and examples for neighborhood search | Incidence adapter and custom group drawing; analytical workflow assembled by host | Useful 2D web baseline, particularly for accessible search-driven exploration | +| [egui_graphs](https://github.com/blitzarx1/egui_graphs) | Rust/egui widget; native and WASM | Pluggable layouts, labels, click/select/hover/drag, styling hooks | Incidence representation and custom hyperedge drawing; comparable stock 3D not documented | Rust integration and WASM are already available for ordinary graphs; README explicitly says development is inactive | + +## Community evidence and adoption + +### Public ecosystem signals + +The counts below were observed on public repository pages during this research. Crawled snapshots vary in freshness; these are approximate visibility signals, not synchronized API counts, active-user counts, or maintenance scores. + +| Project | Observed stars / forks | Stronger context than popularity alone | +|---|---:|---| +| [HyperNetX](https://github.com/pnnl/HyperNetX) | 717 / 117 | Named PNNL development team, tutorials, scientific use and HIF participation | +| [XGI](https://github.com/xgi-org/xgi) | 255 / 51 | Research package with datasets, mailing list, Zulip and contributions; public issues include requests posted in 2026 | +| [HyperNetX Widget](https://github.com/pnnl/hypernetx-widget) | 23 / 9 | Dedicated smaller viewer project; its compatibility must be assessed separately from HNX core | +| [SimpleHypergraphs.jl](https://github.com/pszufe/SimpleHypergraphs.jl) | 88 / 15 | Julia package, published research and explicit HIF workflow | +| [3d-force-graph](https://github.com/vasturiano/3d-force-graph) | About 6,400 / 1,000 | Broad examples and public troubleshooting; general graphs, not a hypergraph adoption measure | +| [egui_graphs](https://github.com/blitzarx1/egui_graphs) | 705 / 77 | Significant interest, but current README says the project is not in active development | + +Hyper's external adoption is **unmeasured here**. The local release checklist and unpublished crate settings establish packaging status, not repository visibility, user demand, or lack of users. Do not compare its unknown adoption with these counts as zero. + +### User problems found in public discussion + +| Evidence | Observed concern | Interpretation for Hyper | Confidence and limit | +|---|---|---|---| +| [HNX Widget issue 143](https://github.com/pnnl/HyperNetX/issues/143), January 2024; [current widget docs](https://hypernetx.readthedocs.io/en/latest/widget.html) | A reviewer could not render the widget in JupyterLab. Docs still specify Notebook 6.5.x and no JupyterLab support | A reliable “load my data and inspect it” path can matter more than additional drawing styles | Concrete historical report, now closed; current documentation supports the compatibility restriction, not a claim that every installation fails | +| [HNX issue 171](https://github.com/pnnl/HyperNetX/issues/171), October 2025 | Reporter describes import failure without internet because a schema was downloaded | Bundle schemas and test offline import when adding HIF | A reported failure; reproduction and present code status were not verified | +| [React Force Graph issue 483](https://github.com/vasturiano/react-force-graph/issues/483), December 2023 | About 10k nodes / 80k edges lag while moving the camera; filtering a neighborhood helps | Measure frozen-scene navigation independently of force calculation; extend bounded exploration | Specific workload and user observation, not a universal capacity limit | +| [Three Force Graph discussion 44](https://github.com/vasturiano/three-forcegraph/discussions/44), April–May 2026 | Label-heavy visualization causes performance concern; maintainer suggests prewarming/freezing layout and fewer geometries | Benchmark labels, rendering, layout, and picking separately; show detail on demand | Maintainer and reporter differ on perceived performance, reinforcing the need for controlled tests | +| [Reddit analyst thread](https://www.reddit.com/r/datascience/comments/17212yf), October 2023 | User wants clickable node/edge attributes and attribute-based color; later prefers Cytoscape desktop | Arbitrary attribute inspection and styling are plausible evaluation criteria | One self-report; not a current Gephi defect audit | +| [Reddit graph-view thread](https://www.reddit.com/r/Rag/comments/1qblw1k/best_knowledge_graph_graph_view/), January 2026 | Original poster says 10k nodes is sufficient; finding a particular node is the harder problem | Measure find-and-explain tasks alongside FPS; Hyper's existing search and focus are relevant | Anecdotal adjacent use case; disregard unsupported capacity claims in replies | +| [XGI issue listing](https://github.com/xgi-org/xgi/issues), request 707, April 2026 | Listing requests heterogeneous multilayer hypergraphs and directed layer visualization | Model semantics deserve investigation after core exploration is reliable | Only issue title/listing was accessible; body and demand breadth were not verified | + +This evidence suggests demand for inspection, compatibility, and manageable views. It does **not** demonstrate that users prefer 3D, that a browser release will create adoption, or that Hyper outperforms the alternatives. + +### Communities to validate with + +Start with researchers already producing hypergraphs: [HNX discussions](https://github.com/pnnl/HyperNetX/discussions), [XGI's community and Zulip links](https://github.com/xgi-org/xgi), the [HIF maintainers](https://github.com/HIF-org/HIF-standard), and Julia users working through SimpleHypergraphs. For visualization methods, use PAOHVis and HGPolyVis publications and associated research groups as technical references. Rust/egui and Bevy users are a second audience for embedding needs; their integration interest would not itself validate hypergraph analytical value. + +Suggested validation, not conducted: recruit 5–8 people across scientific analysis and Rust host development. Have each bring an actual dataset and perform the same membership, search, overlap, and evidence-inspection tasks. Ask which existing tool they use and where they would switch. No community messages were sent in this research. + +## Performance evidence + +### Existing Hyper measurements + +The checked-in Mathlib workload has 9,384 vertices, 37,356 import pairs, 7,550 dependency groups, **44,906 total hyperedge records**, and 118,066 memberships. The distinction avoids comparing “groups” in one tool with all edges in another. Its largest group has 8,532 members. + +Hardware: Apple M5 Pro, 24 GB RAM, Metal, release build, 1920×1080, star-centroid projection. Each recorded run contains 300 measured frames, split into 150 without hulls and 150 with hulls. Figures below reproduce the repository's rounded-rank percentile calculation from raw CSV samples. + +| Layout | Drawing | Median frame | p95 frame | 1000 / median frame time | +|---|---|---:|---:|---:| +| Frozen | Nodes + all import lines | 28.985 ms | 33.778 ms | 34.50 FPS | +| Frozen | Plus dependency-group hull fills | 41.042 ms | 52.086 ms | 24.37 FPS | +| One force step per frame | Nodes + all import lines | 24.906 ms | 31.751 ms | 40.15 FPS | +| One force step per frame | Plus dependency-group hull fills | 94.452 ms | 165.965 ms | 10.59 FPS | + +Sources: [measurement notes](mathlib-demo.md), [manifest](media/mathlib-manifest.json), [frozen CSV](benchmarks/mathlib-frozen.csv), [moving CSV](benchmarks/mathlib-live.csv), [report calculation](../crates/hyper-viz-bevy/src/showcase.rs). + +These measurements were recorded on demo changes based on `9ebe6d6`, before newer main changes were integrated. **They are historical measurements, not verification of current HEAD.** Frame intervals include update/render/presentation scheduling. The scripted tour disables picking and the ordinary interactive HUD, the two phases use different camera views, and there is one run per mode. The faster live no-hull median does not establish a simulation speedup. The MP4's encoding rate is independent of live throughput. Current hull code already caches geometry and periodically rebuilds topology; proposals must account for that implementation. + +### Published external evidence + +| Tool or method | Available evidence | What it supports | What remains unmeasured | +|---|---|---|---| +| Cytoscape.js | January 2025 developer tests on M1 MacBook Pro/Chrome: ~1,200 nodes / 16k edges, ~20 canvas FPS vs >100 WebGL FPS; ~3,200 nodes / 68k edges, ~3 vs ~10 FPS | Renderer choice and workload strongly affect interaction | Same data/hardware/styles as Hyper; present release behavior | +| 3d-force-graph family | Public reports about rendering and label overhead | Useful hypotheses for benchmark design | A controlled equivalent hypergraph comparison | +| HGPolyVis research | Simplification/layout work and structure preservation evaluation | Scalability must include interpretability at reduced detail | Equivalent native 3D frame-time measurements | +| Graphia | Product documentation claims visualization of millions of data points and relationships | A reason to include it as a scale-oriented substitute | A verified FPS envelope under a matched hypergraph workload | +| HNX, XGI, HGX, SimpleHypergraphs, HyperGodot, PAOHVis | Documented features, examples, and/or research use | Credible functional comparison candidates | Comparable end-to-end performance numbers in this review | + +Sources: [Cytoscape.js developer measurements](https://blog.js.cytoscape.org/2025/01/13/webgl-preview/), [rendering discussion](https://github.com/vasturiano/three-forcegraph/discussions/44), [Scalable Hypergraph Visualization](https://arxiv.org/abs/2308.05043), [Structure-Aware Simplification](https://arxiv.org/abs/2407.19621), [Graphia product claims](https://graphia.app/). These rows deliberately retain provenance and limitations instead of presenting an unsupported leaderboard. + +## Positioning and roadmap priorities + +### Recommended position + +Proposed description: **“An embeddable Rust hypergraph explorer for native 3D applications, with explicit group membership, search, and live host integration.”** + +The best initial audience hypothesis is developers building tools that already own hypergraph data and need a view into it. Researchers using Python or Julia become reachable through adapters. The Mathlib atlas is useful evidence of a real dataset, but import groups are a modeling choice and direction is not currently visible. It should not imply theorem dependencies or directed reasoning support. + +### Prioritized roadmap + +These priorities are judgments based on observed gaps and the evidence above. Gates are proposed acceptance criteria, not measured results or delivery promises. + +| Priority | Proposed work | Evidence and rationale | Suggested acceptance gate | +|---|---|---|---| +| P0 | Carry arbitrary attributes and weights into inspection; add attribute search/filter and selected-subgraph export | Wire model already has these fields; stock scene drops them; attribute inspection appears in community requests and substitutes | User can find a record by an attribute, inspect its evidence and exact members, and export the selected records without losing metadata | +| P0 | Add a scoped HIF adapter and reference Python/Julia export examples | Existing HIF adoption supplies datasets and workflow compatibility | Round-trip a documented supported subset, preserve isolated vertices and edge IDs, report unsupported incidence direction/weights explicitly, operate offline | +| P0 | Rebenchmark current HEAD and profile live hull updates | Historical live hull p95 is ~166 ms; current caching means optimizations need fresh profiles | Publish repeated runs with interaction enabled, CPU/GPU stages separated, settings and commit pinned; meet an agreed responsiveness target | +| P1 | Improve membership legibility and bounded views | Approximate hulls are not exact membership evidence; search/focus and host result scenes already exist | Selected-group member lists and highlight agree; large groups and approximation are visible; preserve context while moving between result slices | +| P1 | Make evaluation easy and docs accurate | Git/path-only installation; README disagrees with current reload behavior; widget community reports show setup friction | A new user loads a sample and their own supported file with documented steps; distribute native binaries if maintainers authorize release | +| P1 | Add a complementary 2D or incidence-focused mode | Existing alternatives and simplification research offer readable non-spatial views | Evaluate against 3D on identical membership and overlap tasks before broad implementation | +| P2 | Investigate directed incidences, temporal comparison and richer semantic models | HGX, HIF, PAOHVis and the XGI request show these needs are real but distinct | Choose a user segment and define meaning for head/tail, time, and incidence properties before changing schema | +| Conditional | Browser or notebook delivery | Most scientific users already work outside Rust; web substitutes are readily embedded | Build a small feasibility prototype only after pilots show distribution is the adoption blocker; evaluate GPU, picking, build size and host search behavior | + +For HIF, recognize the semantic mismatch: it supports properties and direction on individual incidences, while Hyper stores only member IDs. A conversion that silently flattens head/tail or incidence attributes would misrepresent data. Also, `from_json_str` uses default-empty fields and has no HIF detection: feeding raw HIF into the existing loader could deserialize to an empty Hyper graph. Input-format detection and clear errors should be part of the adapter design. This is a source-based inference from the [loader](../crates/hyper-viz/src/io/mod.rs) and [schema](../crates/hyper-viz/src/schema.rs), not a reproduced test result. + +### Competitive risks and choices + +The most plausible threat is an established scientific library improving interactive web exploration while retaining its datasets and user workflows. A second threat is a host team adapting a mature ordinary-graph component because installation and customization are easier. Hyper needs to win a particular workflow, not the broadest feature count. + +Keep its domain-agnostic library boundary. Integrate metrics calculated elsewhere instead of adding a large analysis engine. Defer more visual effects until task studies show better interpretation. Avoid “first hypergraph viewer,” “only Rust/3D option,” “exact set boundaries,” and performance superiority claims: the reviewed evidence does not establish them. An early HG-DB [fork](https://github.com/mahsaabdolahnezhad/hypergraph-visualization) also advertises a Rust normalization core with layered browser 3D, although its implementation and adoption were not independently verified; this is a watch item rather than a mature benchmark baseline. + +## Research gaps and refresh triggers + +No cross-tool benchmark, user interview, download audit, or comprehensive contributor/issue-response analysis was performed. Repository counts and open/closed statuses may lag current state. Some sources were only accessible as issue listings or publisher search excerpts; those limitations are attached to their claims. “Current ecosystem” here means the sources available on the research date, not an exhaustive inventory. + +Refresh this analysis when Hyper changes distribution or schema, HIF evolves, viewer compatibility changes, or a pilot user evaluates another tool. For roadmap decisions, revisit the same task-oriented benchmark after a material performance or exploration change. The foundational [2021 visualization survey](https://arxiv.org/abs/2107.13936) is useful taxonomy and evaluation context, but should not be treated as a complete 2026 market survey. diff --git a/docs/superpowers/plans/2026-10-02-hif-python.md b/docs/superpowers/plans/2026-10-02-hif-python.md new file mode 100644 index 0000000..d3513ce --- /dev/null +++ b/docs/superpowers/plans/2026-10-02-hif-python.md @@ -0,0 +1,19 @@ +# HIF and Python implementation record + +Approved design: Rust-owned HIF validation and conversion; PyO3 bindings with generated stubs; separate native viewer process. Preserve full documents; reject unsupported viewer semantics. No publication. + +1. Add integration tests for faithful JSON round trips, mixed integer/string identities, isolated nodes/empty edges, rich incidence preservation, duplicate rejection, weight overflow, native export, and format detection. Run `cargo test -p hyper-viz --test hif` red, implement the adapter, rerun green. +2. Vendor schema at HIF-org/HIF-standard b691a3d2ec32100c0229ebe1151e9afad015c356 and upstream license. Use a compiled offline validator; typed IDs and opaque JSON attributes. Implement `parse_hif`, `load_hif`, `serialize_hif`, `save_hif`, and conversions. +3. Add `InputFormat` and explicit loader APIs. Route CLI and file watcher through identical selection; retain native/legacy compatibility. Validate with core and Bevy tests. +4. Add Python contract tests before bindings/launcher implementation. Build the Rust extension with maturin, generate stubs, run Python tests with pinned XGI; check process ownership and cleanup using real helper processes. +5. Add platform wheel CI, clean-install tests, stub drift checks, and Rust/Python/Julia examples. Run fmt/clippy/tests, inspect final diff and docs; keep research files intact. + +Implementation decisions: Python CPython >=3.10, abi3; package `hyper_viz`, extension `_core`. Viewer IDs use `s:`/`i:` prefixes. HIF document export is faithful; conversion export is deliberately native IDs. Metadata display conventions apply only to native-to-HIF export, never inferred from scientific attributes. Native export rejects attribute collisions instead of losing values. + +## Verification and review + +Implemented all five steps. Rust 1.89: 61 core unit tests, 9 HIF integration tests, 52 Bevy unit tests, and 3 doc tests pass (125 total). Core and Bevy tests also run with Cargo offline. All-target Clippy (including CLI and bindings), formatting, and diff whitespace checks pass. Python: 10 installed-wheel tests including XGI 0.10.2 roundtrip and real process lifecycle; strict package and consumer typing pass. Generated stubs regenerate identically. + +macOS arm64 and Intel ABI3 wheels built; arm64 clean installation and extreme numeric-ID conversion verified. Wheel checks verify native extension uniqueness, generated stubs, typing marker, licenses, and absence of bytecode caches. Linux/Windows wheel builds and CPython 3.10/3.14 install checks are configured in CI but were not executed locally. Julia example API was checked against upstream source; Julia runtime was unavailable. + +Independent read-only review found exponent overflow and equivalent numeric-ID canonicalization bugs; BigInt normalization fixes both, with a red/green regression test. Existing Bevy tests caught invalid-file retry behavior regression; original retry semantics restored. No remaining material review findings. This verification record predates the PR commit. HIF/Python work was subsequently committed and opened as PR #8; the October 3 ecosystem measurements and reproducible harness extend that PR. No PyPI publication was performed. diff --git a/docs/superpowers/plans/2026-10-04-desktop-wheels.md b/docs/superpowers/plans/2026-10-04-desktop-wheels.md new file mode 100644 index 0000000..f7a94dd --- /dev/null +++ b/docs/superpowers/plans/2026-10-04-desktop-wheels.md @@ -0,0 +1,41 @@ +# Desktop wheels and notebook prototype follow-up + +User-authorized scope: ship the desktop companion wheels and document the next +anywidget prototype. Continue in the existing PR #8 worktree. + +## Delivery contract + +- `hypergraph-viz[viewer]` installs a version-matched `hypergraph-viz-viewer` wheel. +- Resolution: explicit executable, installed companion's RECORD, then PATH. + It must work without activating the Python environment. No runtime downloads. +- Keep core wheels and source archives free of Bevy; companion wheels contain + the existing native CLI, MIT/Apache licenses, and the bundled HIF schema license. +- Release 0.1.2 for both distributions on Linux x86_64, macOS arm64/Intel, and + Windows x86_64. PyPI's companion publisher requires user account setup. +- Validate executable identity, installation, real process startup, temporary + snapshot cleanup, and Linux software-rendered window creation. +- Document the notebook prototype; do not add a notebook API in this release. + +## Tasks + +1. Add failing executable-discovery tests using real temporary distribution + metadata. Implement installed-binary resolution, compatibility errors, + explicit override, and missing-install guidance. Run the full Python suite. +2. Package the root CLI through maturin's binary bindings. Version all packages + 0.1.2, pin the optional dependency, check wheel contents, and clean-install + the locally built companion. Preserve the independently built core sdist. +3. Extend the existing release matrix with desktop build/install/startup checks + and gate publishing on core and desktop jobs. Verify actual graphics startup + on local macOS and Linux software Vulkan/Xvfb in CI. Obtain an independent + final review and fix important findings before tagging. +4. Update installation and release docs and add a small-dataset anywidget plan. + Push PR #8, tag after verification and publisher setup, monitor all jobs, + then clean-install the extra from public PyPI and launch its binary. + +## Review focus + +- Missing executable despite an installed companion; damaged or ambiguous RECORD. +- Core/companion mismatch and override priority. +- Inactive virtual environment and paths containing spaces. +- Native platform tags, binary permissions, libraries, licenses, and wheel size. +- No claim that a CLI help check proves graphics startup or notebook support. diff --git a/examples/hif_interop.jl b/examples/hif_interop.jl new file mode 100644 index 0000000..5f390c5 --- /dev/null +++ b/examples/hif_interop.jl @@ -0,0 +1,14 @@ +# Run in a Julia environment with SimpleHypergraphs.jl installed. +# API: https://github.com/pszufe/SimpleHypergraphs.jl/blob/master/src/io_hif.jl +using SimpleHypergraphs + +h = Hypergraph{Int}(3, 2) # vertex 3 is isolated; edge 2 is empty +h[1, 1] = 1 +h[2, 1] = 1 +hg_save("julia.hif.json", h; format=HIF_Format(), pretty=true) +restored = hg_load("julia.hif.json"; format=HIF_Format(), T=Int) +@assert nhv(restored) == 3 +@assert nhe(restored) == 2 +println("Exported julia.hif.json; load with hyper_viz.HifDocument.load from Python.") +# This exporter includes incidence weights, even for weight 1. Hyper preserves +# them in full-document interchange and rejects viewer conversion explicitly. diff --git a/fixtures/notebook-small.hif.json b/fixtures/notebook-small.hif.json new file mode 100644 index 0000000..e1a8417 --- /dev/null +++ b/fixtures/notebook-small.hif.json @@ -0,0 +1,21 @@ +{ + "network-type": "undirected", + "metadata": {"name": "Notebook prototype", "source": {"kind": "synthetic", "seed": 20261004}}, + "nodes": [ + {"node": 0, "attrs": {"label": "Zero", "category": "A"}}, + {"node": 1, "attrs": {"label": "Integer one", "score": 1.25}}, + {"node": "1", "attrs": {"label": "String one", "category": "B"}}, + {"node": "b"}, {"node": "c"}, {"node": "d"}, + {"node": "isolated", "attrs": {"note": "No memberships"}} + ], + "edges": [ + {"edge": "pair"}, {"edge": "triple", "weight": 1.5}, + {"edge": "overlap", "attrs": {"evidence": ["synthetic", null]}}, + {"edge": "empty"} + ], + "incidences": [ + {"node": 0, "edge": "pair"}, {"node": 1, "edge": "pair"}, + {"node": 1, "edge": "triple"}, {"node": "1", "edge": "triple"}, {"node": "b", "edge": "triple"}, + {"node": "b", "edge": "overlap"}, {"node": "c", "edge": "overlap"}, {"node": "d", "edge": "overlap"} + ] +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..c411599 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,29 @@ +[build-system] +requires = ["maturin==1.9.6"] +build-backend = "maturin" + +[project] +name = "hypergraph-viz-viewer" +version = "0.1.2" +requires-python = ">=3.10" +description = "Prebuilt Hyper native desktop viewer for hypergraph-viz" +readme = "docs/DESKTOP_PACKAGE.md" +license = "MIT OR Apache-2.0" +classifiers = [ + "Development Status :: 3 - Alpha", + "Topic :: Scientific/Engineering :: Visualization", + "Programming Language :: Rust", +] + +[project.urls] +Homepage = "https://github.com/atomicstrata/hyper" +Documentation = "https://github.com/atomicstrata/hyper/blob/python-v0.1.2/docs/HIF.md" +Issues = "https://github.com/atomicstrata/hyper/issues" + +[tool.maturin] +bindings = "bin" +strip = true +include = [ + {path = "NOTICE", format = "wheel"}, + {path = "crates/hyper-viz/schemas/HIF-LICENSE", format = "wheel"}, +] diff --git a/scripts/benchmarks/README.md b/scripts/benchmarks/README.md new file mode 100644 index 0000000..8e54e50 --- /dev/null +++ b/scripts/benchmarks/README.md @@ -0,0 +1,135 @@ +# Ecosystem measurements + +Run each track **sequentially** on an otherwise idle machine. Preparation, compilation, +package installation and screenshots must not overlap timed workloads. These tools +produce measurements; they do not assign a universal performance ranking. + +The first recorded run, its comparison and limitations are in +[`docs/benchmarks/ecosystem-2026-10-03`](../../docs/benchmarks/ecosystem-2026-10-03). +Every adapter checks native identities/memberships before its results are usable. +Lossy model conversions are flagged separately from successful timing. + +## Prepare canonical inputs + +Requires Python 3.10+ for the orchestration scripts. The dataset generator itself +uses only the standard library. Synthetic inputs are seeded with `20261003` and CC0. + +Download Diseasome from the pinned CC-BY-4.0 record: + +```sh +mkdir -p target/benchmark/source +curl -L --fail https://zenodo.org/records/21909416/files/diseasome.json -o target/benchmark/source/diseasome.json +curl -L --fail https://api.github.com/repos/leanprover-community/mathlib4/tarball/d13f23b723b8a846827a245b89c10fc7d3f11612 -o target/benchmark/source/mathlib.tar.gz +mkdir -p target/benchmark/source/mathlib +tar -xzf target/benchmark/source/mathlib.tar.gz --strip-components=1 -C target/benchmark/source/mathlib +python3 scripts/mathlib_graph.py target/benchmark/source/mathlib target/benchmark/source/mathlib.json --release v4.34.1 --commit d13f23b723b8a846827a245b89c10fc7d3f11612 +``` + +Then generate the common fixtures: + +```sh +python3 scripts/benchmarks/datasets.py +python3 -m unittest discover -s scripts/benchmarks -p 'test_datasets.py' +``` + +The generated manifest contains counts, provenance and SHA-256 hashes of the +native, HIF, incidence and updated inputs. Verify these against the recorded run's +manifest before combining measurements. Missing scientific/Mathlib source inputs +cause those cases to be omitted; they do not silently produce empty fixtures. + +The recorded Diseasome download SHA-256 is +`5b85df24ec67ae8f796478f7559ef238fe0f17c6d63c613d9502335444b54ea1`. +Mathlib's `source_sha256` is the script's digest of sorted source paths and file +bytes, not the archive hash; it is +`dde69137f0279fdb1ec0b40e16dd5252c0a13aa8550c9508e71ea811565347f3`. + +## Scientific figure workflows + +Use an isolated CPython 3.12 environment and the recorded requirements lock +(`evidence/scientific-requirements.txt` in the report). The direct pins are +HyperNetX 2.4.3, XGI 0.10.2, Hypergraphx 1.8.0, Matplotlib 3.11.2 and Pillow 12.3.0. + +```sh +python -m pip install -r docs/benchmarks/ecosystem-2026-10-03/evidence/scientific-requirements.txt +python -m unittest discover -s scripts/benchmarks -p 'test_*.py' +python scripts/benchmarks/scientific.py --validate --output target/benchmark/science/validation +python scripts/benchmarks/scientific.py --run --output target/benchmark/science/results +python scripts/benchmarks/verify_scientific.py +``` + +The runner starts a fresh worker per case, discards one warmup and records five +attempts. It tests model construction on all inputs and plots correctness, +moderate and Diseasome only. Its 120-second watchdog covers the whole worker. +Construction, artist setup, Agg raster, buffer copy and PNG compression are +separate stages. Model checks are opaque-metadata adapter checks, not tests of +each tool's scientific file format. No figure timing is converted to FPS. + +## Hyper conversion and native renderer + +```sh +cargo build --release -p hyper-viz --example ecosystem_core +cargo build --release -p hyper-viz-bevy --features benchmark --bin ecosystem-benchmark +python3 scripts/benchmarks/run_local.py core --output target/benchmark/core +python3 scripts/benchmarks/run_local.py native --output target/benchmark/native +python3 scripts/benchmarks/run_local.py native --datasets mathlib --projection star --live-datasets mathlib --output target/benchmark/native-star +python3 scripts/benchmarks/run_local.py native --datasets moderate,mathlib --projection star --live-datasets moderate,mathlib --labels --only-hulls --output target/benchmark/native-interactive +``` + +These commands use macOS `/usr/bin/time -l` for whole-process peak RSS and require +access to the real desktop GPU. Do not combine software-rendered or headless +substitutions with native results. Set `BENCHMARK_CONDITIONS` to power, thermal, +background-work and display-cadence observations. Native jobs use 120 warmup and +300 measured application frames, with five fresh processes per configuration. +Live modes perform one solver step per frame. Frozen coordinates do not measure +layout quality. The production hull cap is 24 vertices, so large hulls are +approximate. The opt-in feature does not change normal viewer startup. + +## Browser incidence renderer + +See [browser/README.md](browser/README.md) for installation, pinned Chromium, +Metal verification, untimed validation and camera-motion commands. The recorded +run uses `--job-timeout-ms 90000 --warmup 120 --samples 300 --repetitions 5`. +It keeps all incidences, disables forces and labels, and tests 3d-force-graph, +Cytoscape.js and Sigma. A failed batch has no completed rendering score. + +## Desktop competitors + +See [desktop/README.md](desktop/README.md) for pinned versions, source adapters, +runtime hashes and limitations. After preparing the isolated upstream project: + +```sh +python3 scripts/benchmarks/desktop/run.py hypergodot --executable /path/to/Godot --project /path/to/prepared-hypergodot --output target/benchmark/hypergodot +python3 scripts/benchmarks/desktop/run.py graphia --executable /path/to/Graphia --output target/benchmark/graphia +``` + +HyperGodot measures five warmed intervals in one process, unlike Hyper's fresh +processes. Graphia measures fresh-process headless import/save including startup, +then validates every saved incidence and JSON metadata attribute. Graphia's +adapter does not set render positions or retain document metadata and its stock +saved graph is directed. Neither metric is interchangeable with browser FPS. + +## Aggregate + +```sh +python3 scripts/benchmarks/summarize.py +python3 scripts/benchmarks/report.py +``` + +The report includes all attempts and exclusions. Average FPS is sample count +divided by total interval time; summaries use medians of per-run values and +quantiles. Single-machine exploratory observations need other hardware and human +tasks before they support broad product performance claims. + +## Recompute the recorded table without new measurements + +Use a fresh, empty restoration directory. This uses only the archived observations: + +```sh +python3 scripts/benchmarks/restore_evidence.py --output target/benchmark/archived-replay +python3 scripts/benchmarks/summarize.py --input target/benchmark/archived-replay --output target/benchmark/recomputed-report +python3 scripts/benchmarks/report.py --directory target/benchmark/recomputed-report +``` + +The recorded JSON, CSV and Markdown were regenerated from the archive and matched +byte-for-byte. `checksums.json` records the report, evidence and current harness +source hashes. Excluded diagnostics remain separate from restored score inputs. diff --git a/scripts/benchmarks/archive.py b/scripts/benchmarks/archive.py new file mode 100644 index 0000000..8098aef --- /dev/null +++ b/scripts/benchmarks/archive.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""Archive completed local runs and hash the evidence and current harness sources.""" +import argparse +import gzip +import hashlib +import json +from pathlib import Path +import shutil + + +def archive(root, destination): + evidence = destination / 'evidence' + evidence.mkdir(parents=True, exist_ok=True) + for track, expected in [('core',42),('native',80),('native-star',20),('native-interactive',20),('graphia',18),('hypergodot',1)]: + source = root / track / 'raw.json' + rows = json.loads(source.read_text()) + assert len(rows) == expected, (track, len(rows), expected) + name = track + '-raw.json' + if track.startswith('native'): + (evidence / (name + '.gz')).write_bytes(gzip.compress(source.read_bytes(), mtime=0)) + else: + shutil.copyfile(source, evidence / name) + shutil.copyfile(root / track / 'metadata.json', evidence / (track + '-metadata.json')) + for track, expected in [('browser-sigma-final',35),('browser-final',105)]: + source = root / track / 'results.json' + report = json.loads(source.read_text()) + assert len(report['runs']) == expected + assert len({(r['tool'],r['dataset'],r['mode'],r['repetition']) for r in report['runs']}) == expected + assert all(len(r['trace']['rafIntervalMs']) == 300 for r in report['runs'] if r['status'] == 'completed') + (evidence / (track + '-results.json.gz')).write_bytes(gzip.compress(source.read_bytes(), mtime=0)) + shutil.copyfile(root / 'science/independent-validation.json', evidence / 'scientific-independent-validation.json') + diagnostic = root / 'hypergodot-window-clamped-diagnostic' + (evidence / 'hypergodot-clamped-window-diagnostic.json.gz').write_bytes(gzip.compress((diagnostic / 'raw.json').read_bytes(),mtime=0)) + shutil.copyfile(diagnostic / 'metadata.json',evidence / 'hypergodot-clamped-window-diagnostic-metadata.json') + notes = json.loads((evidence / 'driver-notes.json').read_text()) + notes['browser'] = 'Final105 unique jobs: original70 Cytoscape/3D cases plus35 corrected Sigma cases. Original browser closed after evidence was saved; its lingering Node/esbuild worker was then terminated. Explicit esbuild shutdown was added before the corrected run.' + notes['hypergodot'] = 'HiDPI-disabled window was clamped to1920x917 and excluded. Fullscreen smoke did not match. Final five repetitions restore stock HiDPI and verify actual render texture1920x1080 with exact memberships.' + notes['sigma'] = 'Original full-refresh camera timings are retained only as excluded diagnostics. All35 were replaced by public scheduleRender cached-redraw observations in a separate block. Consolidation verifies input hashes, protocol, browser and package versions.' + (evidence / 'driver-notes.json').write_text(json.dumps(notes, indent=2) + '\n') + paths = sorted(p for p in destination.rglob('*') if p.is_file() and p.name != 'checksums.json') + sources = sorted(p for p in Path('scripts/benchmarks').rglob('*') if p.is_file() and 'node_modules' not in p.parts and '__pycache__' not in p.parts) + sources += [Path(p) for p in ['crates/hyper-viz/examples/ecosystem_core.rs','crates/hyper-viz-bevy/src/benchmark.rs','crates/hyper-viz-bevy/src/bin/ecosystem_benchmark.rs']] + checks = {str(p): {'bytes':p.stat().st_size,'sha256':hashlib.sha256(p.read_bytes()).hexdigest()} for p in paths + sources} + (destination / 'checksums.json').write_text(json.dumps({'scope':'Archived evidence/report and current harness sources. Post-run orchestration/checker changes are described in driver-notes; source hashes are not executable hashes.', 'files':checks},indent=2)+'\n') + print('Archived', len(checks), 'files') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--input', type=Path, default=Path('target/benchmark')) + parser.add_argument('--output', type=Path, default=Path('docs/benchmarks/ecosystem-2026-10-03')) + args = parser.parse_args() + archive(args.input,args.output) diff --git a/scripts/benchmarks/browser/.gitignore b/scripts/benchmarks/browser/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/scripts/benchmarks/browser/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/scripts/benchmarks/browser/README.md b/scripts/benchmarks/browser/README.md new file mode 100644 index 0000000..1e187c6 --- /dev/null +++ b/scripts/benchmarks/browser/README.md @@ -0,0 +1,34 @@ +# Browser incidence rendering benchmark + +This harness measures pinned browser baselines using canonical native Hyper and bipartite scene inputs produced by `scripts/benchmarks/datasets.py`. It validates every scene against exact native memberships, all IDs (including isolated vertices and empty-edge hubs), fixed coordinates, and each live library's own graph structure before measuring. It validates the shared `*.updated.json`/`*.updated.incidence.json` fixtures after each update. It hashes raw input bytes, lockfile and topology. + +Install with `npm ci --prefix scripts/benchmarks/browser`. Node 24.16.0, Playwright 1.63.0 and exact npm dependency versions are recorded in every evidence JSON. The committed lockfile pins transitive dependencies and registry integrity hashes. + +Run untimed validation: + +```sh +node scripts/benchmarks/browser/run.mjs --validate-only --executable '/Users/bregy/Library/Caches/ms-playwright/chromium-1243/chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing' +``` + +This cached browser was verified as Chrome for Testing 153.0.8010.12, Chromium revision `971a7443b0c9b0a9b2860529b33331b76077ec62`. Use an explicit installed browser path, or install Playwright's pinned browser with `npx --prefix scripts/benchmarks/browser playwright install chromium` and omit `--executable`. Evidence includes the *actual* CDP browser version/revision. Do not silently substitute another browser and combine its results. + +Timed run, after the coordinator has finished other resource-intensive workloads: + +```sh +BENCHMARK_CONDITIONS='AC power; thermal condition; background jobs; display refresh rate' \ +node scripts/benchmarks/browser/run.mjs --datasets moderate,large,high-arity-10,high-arity-100,high-arity-1000,scientific,mathlib --modes camera-motion --job-timeout-ms 90000 --executable '/Users/bregy/Library/Caches/ms-playwright/chromium-1243/chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing' +``` + +The main example selects camera motion only across seven datasets (105 runs). Both modes remain the default. A per-job 180-second watchdog (`--job-timeout-ms`) closes failed/stalled pages, saves a failed row with its stage/reason and continues, preserving partial evidence. The default is a headed 1920×1080 viewport with DPR 1, browser default vsync, five repetitions, 120 warmup rAF callbacks and 300 sample callbacks for both static and camera-motion modes. A seeded Fisher–Yates shuffle randomizes tool/dataset/mode order within every repetition. `--datasets`, `--tools`, `--modes static,camera-motion`, `--seed`, `--output`, `--executable` can restrict or place runs. `--headless` is explicit and recorded. `--swiftshader` requests software rendering explicitly and records the true backend; do not publish it as hardware performance. + +At a 60 Hz rAF cadence, 300 samples span about five seconds. No artificial sleep enforces a fixed display cadence. Static scenes also request redraw every callback, so Sigma and Cytoscape do not receive idle-scene scores. 3d-force-graph's internal animation is paused and its public Three.js renderer draws directly; force layout is disabled and fx/fy/fz remain canonical. Sigma's public scheduleRender schedules a cached redraw without reprocessing unchanged graph data while Cytoscape's public forceRender *schedules* a redraw. CPU command duration therefore measures different submission paths and must remain distinct from rAF wall intervals. A two-rAF scripted-update feedback surrogate is separately recorded; it is not human input latency or compositor presentation. Browser startup/evaluate load wall time includes fixture transfer and driver roundtrip. GPU timestamps, GPU completion, layout convergence, peak memory and human task accuracy are unmeasured. + +All vertices, edge hubs and incidence links are supplied, without sampling or line budgets. Settings are nodeRelSize 2, nodeResolution 8, nodeVal 1, linkWidth 0 (thin native WebGL lines), opacity 0.35 for 3d-force-graph, and basic 2D circles/lines, labels disabled, 1 px line width and opacity 0.35 for Cytoscape/Sigma. Exact settings and framing are saved per run. Cyto uses negated y only for its screen coordinate convention and converts it back for canonical verification. Sigma and Cytoscape show XY projections with y upward; 3D uses perspective and orbit. They share fixed source coordinates but cannot be called equivalent GPU workloads or camera projections. Sigma's native normalization/pixel node sizing and 3D sphere world sizing also differ. Report their tracks separately. + +Validation JSON and screenshots go under ignored `target/benchmark/browser`. Screenshot readback happens outside timing and after the canonical update, once per tool/dataset (first static run, or first motion run when static is omitted). Traces contain all individual CPU command durations and rAF callback timestamps/intervals; summaries report p50/p95/p99, mean, max and average throughput calculated as `sample count / sum(wall intervals)`. Do not use inverse median as average FPS. The instrumentation records every actual WebGL renderer, extension availability, and Chromium CDP GPU information. Cytoscape uses Canvas2D: browser GPU device metadata is not evidence that its graph geometry uses WebGL. + +## HNX Widget capability probe + +Official [HNX Widget README](https://github.com/pnnl/hypernetx-widget) describes notebook installation and a bipartite view. Source revision `58e795d6a362dcd3bbd9ccff462052956a40f4f8`, package version `0.1.1-beta.0` declares React 16. The [view source](https://github.com/pnnl/hypernetx-widget/blob/58e795d6a362dcd3bbd9ccff462052956a40f4f8/src/HypernetxWidgetView.js) accepts `pos={uid:[x,y]}` and assigns those positions to internal vertex parents, but not hyperedge hubs. It runs charge/link/center/collide/bound forces on hubs, and has no public frozen shared-hub position/stop simulation API. This prevents a matched frozen bipartite comparison without modifying its rendering implementation. The Python wrapper derives vertices from the union of memberships, omitting explicit isolated vertices. A standalone npm package availability request timed out; that is recorded as an installation probe failure, not a renderer capacity result. A notebook native group-exploration smoke test may be run separately; this harness marks the matched frozen incidence track unsupported rather than assigning a score. + +Primary API references used: [3d-force-graph](https://github.com/vasturiano/3d-force-graph), [Cytoscape.js](https://js.cytoscape.org/), [Sigma lifecycle](https://www.sigmajs.org/docs/advanced/lifecycle/). Local installed API/source was also inspected for render submission and RGBA handling. diff --git a/scripts/benchmarks/browser/client.js b/scripts/benchmarks/browser/client.js new file mode 100644 index 0000000..367681c --- /dev/null +++ b/scripts/benchmarks/browser/client.js @@ -0,0 +1,102 @@ +import ForceGraph3D from '3d-force-graph'; +import cytoscape from 'cytoscape'; +import Sigma from 'sigma'; +import Graph from 'graphology'; + +const WIDTH = 1920, HEIGHT = 1080; +const host = document.getElementById('view'); +let adapter, sourceScene, framing; +const color = n => n.role === 'edge' || n.id.startsWith('e:') ? '#ef9866' : '#5cbded'; +const endpoint = p => typeof p === 'object' ? p.id : p; +const copy = x => structuredClone(x); +function bounds(nodes) { + const xs = nodes.map(n => n.x), ys = nodes.map(n => n.y), zs = nodes.map(n => n.z); + const extent = a => [Math.min(...a), Math.max(...a)]; + const [xmin,xmax]=extent(xs), [ymin,ymax]=extent(ys), [zmin,zmax]=extent(zs); + return {x:(xmin+xmax)/2,y:(ymin+ymax)/2,z:(zmin+zmax)/2, + span:Math.max(xmax-xmin,ymax-ymin,zmax-zmin,1), + zoom:Math.min((WIDTH-80)/Math.max(xmax-xmin,1),(HEIGHT-80)/Math.max(ymax-ymin,1))}; +} +function topology(nodes,links) { + return {nodes:nodes.map(n=>({id:n.id,x:n.x,y:n.y,z:n.z})).sort((a,b)=>a.id.localeCompare(b.id)), + links:links.map(l=>({id:l.id,source:endpoint(l.source),target:endpoint(l.target)})).sort((a,b)=>a.id.localeCompare(b.id))}; +} +const raf = () => new Promise(resolve => requestAnimationFrame(resolve)); +window.benchmark = { + async setup(tool, scene) { + sourceScene=copy(scene); framing=bounds(scene.nodes); host.innerHTML=''; + const nodes=copy(scene.nodes), links=copy(scene.links); + if(tool==='3d-force-graph') { + const graph=new ForceGraph3D(host,{controlType:'orbit',rendererConfig:{antialias:true,alpha:false}}) + .width(WIDTH).height(HEIGHT).backgroundColor('#080c14').showNavInfo(false) + .nodeRelSize(2).nodeResolution(8).nodeVal(1).nodeLabel('').nodeColor(color) + .linkWidth(0).linkOpacity(0.35).linkColor(()=> '#8fa9c4').linkLabel('') + .enableNodeDrag(false).enablePointerInteraction(false).cooldownTicks(0) + .graphData({nodes:nodes.map(n=>({...n,fx:n.x,fy:n.y,fz:n.z})),links}); + graph.renderer().setPixelRatio(1); + await raf(); await raf(); graph.pauseAnimation(); + const camera=graph.camera(), distance=framing.span/(2*Math.tan(camera.fov*Math.PI/360))*1.18+framing.span/2; + graph.cameraPosition({x:framing.x,y:framing.y,z:framing.z+distance},{x:framing.x,y:framing.y,z:framing.z},0); + adapter={kind:'WebGL3D',counters(){return {render:{...graph.renderer().info.render},memory:{...graph.renderer().info.memory},meaning:'Three.js counters for last CPU render submission; not GPU duration or process RSS'};},cameraState(){return {position:camera.position.toArray(),fov:camera.fov,aspect:camera.aspect,near:camera.near,far:camera.far};}, render(angle){ + camera.position.set(framing.x+Math.sin(angle)*distance,framing.y,framing.z+Math.cos(angle)*distance); + camera.lookAt(framing.x,framing.y,framing.z); graph.renderer().render(graph.scene(),camera); + }, snapshot(){return topology(graph.graphData().nodes,graph.graphData().links);}, + update(updated){const old=new Map(graph.graphData().nodes.map(n=>[n.id,n])); + graph.graphData({nodes:updated.nodes.map(n=>Object.assign(old.get(n.id)||{},n,{fx:n.x,fy:n.y,fz:n.z})),links:copy(updated.links)}); + },dispose(){graph._destructor();}}; + } else if(tool==='cytoscape') { + const cy=cytoscape({container:host,layout:{name:'preset'},pixelRatio:1,minZoom:1e-9,maxZoom:1e9, + style:[{selector:'node',style:{'width':4,'height':4,'background-color':n=>color(n.data()),'label':''}}, + {selector:'edge',style:{'width':1,'line-color':'#8fa9c4','opacity':0.35,'curve-style':'straight','label':''}}], + elements:[...nodes.map(n=>({data:{...n},position:{x:n.x,y:-n.y}})),...links.map(l=>({data:{...l}}))], + userZoomingEnabled:false,userPanningEnabled:false,boxSelectionEnabled:false}); + cy.zoom(framing.zoom); cy.pan({x:WIDTH/2-framing.x*framing.zoom,y:HEIGHT/2+framing.y*framing.zoom}); + adapter={kind:'Canvas2D',cameraState(){return {zoom:cy.zoom(),pan:cy.pan()};},render(angle){cy.pan({x:WIDTH/2-framing.x*framing.zoom+Math.sin(angle)*40,y:HEIGHT/2+framing.y*framing.zoom});cy.forceRender();}, + snapshot(){return topology(cy.nodes().map(n=>({...n.data(),x:n.position('x'),y:-n.position('y')})),cy.edges().map(e=>e.data()));}, + update(updated){cy.batch(()=>{for(const n of updated.nodes){cy.getElementById(n.id).data(n).position({x:n.x,y:-n.y});} + const wanted=new Set(updated.links.map(l=>l.id));cy.edges().filter(e=>!wanted.has(e.id())).remove(); + for(const l of updated.links){const edge=cy.getElementById(l.id);if(edge.length)edge.move({source:l.source,target:l.target});else cy.add({data:{...l}});}});},dispose(){cy.destroy();}}; + } else if(tool==='sigma') { + const graph=new Graph({type:'undirected',multi:true,allowSelfLoops:false}); + nodes.forEach(n=>graph.addNode(n.id,{...n,size:2,color:color(n),label:''})); + links.forEach(l=>graph.addUndirectedEdgeWithKey(l.id,l.source,l.target,{size:1,color:'rgba(143,169,196,0.35)'})); + const sigma=new Sigma(graph,host,{renderLabels:false,renderEdgeLabels:false,hideEdgesOnMove:false,hideLabelsOnMove:false, + enableEdgeEvents:false,enableCameraRotation:false,stagePadding:40,zIndex:false}); + const camera=sigma.getCamera(); camera.setState({x:0.5,y:0.5,ratio:1,angle:0}); + adapter={kind:'WebGL2D',cameraState(){return camera.getState();},render(angle){camera.setState({x:0.5+Math.sin(angle)*0.025,y:0.5,ratio:1,angle:0});sigma.scheduleRender();}, + snapshot(){return topology(graph.mapNodes((id,a)=>({id,...a})),graph.mapEdges((id,a,source,target)=>({id,source,target})));}, + update(updated){for(const n of updated.nodes)graph.mergeNodeAttributes(n.id,n); + const wanted=new Set(updated.links.map(l=>l.id));for(const id of graph.edges())if(!wanted.has(id))graph.dropEdge(id); + for(const l of updated.links){if(!graph.hasEdge(l.id)||graph.source(l.id)!==l.source||graph.target(l.id)!==l.target){if(graph.hasEdge(l.id))graph.dropEdge(l.id);graph.addUndirectedEdgeWithKey(l.id,l.source,l.target,{size:1,color:'rgba(143,169,196,0.35)'});}} + sigma.refresh();},dispose(){sigma.kill();}}; + } else throw new Error(`Unknown tool ${tool}`); + adapter.render(0); await raf(); await raf(); + return {topology:adapter.snapshot(),kind:adapter.kind,framing, + settings:{initialCameraState:adapter.cameraState(),width:WIDTH,height:HEIGHT,dpr:devicePixelRatio,labels:false,layout:'none; canonical fixed coordinates',lineOpacity:0.35,nodeSize2D:2,lineWidth2D:1,nodeRadius3D:2,nodeResolution3D:8,linkWidth3D:0, + camera:tool==='3d-force-graph'?'perspective fit all coordinates, orbit around canonical center':'2D XY projection with Y upward, fitted initial viewport', + commandTiming:tool==='cytoscape'?'public forceRender schedules Canvas redraw; submission only':tool==='sigma'?'public scheduleRender schedules cached WebGL redraw; submission only':'JavaScript render command; CPU only, excludes GPU completion'}}; + }, + snapshot(){return adapter.snapshot();}, + graphics(){return {contexts:window.__glContexts.map(gl=>{ + const debug=gl.getExtension('WEBGL_debug_renderer_info'); + return {version:gl.getParameter(gl.VERSION),renderer:debug?gl.getParameter(debug.UNMASKED_RENDERER_WEBGL):gl.getParameter(gl.RENDERER),softwareFallback:/swiftshader|llvmpipe|lavapipe|software/i.test(debug?gl.getParameter(debug.UNMASKED_RENDERER_WEBGL):gl.getParameter(gl.RENDERER)),vendor:debug?gl.getParameter(debug.UNMASKED_VENDOR_WEBGL):gl.getParameter(gl.VENDOR), + timerQuerySupported:!!(gl.getExtension('EXT_disjoint_timer_query_webgl2')||gl.getExtension('EXT_disjoint_timer_query'))}; + }),gpuTiming:'not measured; WebGL timestamp extension availability reported only',gpuFinish:'not called; GPU synchronization would alter workload'};}, + async sample({mode,warmup=120,samples=300}) { + const commandMs=[],rafIntervalMs=[],rafTimestampsMs=[];let previous; + for(let frame=-warmup;frame=0){commandMs.push(end-start);rafTimestampsMs.push(stamp);rafIntervalMs.push(stamp-previous);}previous=stamp; + } + return {commandMs,rafIntervalMs,rafTimestampsMs,mode,warmup,samples,rendererCounters:adapter.counters?.()||null, + averageThroughputFPS:1000*samples/rafIntervalMs.reduce((a,b)=>a+b,0), + meaning:'requestAnimationFrame callback intervals are wall cadence, not GPU duration or verified presented frames'}; + }, + async update(updated,measure=true){ + const start=performance.now();adapter.update(updated);const commandEnd=performance.now();adapter.render(0); + await raf();await raf();const completion=performance.now(); + return {topology:adapter.snapshot(),...(measure?{commandMs:commandEnd-start,twoRafFeedbackSurrogateMs:completion-start}:{}), + meaning:'script-triggered graph update followed by two rAF callbacks; not human input latency or compositor presentation'}; + },dispose(){adapter?.dispose();adapter=null;} +}; diff --git a/scripts/benchmarks/browser/package-lock.json b/scripts/benchmarks/browser/package-lock.json new file mode 100644 index 0000000..995bea9 --- /dev/null +++ b/scripts/benchmarks/browser/package-lock.json @@ -0,0 +1,964 @@ +{ + "name": "hyper-browser-benchmark", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "hyper-browser-benchmark", + "dependencies": { + "3d-force-graph": "1.80.1", + "cytoscape": "3.34.3", + "graphology": "0.26.0", + "sigma": "3.0.3" + }, + "devDependencies": { + "esbuild": "0.28.2", + "playwright": "1.63.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@tweenjs/tween.js": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-25.0.0.tgz", + "integrity": "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A==", + "license": "MIT" + }, + "node_modules/3d-force-graph": { + "version": "1.80.1", + "resolved": "https://registry.npmjs.org/3d-force-graph/-/3d-force-graph-1.80.1.tgz", + "integrity": "sha512-8+JcH/K1MkVncxthCVg1vdNY49PSUY7gPMlFzBHJD91AmofqqxZNnVo5RfY61BShwZk6koqyYK7f5hp+NT6utQ==", + "license": "MIT", + "dependencies": { + "accessor-fn": "1", + "kapsule": "^1.16", + "three": ">=0.179 <1", + "three-forcegraph": "1", + "three-render-objects": "^1.41" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/accessor-fn": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/accessor-fn/-/accessor-fn-1.5.3.tgz", + "integrity": "sha512-rkAofCwe/FvYFUlMB0v0gWmhqtfAtV1IUkdPbfhTUyYniu5LrC0A0UJkTH0Jv3S8SvwkmfuAlY+mQIJATdocMA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/cytoscape": { + "version": "3.34.3", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.3.tgz", + "integrity": "sha512-yfYGhRcGAntq6YBD583j4n0Eg3jIxvWmZtz/5uz9UYkeIStSlMxuUja+ec5j3iBD8nv1rwaOAYMW09tBdkSeaQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-binarytree": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d3-binarytree/-/d3-binarytree-1.0.2.tgz", + "integrity": "sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw==", + "license": "MIT" + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force-3d": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/d3-force-3d/-/d3-force-3d-3.0.6.tgz", + "integrity": "sha512-4tsKHUPLOVkyfEffZo1v6sFHvGFwAIIjt/W8IThbp08DYAsXZck+2pSHEG5W1+gQgEvFLdZkYvmJAbRM2EzMnA==", + "license": "MIT", + "dependencies": { + "d3-binarytree": "1", + "d3-dispatch": "1 - 3", + "d3-octree": "1", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-octree": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-octree/-/d3-octree-1.1.0.tgz", + "integrity": "sha512-F8gPlqpP+HwRPMO/8uOu5wjH110+6q4cgJvgJT6vlpy3BEaDIKlTZrgHKZSp/i1InRpVfh4puY/kvL6MxK930A==", + "license": "MIT" + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-bind-mapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/data-bind-mapper/-/data-bind-mapper-1.0.3.tgz", + "integrity": "sha512-QmU3lyEnbENQPo0M1F9BMu4s6cqNNp8iJA+b/HP2sSb7pf3dxwF3+EP1eO69rwBfH9kFJ1apmzrtogAmVt2/Xw==", + "license": "MIT", + "dependencies": { + "accessor-fn": "1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/float-tooltip": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/float-tooltip/-/float-tooltip-1.7.5.tgz", + "integrity": "sha512-/kXzuDnnBqyyWyhDMH7+PfP8J/oXiAavGzcRxASOMRHFuReDtofizLLJsf7nnDLAfEaMW4pVWaXrAjtnglpEkg==", + "license": "MIT", + "dependencies": { + "d3-selection": "2 - 3", + "kapsule": "^1.16", + "preact": "10" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/graphology": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/graphology/-/graphology-0.26.0.tgz", + "integrity": "sha512-8SSImzgUUYC89Z042s+0r/vMibY7GX/Emz4LDO5e7jYXhuoWfHISPFJYjpRLUSJGq6UQ6xlenvX1p/hJdfXuXg==", + "license": "MIT", + "dependencies": { + "events": "^3.3.0" + }, + "peerDependencies": { + "graphology-types": ">=0.24.0" + } + }, + "node_modules/graphology-types": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.8.tgz", + "integrity": "sha512-hDRKYXa8TsoZHjgEaysSRyPdT6uB78Ci8WnjgbStlQysz7xR52PInxNsmnB7IBOM1BhikxkNyCVEFgmPKnpx3Q==", + "license": "MIT", + "peer": true + }, + "node_modules/graphology-utils": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/graphology-utils/-/graphology-utils-2.5.2.tgz", + "integrity": "sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ==", + "license": "MIT", + "peerDependencies": { + "graphology-types": ">=0.23.0" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/kapsule": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/kapsule/-/kapsule-1.16.3.tgz", + "integrity": "sha512-4+5mNNf4vZDSwPhKprKwz3330iisPrb08JyMgbsdFrimBCKNHecua/WBwvVg3n7vwx0C1ARjfhwIpbrbd9n5wg==", + "license": "MIT", + "dependencies": { + "lodash-es": "4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, + "node_modules/ngraph.events": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ngraph.events/-/ngraph.events-1.4.0.tgz", + "integrity": "sha512-NeDGI4DSyjBNBRtA86222JoYietsmCXbs8CEB0dZ51Xeh4lhVl1y3wpWLumczvnha8sFQIW4E0vvVWwgmX2mGw==", + "license": "BSD-3-Clause" + }, + "node_modules/ngraph.forcelayout": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/ngraph.forcelayout/-/ngraph.forcelayout-3.3.1.tgz", + "integrity": "sha512-MKBuEh1wujyQHFTW57y5vd/uuEOK0XfXYxm3lC7kktjJLRdt/KEKEknyOlc6tjXflqBKEuYBBcu7Ax5VY+S6aw==", + "license": "BSD-3-Clause", + "dependencies": { + "ngraph.events": "^1.0.0", + "ngraph.merge": "^1.0.0", + "ngraph.random": "^1.0.0" + } + }, + "node_modules/ngraph.graph": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/ngraph.graph/-/ngraph.graph-20.1.2.tgz", + "integrity": "sha512-W/G3GBR3Y5UxMLHTUCPP9v+pbtpzwuAEIqP5oZV+9IwgxAIEZwh+Foc60iPc1idlnK7Zxu0p3puxAyNmDvBd0Q==", + "license": "BSD-3-Clause", + "dependencies": { + "ngraph.events": "^1.4.0" + } + }, + "node_modules/ngraph.merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ngraph.merge/-/ngraph.merge-1.0.0.tgz", + "integrity": "sha512-5J8YjGITUJeapsomtTALYsw7rFveYkM+lBj3QiYZ79EymQcuri65Nw3knQtFxQBU1r5iOaVRXrSwMENUPK62Vg==", + "license": "MIT" + }, + "node_modules/ngraph.random": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ngraph.random/-/ngraph.random-1.2.0.tgz", + "integrity": "sha512-4EUeAGbB2HWX9njd6bP6tciN6ByJfoaAvmVL9QTaZSeXrW46eNGA9GajiXiPBbvFqxUWFkEbyo6x5qsACUuVfA==", + "license": "BSD-3-Clause" + }, + "node_modules/playwright": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz", + "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright-core": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz", + "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/polished": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", + "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.17.8" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/preact": { + "version": "10.29.8", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.8.tgz", + "integrity": "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "preact-render-to-string": ">=5" + }, + "peerDependenciesMeta": { + "preact-render-to-string": { + "optional": true + } + } + }, + "node_modules/sigma": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sigma/-/sigma-3.0.3.tgz", + "integrity": "sha512-5H0zFlx6/NTQpqBg4Rm569ZOpnBOXMaS25UQThIWMU3XyzI5AhmorK/gnl87BvJBLhQd0tW4C0LIp3enWzMoNw==", + "license": "MIT", + "dependencies": { + "events": "^3.3.0", + "graphology-utils": "^2.5.2" + } + }, + "node_modules/three": { + "version": "0.186.1", + "resolved": "https://registry.npmjs.org/three/-/three-0.186.1.tgz", + "integrity": "sha512-blFeqb49wRCSGUGj7gtpfnSGHy2lwDk94RhUmS1c/hTby70kvChbWpkJ4Pm1390LqzzvTmzgXKHPEafJwCb8jA==", + "license": "MIT" + }, + "node_modules/three-forcegraph": { + "version": "1.43.6", + "resolved": "https://registry.npmjs.org/three-forcegraph/-/three-forcegraph-1.43.6.tgz", + "integrity": "sha512-fayF9Cn6XfSUJOcKUcJ1tg8EQEHP9X3wELzNX/Ik3TxnbHTfYuEFspSld7GvK8RwEpsyoJaPbfEGgMTJoiywYg==", + "license": "MIT", + "dependencies": { + "accessor-fn": "1", + "d3-array": "1 - 3", + "d3-force-3d": "2 - 3", + "d3-scale": "1 - 4", + "d3-scale-chromatic": "1 - 3", + "data-bind-mapper": "1", + "kapsule": "^1.16", + "ngraph.forcelayout": "3", + "ngraph.graph": "20", + "tinycolor2": "1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "three": ">=0.118.3" + } + }, + "node_modules/three-render-objects": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/three-render-objects/-/three-render-objects-1.43.0.tgz", + "integrity": "sha512-CoGJ7dRJsbtGoR4YhxUQlgocZ4vnmVC0YEFsvTye0DKifHPogoa7zl9knea+3EG2+biDicTC1PW7O02TxlV2vQ==", + "license": "MIT", + "dependencies": { + "@tweenjs/tween.js": "18 - 25", + "accessor-fn": "1", + "float-tooltip": "^1.7", + "kapsule": "^1.16", + "polished": "4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "three": ">=0.179" + } + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "license": "MIT" + } + } +} diff --git a/scripts/benchmarks/browser/package.json b/scripts/benchmarks/browser/package.json new file mode 100644 index 0000000..7b4739f --- /dev/null +++ b/scripts/benchmarks/browser/package.json @@ -0,0 +1,19 @@ +{ + "name": "hyper-browser-benchmark", + "private": true, + "type": "module", + "scripts": { + "validate": "node run.mjs --validate-only", + "benchmark": "node run.mjs" + }, + "dependencies": { + "3d-force-graph": "1.80.1", + "cytoscape": "3.34.3", + "sigma": "3.0.3", + "graphology": "0.26.0" + }, + "devDependencies": { + "esbuild": "0.28.2", + "playwright": "1.63.0" + } +} diff --git a/scripts/benchmarks/browser/run.mjs b/scripts/benchmarks/browser/run.mjs new file mode 100644 index 0000000..92146bb --- /dev/null +++ b/scripts/benchmarks/browser/run.mjs @@ -0,0 +1,105 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import http from 'node:http'; +import crypto from 'node:crypto'; +import os from 'node:os'; +import {fileURLToPath} from 'node:url'; +import {build,stop} from 'esbuild'; +import {chromium} from 'playwright'; + +const here=path.dirname(fileURLToPath(import.meta.url)),root=path.resolve(here,'../../..'); +const args=process.argv.slice(2), value=(name,fallback)=>args.includes(name)?args[args.indexOf(name)+1]:fallback; +const validateOnly=args.includes('--validate-only'); +const datasets=value('--datasets',validateOnly?'correctness':'correctness,moderate,large,high-arity-10,high-arity-100,high-arity-1000,scientific,mathlib').split(','); +const tools=value('--tools','3d-force-graph,cytoscape,sigma').split(','); +const modes=value('--modes','static,camera-motion').split(','); +if(modes.some(m=>!['static','camera-motion'].includes(m)))throw Error('Modes must be static or camera-motion'); +const jobTimeoutMs=Number(value('--job-timeout-ms','180000')); +if(!Number.isFinite(jobTimeoutMs)||jobTimeoutMs<=0)throw Error('Invalid watchdog duration'); +const output=path.resolve(root,value('--output','target/benchmark/browser')); +const executable=value('--executable',process.env.BENCHMARK_CHROMIUM_EXECUTABLE); +const headed=!args.includes('--headless'); +const seed=Number(value('--seed','20261003')),warmup=Number(value('--warmup','120')),samples=Number(value('--samples','300')),repetitions=Number(value('--repetitions','5')); +if(!validateOnly&&(warmup<120||samples<300||repetitions<5))throw Error('Protocol requires >=120 warmup frames, >=300 samples, >=5 repetitions'); +await fs.mkdir(output,{recursive:true}); +const sha=b=>crypto.createHash('sha256').update(b).digest('hex'); +const canonical=p=>JSON.stringify(p); +function assertions(graph,scene){ + const ids=scene.nodes.map(n=>n.id);if(new Set(ids).size!==ids.length)throw Error('Duplicate scene node IDs'); + if(new Set(scene.links.map(l=>l.id)).size!==scene.links.length)throw Error('Duplicate scene link IDs'); + const expectedIds=[...graph.vertices.map(v=>'v:'+v.id),...graph.hyperedges.map(e=>'e:'+e.id)].sort(); + if(canonical([...ids].sort())!==canonical(expectedIds))throw Error('Missing vertex or edge hub; includes isolates and empty edges'); + const expected=graph.hyperedges.flatMap(e=>[...new Set(e.vertices)].map(v=>'e:'+e.id+'\0v:'+v)).sort(); + const actual=scene.links.map(l=>l.source+'\0'+l.target).sort(); + if(canonical(expected)!==canonical(actual))throw Error('Canonical exact memberships differ from incidence scene'); + for(const n of scene.nodes)if(![n.x,n.y,n.z].every(Number.isFinite))throw Error('Non-finite coordinate '+n.id); + const sceneById=new Map(scene.nodes.map(n=>[n.id,n])); + for(const v of graph.vertices){const n=sceneById.get('v:'+v.id);if(canonical(v.attrs.position)!==canonical([n.x,n.y,n.z]))throw Error('Vertex coordinate mismatch '+v.id);} + const rawMemberships=graph.hyperedges.reduce((n,e)=>n+e.vertices.length,0); + return {vertices:graph.vertices.length,hyperedges:graph.hyperedges.length,rawMemberships,uniqueMemberships:expected.length,duplicateMemberships:rawMemberships-expected.length,sceneNodes:ids.length,sceneLinks:actual.length,maxArity:Math.max(0,...graph.hyperedges.map(e=>new Set(e.vertices).size)),emptyEdges:graph.hyperedges.filter(e=>!e.vertices.length).length,exactMemberships:true}; +} +function validateSnapshot(snapshot,scene){ + const nodes=new Map(snapshot.nodes.map(n=>[n.id,n])),links=new Map(snapshot.links.map(l=>[l.id,l])); + if(nodes.size!==scene.nodes.length||links.size!==scene.links.length)throw Error('Adapter topology count mismatch'); + for(const n of scene.nodes){const a=nodes.get(n.id);if(!a||a.x!==n.x||a.y!==n.y||a.z!==n.z)throw Error('Adapter coordinate/ID mismatch '+n.id);} + for(const l of scene.links){const a=links.get(l.id);if(!a||a.source!==l.source||a.target!==l.target)throw Error('Adapter incidence mismatch '+l.id);} + return {nodes:nodes.size,links:links.size,exactIDsCoordinatesAndMemberships:true,topologySha256:sha(canonical({nodes:[...nodes.keys()].sort(),links:[...links.values()].sort((a,b)=>a.id.localeCompare(b.id))}))}; +} +let randomState=seed>>>0;function random(){randomState=(Math.imul(1664525,randomState)+1013904223)>>>0;return randomState/2**32;} +function shuffle(a){for(let i=a.length-1;i>0;i--){const j=Math.floor(random()*(i+1));[a[i],a[j]]=[a[j],a[i]];}return a;} +const bundle=await build({entryPoints:[path.join(here,'client.js')],bundle:true,write:false,format:'iife',platform:'browser'}); +const html='
'; +const server=http.createServer((req,res)=>{res.setHeader('Content-Type',req.url==='/bundle.js'?'text/javascript':'text/html');res.end(req.url==='/bundle.js'?bundle.outputFiles[0].contents:html);}); +await new Promise(r=>server.listen(0,'127.0.0.1',r)); +const url='http://127.0.0.1:'+server.address().port; +const launchArgs=['--disable-background-timer-throttling','--disable-renderer-backgrounding','--disable-backgrounding-occluded-windows','--window-size=1920,1080']; +if(args.includes('--swiftshader'))launchArgs.push('--use-angle=swiftshader','--enable-unsafe-swiftshader'); +const browser=await chromium.launch({headless:!headed,...(executable?{executablePath:executable}:{}),args:launchArgs}); +const context=await browser.newContext({viewport:{width:1920,height:1080},deviceScaleFactor:1}); +await context.addInitScript(()=>{window.__glContexts=[];const original=HTMLCanvasElement.prototype.getContext;HTMLCanvasElement.prototype.getContext=function(type,...rest){const result=original.call(this,type,...rest);if(result&&/^(webgl2?|experimental-webgl)$/.test(type)&&!window.__glContexts.includes(result))window.__glContexts.push(result);return result;};}); +const packageJSON=JSON.parse(await fs.readFile(path.join(here,'package.json'))); +const session=await browser.newBrowserCDPSession(); +const [browserInfo,systemInfo]=await Promise.all([session.send('Browser.getVersion'),session.send('SystemInfo.getInfo')]); +const lock=await fs.readFile(path.join(here,'package-lock.json')); +const report={kind:validateOnly?'untimed-adapter-validation':'browser-frozen-incidence-benchmark',startedUTC:new Date().toISOString(),environment:{os:os.type(),release:os.release(),arch:os.arch(),cpus:os.cpus().map(c=>c.model),ramBytes:os.totalmem(),node:process.version,browser:browserInfo,gpu:systemInfo.gpu,headed,executable:executable||chromium.executablePath(),launchArgs,viewport:{width:1920,height:1080,dpr:1},packageVersions:{...packageJSON.dependencies,...packageJSON.devDependencies},lockfileSha256:sha(lock),powerThermalAndBackgroundLoad:process.env.BENCHMARK_CONDITIONS||'unrecorded; set BENCHMARK_CONDITIONS before publishing',vsync:'browser default; rAF cadence recorded, presentation unverified'},protocol:{seed,warmup,samples,repetitions,modes,jobTimeoutMs,order:'seeded Fisher-Yates over tool/dataset/mode for each repetition',gpuDuration:'not measured',memory:'Peak browser/GPU memory not measured; host total RAM is environment capacity only',inputLatency:'two-rAF scripted update surrogate, not human input or presentation latency',twoDvsThreeD:'Cytoscape and Sigma XY projection differs from 3D sphere and perspective rendering'},datasets:{},runs:[],unavailable:[{tool:'HNX Widget',reason:'Unsupported matched frozen incidence track: official 58e795d6a362dcd3bbd9ccff462052956a40f4f8 src/HypernetxWidgetView.js pos input fixes vertex parent nodes, not hyperedge hubs; public renderer runs charge/link/center/collide/bound simulation. Notebook wrapper also omits isolated vertices. See README capability probe. Native group-exploration remains a separate track'}]}; +function distribution(a){const sorted=[...a].sort((x,y)=>x-y);const p=q=>sorted[Math.min(sorted.length-1,Math.floor(q*(sorted.length-1)))];return {p50Ms:p(.5),p95Ms:p(.95),p99Ms:p(.99),meanMs:a.reduce((x,y)=>x+y,0)/a.length,maxMs:sorted.at(-1)};} +const scenes=new Map(); +try{ + for(const name of datasets){ + const graphPath=path.join(root,'target/benchmark/datasets',name+'.json'),scenePath=graphPath.replace(/\.json$/,'.incidence.json'); + const [graphBytes,sceneBytes]=await Promise.all([fs.readFile(graphPath),fs.readFile(scenePath)]); + const graph=JSON.parse(graphBytes),scene=JSON.parse(sceneBytes); + const changedPath=path.join(root,'target/benchmark/datasets',name+'.updated.incidence.json'); + let changed=null,changedSha=null,changedGraphSha=null,changedCounts=null;try{const bytes=await fs.readFile(changedPath);changed=JSON.parse(bytes);changedSha=sha(bytes);const updatedBytes=await fs.readFile(graphPath.replace(/\.json$/,'.updated.json'));changedGraphSha=sha(updatedBytes);changedCounts=assertions(JSON.parse(updatedBytes),changed);}catch(e){if(e.code!=='ENOENT')throw e;} + report.datasets[name]={graphSha256:sha(graphBytes),incidenceSha256:sha(sceneBytes),...assertions(graph,scene),updatedIncidenceSha256:changedSha,updatedGraphSha256:changedGraphSha,updatedCounts:changedCounts,update:changed?'canonical shared updated incidence fixture':'Unavailable: canonical shared update fixture absent'}; + scenes.set(name,{scene,changed}); + } + const order=[];for(let repetition=0;repetition<(validateOnly?1:repetitions);repetition++){ + const batch=[];for(const dataset of datasets)for(const tool of tools)for(const mode of (validateOnly?['static']:modes))batch.push({dataset,tool,mode,repetition}); + order.push(...shuffle(batch)); + } + report.order=order; + for(const job of order){ + const page=await context.newPage();let watchdog;let stage='load'; + try { const execute=async()=>{const errors=[];page.on('pageerror',e=>errors.push(String(e)));await page.goto(url);await page.waitForFunction(()=>window.benchmark); + const {scene,changed}=scenes.get(job.dataset);const start=performance.now(); + stage='setup';const initialized=await page.evaluate(({tool,scene})=>window.benchmark.setup(tool,scene),{tool:job.tool,scene});const loadWallMs=performance.now()-start; + stage='adapter validation';const topology=validateSnapshot(initialized.topology,scene); + const graphics=await page.evaluate(()=>window.benchmark.graphics()); + const run={...job,status:'completed',framing:initialized.framing,validation:topology,settings:initialized.settings,kind:initialized.kind,graphics,errors}; + if(!validateOnly){stage='timed sampling';run.loadWallMs=loadWallMs;run.trace=await page.evaluate(config=>window.benchmark.sample(config),{mode:job.mode,warmup,samples});run.summary={javascriptCommand:distribution(run.trace.commandMs),rafWallInterval:distribution(run.trace.rafIntervalMs),averageThroughputFPS:run.trace.averageThroughputFPS};} + if(changed){stage='incremental update';const updated=await page.evaluate(({scene,measure})=>window.benchmark.update(scene,measure),{scene:changed,measure:!validateOnly}); + run.incremental={...updated,topology:validateSnapshot(updated.topology,changed),unchangedNodeIDs:canonical(scene.nodes.map(n=>n.id).sort())===canonical(changed.nodes.map(n=>n.id).sort()),nodeCountDelta:changed.nodes.length-scene.nodes.length,linkCountDelta:changed.links.length-scene.links.length};} + // Pixel readback outside timed sampling, once per tool/dataset only. + stage='screenshot outside timing';if(job.repetition===0&&(job.mode==='static'||!modes.includes('static'))){const screenshot=path.join(output,`${job.dataset}-${job.tool}.png`);await page.screenshot({path:screenshot});run.screenshot=path.relative(root,screenshot);} + if(errors.length)throw Error('Page errors '+errors.join('\n')); + report.runs.push(run); + await page.evaluate(()=>window.benchmark.dispose()); + console.log(`${validateOnly?'Validated':'Measured'} ${job.tool} ${job.dataset} ${job.mode} repetition ${job.repetition+1}`); + }; + await Promise.race([execute(),new Promise((_,reject)=>{watchdog=setTimeout(()=>reject(Error('Job watchdog exceeded '+jobTimeoutMs+' ms')),jobTimeoutMs);})]); + } catch(error){report.runs.push({...job,status:'failed',stage,error:String(error)});console.error(`Failed ${job.tool} ${job.dataset} ${stage}: ${error}`);process.exitCode=1;} + finally{clearTimeout(watchdog);await page.close().catch(()=>{});await fs.writeFile(path.join(output,validateOnly?'validation.json':'results.json'),JSON.stringify(report,null,2));} + } + console.log('Evidence: '+path.join(output,validateOnly?'validation.json':'results.json')); +} finally{await browser.close();server.closeAllConnections();server.close();stop();} diff --git a/scripts/benchmarks/consolidate_browser.py b/scripts/benchmarks/consolidate_browser.py new file mode 100644 index 0000000..8b5b30e --- /dev/null +++ b/scripts/benchmarks/consolidate_browser.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +"""Replace the discarded Sigma refresh track with its corrected redraw runs.""" +import argparse +import hashlib +import json +from pathlib import Path + + +def key(row): + return tuple(row[k] for k in ('dataset', 'tool', 'mode', 'repetition')) + + +def consolidate(original, replacement): + old = json.loads(original.read_text()) + new = json.loads(replacement.read_text()) + for field in ('os', 'release', 'arch', 'ramBytes', 'node', 'browser', 'headed', 'viewport', 'packageVersions', 'lockfileSha256'): + assert old['environment'][field] == new['environment'][field], f'Environment changed: {field}' + for field in ('seed', 'warmup', 'samples', 'repetitions', 'modes', 'jobTimeoutMs'): + assert old['protocol'][field] == new['protocol'][field], f'Protocol changed: {field}' + assert old['datasets'] == new['datasets'], 'Input hashes/provenance changed' + expected = {key(r) for r in old['runs'] if r['tool'] == 'sigma'} + actual = {key(r) for r in new['runs']} + assert len(new['runs']) == len(actual) == len(expected) == 35 + assert expected == actual + assert all(r['tool'] == 'sigma' for r in new['runs']) + old['runs'] = [r for r in old['runs'] if r['tool'] != 'sigma'] + new['runs'] + assert len(old['runs']) == len({key(r) for r in old['runs']}) == 105 + old['consolidation'] = {'reason': 'Sigma full refresh reprocessed unchanged graph data during camera motion. Replaced with public scheduleRender cached redraw; original Sigma timings excluded.', + 'original_report_sha256': hashlib.sha256(original.read_bytes()).hexdigest(), + 'replacement_report_sha256': hashlib.sha256(replacement.read_bytes()).hexdigest(), + 'replacement_started_utc': new['startedUTC'], + 'replacement_environment': new['environment'], + 'replacement_order': new['order'], + 'order_limit': 'Corrected Sigma ran in a separate block after native work; original mixed-tool order applies only to retained Cytoscape/3D cases.'} + return old + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--original', type=Path, default=Path('target/benchmark/browser-original/results.json')) + parser.add_argument('--replacement', type=Path, default=Path('target/benchmark/browser-sigma-final/results.json')) + parser.add_argument('--output', type=Path, default=Path('target/benchmark/browser-final/results.json')) + args = parser.parse_args() + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(json.dumps(consolidate(args.original, args.replacement), indent=2) + '\n') diff --git a/scripts/benchmarks/datasets.py b/scripts/benchmarks/datasets.py new file mode 100644 index 0000000..ebcbeed --- /dev/null +++ b/scripts/benchmarks/datasets.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +"""Seeded canonical fixtures and lossless incidence projections (stdlib only).""" +import copy +import hashlib +import json +import math +from pathlib import Path +import random + +SEED = 20261003 + +def positions(graph): + rng = random.Random(SEED) + for node in graph['vertices']: + node.setdefault('attrs', {})['position'] = [round(rng.uniform(-500, 500), 6) for _ in range(3)] + graph.setdefault('meta', {}).setdefault('attrs', {})['benchmark_seed'] = SEED + return graph + +def synthetic(name, n, m, arity, fixed=False): + rng = random.Random(SEED) + vertices = [{'id': f'n{i}', 'label': f'Node {i}', 'attrs': {'category': i % 5, 'nested': {'source': name}}} for i in range(n)] + edges = [] + for i in range(m): + k = min(n, arity if fixed else [2, 3, 4, arity, arity * 2][i % 5]) + members = rng.sample(range(n), k) + edges.append({'id': f'g{i}', 'vertices': [f'n{j}' for j in members], 'attrs': {'category': i % 5}}) + return positions({'version': 'hypergraph.v1', 'meta': {'id': name, 'title': name, 'attrs': {'license': 'CC0-1.0'}}, 'vertices': vertices, 'hyperedges': edges}) + +def hif(graph): + def attributes(record): + # Retain all native display metadata as explicit attributes. + return {**record.get('attrs', {}), **{k: record[k] for k in ('label', 'kind', 'status') if k in record}} + return {'network-type': 'undirected', 'metadata': graph.get('meta', {}), + 'nodes': [{'node': v['id'], 'attrs': attributes(v), **({'weight': v['weight']} if 'weight' in v else {})} for v in graph['vertices']], + 'edges': [{'edge': e['id'], 'attrs': attributes(e), **({'weight': e['weight']} if 'weight' in e else {})} for e in graph['hyperedges']], + 'incidences': [{'node': v, 'edge': e['id']} for e in graph['hyperedges'] for v in e['vertices']]} + +def incidence(graph): + points = {v['id']: v['attrs']['position'] for v in graph['vertices']} + nodes = [{'id': 'v:' + v['id'], 'role': 'vertex', 'original_id': v['id'], 'x': points[v['id']][0], 'y': points[v['id']][1], 'z': points[v['id']][2], 'attrs': v.get('attrs', {})} for v in graph['vertices']] + links = [] + for e in graph['hyperedges']: + members = e['vertices'] + p = [sum(points[v][axis] for v in members) / len(members) if members else 0 for axis in range(3)] + nodes.append({'id': 'e:' + e['id'], 'role': 'hyperedge', 'original_id': e['id'], 'x': p[0], 'y': p[1], 'z': p[2], 'attrs': e.get('attrs', {})}) + links.extend({'id': e['id'] + ':' + str(i), 'source': 'e:' + e['id'], 'target': 'v:' + v, 'edge': e['id'], 'node': v} for i, v in enumerate(members)) + return {'nodes': nodes, 'links': links, 'meta': graph.get('meta', {})} + +def changed(graph): + result = copy.deepcopy(graph) + all_ids = [v['id'] for v in result['vertices']] + for e in result['hyperedges'][:math.ceil(len(result['hyperedges']) * .01)]: + members = e['vertices'] + replacement = next((v for v in all_ids if v not in members), None) + if replacement is not None: + if members: + members[0] = replacement + else: + members.append(replacement) + else: + members.pop() + return result + +def from_hif(doc): + edges = {e['edge']: {'id': 'g:' + str(e['edge']), 'vertices': [], 'attrs': e.get('attrs', {})} for e in doc['edges']} + for i in doc['incidences']: + edges[i['edge']]['vertices'].append('n:' + str(i['node'])) + return positions({'version': 'hypergraph.v1', 'meta': {'id': 'diseasome', 'title': 'Diseasome', 'attrs': {'license': 'CC-BY-4.0', 'source': 'https://doi.org/10.5281/zenodo.21909416', 'source_metadata': doc.get('metadata', {})}}, 'vertices': [{'id': 'n:' + str(v['node']), 'attrs': v.get('attrs', {})} for v in doc['nodes']], 'hyperedges': list(edges.values())}) + +def write_suite(directory, source): + directory.mkdir(parents=True, exist_ok=True) + graphs = {name: synthetic(name, n, m, a, fixed) for name, n, m, a, fixed in [('correctness', 30, 15, 4, False), ('moderate', 1000, 2000, 8, False), ('large', 10000, 10000, 8, False), ('high-arity-10', 1000, 200, 10, True), ('high-arity-100', 1000, 200, 100, True), ('high-arity-1000', 1000, 200, 1000, True)]} + fixture = graphs['correctness'] + fixture['hyperedges'][0]['vertices'] = [] + fixture['hyperedges'][1]['vertices'] = ['n0'] + for e in fixture['hyperedges']: + e['vertices'] = [v for v in e['vertices'] if v != 'n29'] + if (source / 'diseasome.json').exists(): + graphs['scientific'] = from_hif(json.loads((source / 'diseasome.json').read_text())) + if (source / 'mathlib.json').exists(): + graphs['mathlib'] = positions(json.loads((source / 'mathlib.json').read_text())) + manifest = [] + for name, graph in graphs.items(): + artifacts = {'': graph, '.hif': hif(graph), '.incidence': incidence(graph), '.updated': changed(graph), '.updated.incidence': incidence(changed(graph))} + hashes = {} + for suffix, data in artifacts.items(): + raw = (json.dumps(data, separators=(',', ':'), ensure_ascii=False) + '\n').encode() + filename = name + suffix + '.json' + (directory / filename).write_bytes(raw) + hashes[filename] = hashlib.sha256(raw).hexdigest() + manifest.append({'dataset': name, 'vertices': len(graph['vertices']), 'hyperedges': len(graph['hyperedges']), 'memberships': sum(len(e['vertices']) for e in graph['hyperedges']), 'maximum_arity': max(map(lambda e: len(e['vertices']), graph['hyperedges'])), 'empty_edges': sum(not e['vertices'] for e in graph['hyperedges']), 'isolated_nodes': len(set(v['id'] for v in graph['vertices']) - set(v for e in graph['hyperedges'] for v in e['vertices'])), 'provenance': graph.get('meta'), 'sha256': hashes}) + (directory / 'manifest.json').write_text(json.dumps(manifest, indent=2) + '\n') + +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('--output', type=Path, default=Path('target/benchmark/datasets')) + parser.add_argument('--source', type=Path, default=Path('target/benchmark/source')) + args = parser.parse_args() + write_suite(args.output, args.source) diff --git a/scripts/benchmarks/desktop/README.md b/scripts/benchmarks/desktop/README.md new file mode 100644 index 0000000..2123246 --- /dev/null +++ b/scripts/benchmarks/desktop/README.md @@ -0,0 +1,110 @@ +# Desktop benchmark preparation + +These adapters prepare inputs and instrumentation; preparation does not start timed workloads. + +## HyperGodot + +Source: https://github.com/ElsevierSoftwareX/SOFTX-D-25-00370 at +`49d03cd7540d2900cc8624048973ef430ff4e009`. Runtime prepared from Godot +4.7.2 stable universal macOS release. The upstream project requests Godot 4.4 +Forward Plus and disables vsync. This comparison is a 2D group viewer using +native hull drawing, distinct from the 3D incidence render track. +The downloaded runtime SHA-256 was verified as +`c58a24e31d720be9d62f60cb5627c4e695fb72f21b0cfe1bc9ccaa9a3b3ba63e`; +`--version` returns `4.7.2.stable.official.ed1daf0bf`. Headless source import and +two untimed initialization frames of the moderate dataset succeeded. No rendered +frame timings were collected during preparation. Direct `--check-only` on the +scene script omits project autoload globals; the actual project initialization +loads those globals and compiles successfully. + +```sh +git clone https://github.com/ElsevierSoftwareX/SOFTX-D-25-00370 /tmp/hyper-benchmark-competitors/hypergodot +git -C /tmp/hyper-benchmark-competitors/hypergodot checkout 49d03cd7540d2900cc8624048973ef430ff4e009 +python3 scripts/benchmarks/desktop/prepare_hypergodot.py --dataset target/benchmark/datasets/moderate.json --destination /tmp/hyper-benchmark-competitors/hypergodot-moderate +/path/to/Godot --headless --editor --path /tmp/hyper-benchmark-competitors/hypergodot-moderate --import --quit +``` + +Preparation copies upstream source, removes its missing and unused +`MaterialIconsDB` autoload, exports exact parser-compatible tagged text, and adds +the instrumentation scripts. The format is `n0 n1 #WEIGHT: 1 #GROUP baseline`; +the actual parser requires no colon after GROUP. Safe token IDs have a reversible +map; hyperedge IDs are line numbers in upstream. Isolated vertices, empty edges, +and duplicate memberships are rejected explicitly. Weights/groups are normalized +for this matched unweighted fixture and recorded as adaptations. + +After source import and harness validation, the root coordinator can execute: + +```sh +/tmp/hyper-benchmark-competitors/Godot.app/Contents/MacOS/Godot --path /tmp/hyper-benchmark-competitors/hypergodot-moderate --script res://benchmark_runner.gd -- --output /tmp/hyper-benchmark-competitors/hypergodot-moderate-results.json --warmup 120 --frames 300 --repetitions 5 +``` + +The harness overrides upstream startup layout while invoking production +`read_file`, `create_nodes`, `populate_edge_data`, sizing and drawing. It sets +canonical XY coordinates, pans/zooms a Camera2D, requests a 1920x1080 window +with stock HiDPI enabled, and records intervals between `_process` callbacks with the monotonic +clock. These include engine scheduling/render/presentation from the previous +frame and are not GPU duration. Upstream nodes, hulls, UI, labels and picking +remain; node radius 30, hull point count 4, edge width 1. Repetitions share one +process and receive a new warmup each. Window and viewport size, screen scale/DPI, +and the HiDPI setting are recorded. The final run verifies the actual render texture as 1920×1080; an earlier HiDPI-disabled run was clamped to 1920×917 and is excluded. reject matched-render comparison if actual +pixel dimensions differ from the requested size. + +## Graphia + +Source: https://github.com/graphia-app/graphia at version 5.5 commit +`b8ad51e820192a6f22ab310ebd84282384327c10`. Stock headless interface: +https://graphia.app/guide/section5/2_headless.html . +The public macOS release is universal arm64/x86_64, passes deep/strict code +signature verification, and its SHA-256 is +`ed77be142e34f209b6d95e959bb2b3937eca4d9621239a5f0dcc7a892a98ea85`. +Native CLI help and an untimed correctness GraphML import/save succeeded with +exit code 0. This bundle only supplies the Cocoa Qt platform plugin; setting +`QT_QPA_PLATFORM=offscreen` fails. Use its supported stock headless mode with the +native Cocoa platform instead. + +```sh +python3 scripts/benchmarks/desktop/prepare_graphia.py --dataset target/benchmark/datasets/moderate.json --output /tmp/hyper-benchmark-competitors/graphia-inputs +/tmp/hyper-benchmark-competitors/Graphia.app/Contents/MacOS/Graphia --parameters /tmp/hyper-benchmark-competitors/graphia-inputs/moderate.parameters.json /tmp/hyper-benchmark-competitors/graphia-inputs/moderate.graphml +``` + +GraphML retains every canonical vertex and group as explicit incidence nodes. +XYZ values are attributes; the stock parser does not establish a common fixed +layout. Headless import/save time can be measured independently of interactive +rendering. Do not report it as rendering FPS. + +## HNX Widget + +Source https://github.com/pnnl/hypernetx-widget at +`58e795d6a362dcd3bbd9ccff462052956a40f4f8`; PyPI package `hnxwidget==0.1.1b3`. +Isolated environment is `/tmp/hyper-benchmark-competitors/widget-venv` with +`notebook==6.5.7`, `traitlets<5.10`, and `hypernetx==2.4.0`. Notebook 6.5.x is the +documented compatibility path. Construct `HypernetxWidget(incidence_mapping, +pos={vertex_id: [x, y]}, collapseNodes=False)` in a notebook cell. The wrapper +was smoke tested successfully with three vertices and two groups; installed +`ipywidgets==7.8.5` provides the compatible classic widget frontend. +The separate HNX 2.4.0 constructor fails with the resolved pandas 3.0.6 +dependency; direct membership dictionary input avoids that constructor and is +supported by the widget's production API. +The wrapper +derives nodes from memberships, so isolated vertices are not preserved. Source +inspection shows supplied `pos` does not fix all bipartite hyperedge hubs and +forces continue; this belongs in the group exploration track, not the fully +controlled common-coordinate incidence track without further adaptation. + +## HGPolyVis + +https://github.com/peterdanieloliver/HGPolyVis at +`ba8868b1b002be5c32fb1e83548778fd7feed186` contains Windows 11 executable/Qt5 +DLLs, datasets and README, without buildable application source. Upstream states +no other supported OS. It requires Windows for a valid native benchmark, and is +unavailable on the tested macOS. CSV import rows are +`hyperedge_id,member_id,member_id,...`; `.er` supports saved positions/colors. + +## Julia + +Optional Julia 1.13.1 macOS arm64 runtime was extracted and `--version` verified from official +https://julialang-s3.julialang.org/bin/mac/aarch64/1.13/julia-1.13.1-macaarch64.tar.gz +(expected SHA-256 `a3e0259d4777c2b776c2cba134d5b6c6c124048dcb913f8881517892c49a0f7f`). +The downloaded SHA-256 matched the expected value. The executable is +`/tmp/hyper-benchmark-competitors/julia-1.13.1/bin/julia`. +SimpleHypergraphs.jl dependency installation and measurements remain pending. diff --git a/scripts/benchmarks/desktop/hypergodot_runner.gd b/scripts/benchmarks/desktop/hypergodot_runner.gd new file mode 100644 index 0000000..47d464c --- /dev/null +++ b/scripts/benchmarks/desktop/hypergodot_runner.gd @@ -0,0 +1,6 @@ +extends SceneTree + +func _initialize(): + var scene = load("res://main.tscn").instantiate() + scene.set_script(load("res://benchmark_scene.gd")) + root.add_child(scene) diff --git a/scripts/benchmarks/desktop/hypergodot_scene.gd b/scripts/benchmarks/desktop/hypergodot_scene.gd new file mode 100644 index 0000000..737713e --- /dev/null +++ b/scripts/benchmarks/desktop/hypergodot_scene.gd @@ -0,0 +1,128 @@ +# External benchmark instrumentation; upstream production drawing is inherited. +extends "res://main.gd" + +var samples: Array = [] +var repetitions: Array = [] +var frame_index = 0 +var repetition = 0 +var previous_usec = 0 +var benchmark_camera: Camera2D +var center = Vector2.ZERO +var base_zoom = 1.0 +var output_path = "res://benchmark_results.json" +var warmup_frames = 120 +var measured_frames = 300 +var repeat_count = 5 +var startup_ms = 0.0 +var exact_memberships = false + +func _ready(): + seed(20261003) + var started = Time.get_ticks_usec() + var args = OS.get_cmdline_user_args() + for index in range(args.size() - 1): + match args[index]: + "--output": output_path = args[index + 1] + "--warmup": warmup_frames = int(args[index + 1]) + "--frames": measured_frames = int(args[index + 1]) + "--repetitions": repeat_count = int(args[index + 1]) + var graph = JSON.parse_string(FileAccess.get_file_as_string("res://benchmark_input.json")) + var ids = JSON.parse_string(FileAccess.get_file_as_string("res://benchmark_idmap.json")) + var data = read_file("res://patterns.txt") + if data["nodes"].size() != graph["vertices"].size() or data["edges"].size() != graph["hyperedges"].size(): + push_error("Parser changed canonical node/group counts") + get_tree().quit(3) + return + for i in range(graph["hyperedges"].size()): + var expected: Array = [] + for member in graph["hyperedges"][i]["vertices"]: + expected.append(ids[member]) + expected.sort() + var actual: Array = Array(data["edges"][i]["nodes"]) + actual.sort() + if actual != expected: + push_error("Parser changed memberships for group " + str(i)) + get_tree().quit(3) + return + create_nodes(data["nodes"]) + populate_edge_data(data) + if get_tree().get_nodes_in_group("edges").size() != graph["hyperedges"].size(): + push_error("Renderer omitted hyperedges") + get_tree().quit(3) + return + exact_memberships = true + var low = Vector2(INF, INF) + var high = Vector2(-INF, -INF) + for vertex in graph["vertices"]: + var p = vertex["attrs"]["position"] + var point = Vector2(float(p[0]), float(p[1])) + get_node(ids[vertex["id"]]).position = point + low = low.min(point) + high = high.max(point) + calculate_centrality_and_resize_nodes(data) + for item in get_children(): + if item.has_method("mark_dirty"): + item.mark_dirty() + center = (low + high) * 0.5 + base_zoom = min(1920.0 / (high.x - low.x + 200.0), 1080.0 / (high.y - low.y + 200.0)) * 0.8 + benchmark_camera = Camera2D.new() + add_child(benchmark_camera) + benchmark_camera.position = center + benchmark_camera.zoom = Vector2.ONE * base_zoom + benchmark_camera.make_current() + DisplayServer.window_set_size(Vector2i(1920, 1080)) + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) + startup_ms = float(Time.get_ticks_usec() - started) / 1000.0 + previous_usec = Time.get_ticks_usec() + +func _process(_delta): + var now = Time.get_ticks_usec() + if frame_index >= warmup_frames: + samples.append(float(now - previous_usec) / 1000.0) + previous_usec = now + # Camera animation changes presentation without recomputing membership hulls. + var t = float(frame_index) * 0.015 + benchmark_camera.position = center + Vector2(sin(t) * 30.0, cos(t) * 20.0) + benchmark_camera.zoom = Vector2.ONE * base_zoom * (1.0 + sin(t * 0.4) * 0.08) + frame_index += 1 + if samples.size() >= measured_frames: + repetitions.append({"repetition": repetition, "frame_ms": samples.duplicate()}) + repetition += 1 + samples.clear() + frame_index = 0 + if repetition >= repeat_count: + finish_benchmark() + +func finish_benchmark(): + var manifest = JSON.parse_string(FileAccess.get_file_as_string("res://benchmark_manifest.json")) + manifest["godot_version"] = Engine.get_version_info() + manifest["renderer"] = RenderingServer.get_current_rendering_method() + manifest["renderer_driver"] = RenderingServer.get_current_rendering_driver_name() + var window_size = DisplayServer.window_get_size() + var screen_size = DisplayServer.screen_get_size() + manifest["window_size"] = [window_size.x, window_size.y] + manifest["screen_size"] = [screen_size.x, screen_size.y] + manifest["window_mode"] = DisplayServer.window_get_mode() + var viewport_size = get_viewport_rect().size + manifest["viewport_size"] = [viewport_size.x, viewport_size.y] + var render_size = get_viewport().get_texture().get_size() + manifest["render_target_size"] = [render_size.x, render_size.y] + manifest["matched_requested_viewport"] = render_size == Vector2(1920, 1080) + manifest["adaptations"].append("stock HiDPI enabled; physical viewport verified") + manifest["screen_scale"] = DisplayServer.screen_get_scale() + manifest["screen_dpi"] = DisplayServer.screen_get_dpi() + manifest["allow_hidpi"] = ProjectSettings.get_setting("display/window/dpi/allow_hidpi", true) + manifest["warmup_frames"] = warmup_frames + manifest["measured_frames"] = measured_frames + manifest["startup_construction_ms"] = startup_ms + manifest["exact_parser_memberships_and_rendered_group_count"] = exact_memberships + manifest["color_seed"] = 20261003 + manifest["repetitions"] = repetitions + var output = FileAccess.open(output_path, FileAccess.WRITE) + if output == null: + push_error("Cannot write benchmark output: " + output_path) + get_tree().quit(2) + return + output.store_string(JSON.stringify(manifest, "\t")) + output.close() + get_tree().quit() diff --git a/scripts/benchmarks/desktop/prepare_graphia.py b/scripts/benchmarks/desktop/prepare_graphia.py new file mode 100644 index 0000000..662aecd --- /dev/null +++ b/scripts/benchmarks/desktop/prepare_graphia.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Export a lossless incidence GraphML and stock Graphia headless parameters.""" +import argparse +import hashlib +import json +from pathlib import Path +import sys +import xml.etree.ElementTree as ET + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from datasets import incidence + +def prepare(dataset, directory): + graph = json.loads(dataset.read_text()) + projected = incidence(graph) + ns = 'http://graphml.graphdrawing.org/xmlns' + ET.register_namespace('', ns) + root = ET.Element(f'{{{ns}}}graphml') + for key in ['role', 'original_id', 'attrs', 'x', 'y', 'z']: + ET.SubElement(root, f'{{{ns}}}key', {'id': key, 'for': 'node', 'attr.name': key, 'attr.type': 'string'}) + g = ET.SubElement(root, f'{{{ns}}}graph', {'id': 'incidence', 'edgedefault': 'undirected'}) + for item in projected['nodes']: + node = ET.SubElement(g, f'{{{ns}}}node', {'id': item['id']}) + ET.SubElement(node, f'{{{ns}}}desc').text = item['id'] + for key in ['role', 'original_id', 'attrs', 'x', 'y', 'z']: + ET.SubElement(node, f'{{{ns}}}data', {'key': key}).text = json.dumps(item[key], ensure_ascii=False) + for link in projected['links']: + ET.SubElement(g, f'{{{ns}}}edge', {'id': link['id'], 'source': link['source'], 'target': link['target']}) + directory.mkdir(parents=True, exist_ok=True) + name = dataset.stem + target = directory / f'{name}.graphml' + ET.ElementTree(root).write(target, encoding='utf-8', xml_declaration=True) + params = directory / f'{name}.parameters.json' + params.write_text(json.dumps({'type': 'GraphML', 'destination': str((directory / f'{name}.graphia').resolve()), 'plugin': {'name': 'Generic', 'parameters': {}}}, indent=2)) + manifest = {'tool': 'Graphia', 'version': '5.5', 'commit': 'b8ad51e820192a6f22ab310ebd84282384327c10', 'dataset_sha256': hashlib.sha256(dataset.read_bytes()).hexdigest(), 'nodes': len(projected['nodes']), 'links': len(projected['links']), 'adaptation': 'incidence projection; isolated vertices and empty hyperedges retained as standalone nodes', 'coordinate_limit': 'XYZ stored as attributes; stock GraphML parser does not establish common-coordinate layout', 'metric': 'headless load/save wall time includes startup and processing, not render frame time'} + (directory / f'{name}.manifest.json').write_text(json.dumps(manifest, indent=2)) + print(json.dumps({'graphml': str(target.resolve()), 'parameters': str(params.resolve()), 'manifest': manifest})) + +if __name__ == '__main__': + p = argparse.ArgumentParser() + p.add_argument('--dataset', type=Path, required=True) + p.add_argument('--output', type=Path, required=True) + a = p.parse_args() + prepare(a.dataset, a.output) diff --git a/scripts/benchmarks/desktop/prepare_hypergodot.py b/scripts/benchmarks/desktop/prepare_hypergodot.py new file mode 100644 index 0000000..11019c6 --- /dev/null +++ b/scripts/benchmarks/desktop/prepare_hypergodot.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Prepare a disposable copy of pinned HyperGodot, without running it.""" +import argparse +import hashlib +import json +from pathlib import Path +import shutil +import subprocess + +HERE = Path(__file__).resolve().parent + +def prepare(source, dataset, destination): + graph = json.loads(dataset.read_text()) + ids = {v['id'] for v in graph['vertices']} + members = {v for e in graph['hyperedges'] for v in e['vertices']} + if ids != members or any(not e['vertices'] for e in graph['hyperedges']): + raise ValueError('Stock HyperGodot cannot represent isolated vertices or empty hyperedges; use a compatible dataset, not silent omission.') + if any(len(e['vertices']) != len(set(e['vertices'])) for e in graph['hyperedges']): + raise ValueError('Duplicate memberships require an explicit normalization policy.') + if any(set(e['vertices']) - ids for e in graph['hyperedges']): + raise ValueError('Unknown member ID') + if destination.exists(): + raise ValueError('Destination already exists; choose a fresh disposable copy.') + shutil.copytree(source, destination, ignore=shutil.ignore_patterns('.git', '.godot', 'Export', '.import')) + project = destination / 'project.godot' + project.write_text(project.read_text().replace('MaterialIconsDB="*res://addons/material-design-icons/icons/icons.gd"\n', '').replace('[display]\n', '[display]\n\nwindow/dpi/allow_hidpi=true\n')) + mapping = {v['id']: f'n{i}' for i, v in enumerate(graph['vertices'])} + # Safe token IDs preserve a reversible mapping even for IDs with spaces. + text = ''.join(' '.join(mapping[v] for v in e['vertices']) + ' #WEIGHT: 1 #GROUP baseline\n' for e in graph['hyperedges']) + (destination / 'patterns.txt').write_text(text) + (destination / 'benchmark_input.json').write_text(json.dumps(graph)) + (destination / 'benchmark_idmap.json').write_text(json.dumps(mapping)) + shutil.copy2(HERE / 'hypergodot_scene.gd', destination / 'benchmark_scene.gd') + shutil.copy2(HERE / 'hypergodot_runner.gd', destination / 'benchmark_runner.gd') + manifest = {'tool': 'HyperGodot', 'source_commit': subprocess.check_output(['git', '-C', str(source), 'rev-parse', 'HEAD'], text=True).strip(), + 'dataset': str(dataset.resolve()), 'sha256': hashlib.sha256(dataset.read_bytes()).hexdigest(), + 'vertices': len(ids), 'hyperedges': len(graph['hyperedges']), 'memberships': sum(len(e['vertices']) for e in graph['hyperedges']), + 'adaptations': ['remove missing unused MaterialIconsDB autoload', 'reversible safe token IDs and line-order hyperedge IDs', 'unit weights and one baseline group', 'fixed XY projection of canonical 3D positions', 'override startup layout; production read_file/create_nodes/populate_edge_data/drawing', 'scripted camera and wall-clock process-boundary frame intervals'], + 'node_radius': 30, 'edge_width': 1, 'point_count': 4, 'vsync': 'disabled by upstream project', 'metric': 'wall-clock frame intervals, not GPU timestamps'} + (destination / 'benchmark_manifest.json').write_text(json.dumps(manifest, indent=2)) + print(json.dumps({'project': str(destination.resolve()), 'manifest': manifest})) + +if __name__ == '__main__': + p = argparse.ArgumentParser() + p.add_argument('--source', type=Path, default=Path('/tmp/hyper-benchmark-competitors/hypergodot')) + p.add_argument('--dataset', type=Path, required=True) + p.add_argument('--destination', type=Path, required=True) + a = p.parse_args() + prepare(a.source, a.dataset, a.destination) diff --git a/scripts/benchmarks/desktop/run.py b/scripts/benchmarks/desktop/run.py new file mode 100644 index 0000000..cd797be --- /dev/null +++ b/scripts/benchmarks/desktop/run.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +"""Run installed desktop competitors sequentially, with bounded subprocesses.""" +import argparse +from collections import Counter +import gzip +import hashlib +import json +import math +import os +from pathlib import Path +import random +import re +import signal +import subprocess +import sys +import time + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from datasets import incidence +from prepare_graphia import prepare + + +def execute(command, timeout): + started = time.perf_counter() + process = subprocess.Popen(['/usr/bin/time', '-l', *command], stdout=subprocess.PIPE, + stderr=subprocess.PIPE, text=True, start_new_session=True) + try: + stdout, stderr = process.communicate(timeout=timeout) + status = 'ok' if process.returncode == 0 else 'failed' + except subprocess.TimeoutExpired: + os.killpg(process.pid, signal.SIGKILL) + stdout, stderr = process.communicate() + status = 'watchdog_timeout' + rss = re.search(r'(\d+)\s+maximum resident set size', stderr) + return {'status': status, 'returncode': process.returncode, + 'process_wall_seconds': time.perf_counter() - started, + 'process_peak_rss_bytes': int(rss.group(1)) if rss else None, + 'stdout': stdout[-3000:], 'stderr': stderr[-12000:]} + + +def validate_graphia(path, graph): + saved = json.loads(gzip.open(path, 'rb').read()) + assert saved[0]['appVersion'] == '5.5', 'Graphia version differs from pinned run' + document = saved[1] + names = {str(n['id']): n['value'] for n in document['nodeNames']} + expected = incidence(graph) + assert len(names) == len(expected['nodes']) + assert len(set(names.values())) == len(names) + assert set(names.values()) == {n['id'] for n in expected['nodes']} + pairs = Counter((names[e['source']], names[e['target']]) for e in document['graph']['edges']) + assert pairs == Counter((e['source'], e['target']) for e in expected['links']) + data = document['userNodeData'] + columns = {key: value['values'] for vector in data['vectors'] for key, value in vector.items()} + decoded = {names[str(native_id)]: {key: json.loads(columns[key][i]) for key in ('role', 'original_id', 'attrs')} + for i, native_id in enumerate(data['ids'])} + assert decoded == {n['id']: {key: n[key] for key in ('role', 'original_id', 'attrs')} for n in expected['nodes']} + by_id = {n['id']: n for n in expected['nodes']} + for i, native_id in enumerate(data['ids']): + for axis in ('x', 'y', 'z'): + assert math.isclose(float(columns[axis][i]), by_id[names[str(native_id)]][axis], rel_tol=1e-9, abs_tol=1e-6) + return {'exact_incidence_and_node_attributes': True, + 'document_metadata_retained': False, + 'typed_native_display_fields_retained': False, + 'saved_as_directed': document['graph']['directed'], + 'coordinates_are_attributes_not_layout': True, + 'coordinate_attributes_retained': True, + 'vertices_and_hubs': len(names), 'memberships': sum(pairs.values())} + + +def main(args): + output = args.output.resolve() + output.mkdir(parents=True, exist_ok=True) + rows = [] + if args.tool == 'graphia': + sources = {} + for dataset in args.datasets.split(','): + source = Path('target/benchmark/datasets') / (dataset + '.json') + prepare(source, output / 'inputs') + sources[dataset] = (source, json.loads(source.read_text())) + schedule = [] + rng = random.Random(20261003) + for repetition in range(-1, 5): + batch = list(sources) + rng.shuffle(batch) + schedule.extend((repetition, dataset) for dataset in batch) + for repetition, dataset in schedule: + source, graph = sources[dataset] + saved = output / 'inputs' / (dataset + '.graphia') + saved.unlink(missing_ok=True) + row = execute([args.executable, '--parameters', str(output / 'inputs' / (dataset + '.parameters.json')), + str(output / 'inputs' / (dataset + '.graphml'))], args.timeout) + row.update(tool='Graphia', dataset=dataset, repetition=repetition, warmup=repetition == -1, + dataset_sha256=hashlib.sha256(source.read_bytes()).hexdigest()) + if row['status'] == 'ok': + try: + row['correctness'] = validate_graphia(saved, graph) + except Exception as error: + row.update(status='validation_failed', validation_error=repr(error)) + rows.append(row) + (output / 'raw.json').write_text(json.dumps(rows, indent=2) + '\n') + print('Graphia', dataset, repetition, row['status'], flush=True) + else: + saved = output / 'moderate.json' + saved.unlink(missing_ok=True) + row = execute([args.executable, '--path', str(args.project.resolve()), '--script', 'res://benchmark_runner.gd', + '--', '--output', str(saved), '--warmup', '120', '--frames', '300', '--repetitions', '5'], args.timeout) + row.update(tool='HyperGodot', dataset='moderate', warmup=False) + if row['status'] == 'ok' and saved.exists(): + row['measurements'] = json.loads(saved.read_text()) + elif row['status'] == 'ok': + row.update(status='failed', reason='Process exited without evidence JSON') + rows.append(row) + (output / 'raw.json').write_text(json.dumps(rows, indent=2) + '\n') + print('HyperGodot', row['status'], flush=True) + metadata = {'tool': args.tool, 'executable': args.executable, 'timeout_seconds': args.timeout, + 'executable_sha256': hashlib.sha256(Path(args.executable).read_bytes()).hexdigest(), + 'conditions': os.environ.get('BENCHMARK_CONDITIONS', 'unrecorded'), + 'peak_memory': 'macOS /usr/bin/time -l whole-process RSS high-water in bytes', + 'fresh_process_per_repetition': args.tool == 'graphia', + 'repetitions': 5, 'seed': 20261003} + (output / 'metadata.json').write_text(json.dumps(metadata, indent=2) + '\n') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('tool', choices=['graphia', 'hypergodot']) + parser.add_argument('--executable', required=True) + parser.add_argument('--project', type=Path) + parser.add_argument('--datasets', default='moderate,scientific,mathlib') + parser.add_argument('--output', type=Path, required=True) + parser.add_argument('--timeout', type=int, default=180) + main(parser.parse_args()) diff --git a/scripts/benchmarks/report.py b/scripts/benchmarks/report.py new file mode 100644 index 0000000..bcb6eee --- /dev/null +++ b/scripts/benchmarks/report.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +"""Render comparison tables from measured summaries, retaining failure counts.""" +import argparse +import json +from pathlib import Path + + +def number(value, digits=2): + return '—' if value is None else f'{value:,.{digits}f}' + + +def table(lines, headings, rows): + lines += ['| ' + ' | '.join(headings) + ' |', '|' + '|'.join(['---'] * len(headings)) + '|'] + lines += ['| ' + ' | '.join(str(value) for value in row) + ' |' for row in rows] + lines.append('') + + +def render(rows): + lines = ['# Measured ecosystem comparison — October 3, 2026', '', + f'Exploratory results on an Apple M5 Pro / 24 GiB Mac: {sum(r["attempts"] for r in rows)} measured attempts across {len(rows)} configurations. Five attempts per case unless noted. ' + 'The same graph records and seeded coordinates are used through explicit adapters. ' + 'Scientific figures, 2D browser views and 3D native rendering have different work and are separate tracks.', '', + 'Read the [method and limitations](methodology.md), [environment](environment.json), ' + '[complete CSV](summary.csv), [structured summary](summary.json) and [raw evidence](evidence/). ' + 'The [harness guide](../../../scripts/benchmarks/README.md) provides pinned dependencies and rerun commands.', '', + '## Findings to use for roadmap decisions', '', + 'Model fidelity is the clearest differentiator in this run: exact group identity, isolated nodes and empty groups need explicit checks at adapter boundaries. ' + 'Rendering results are conditional on completion. Hyper had intermittent whole-job stalls, including one on a small fixture; their cause remains unresolved. ' + 'The source audit found a quadratic hub-status lookup worth profiling, but it has not isolated the cause of those stalls.', '', + 'Sigma camera measurements use its public cached redraw path. Earlier full-refresh camera measurements were discarded and replaced; ' + 'the diagnostic evidence is retained separately. Comparisons below keep representations and timing boundaries explicit.', '', + '## Scientific figure workflows', '', + 'Times exclude the separately measured top-level tool import, file I/O and JSON parsing. Setup creates the artists and includes any lazy drawing-module initialization; raster draws the Agg canvas. ' + 'PNG compression is a separate stage. A 120-second timeout covers the whole worker. ' + 'HGX uses its stock 12-round smoothing, HNX rubber-band outlines, XGI convex hulls.', ''] + scientific = [r for r in rows if r['track'] == 'scientific' and r['mode'] == 'plot' and r['dataset'] != 'correctness'] + table(lines, ['Dataset', 'Tool', 'Completed / attempts', 'Exact model records', 'Construct ms', 'Artist setup ms', 'Raster ms', 'PNG ms', 'Worker peak MiB'], + [[r['dataset'], r['tool'], f"{r['successful_runs']}/{r['attempts']}", 'yes' if r['exact_model_records'] else 'no / unavailable', + *[number(r.get(k)) for k in ['adapter_construct_ms','artist_setup_ms','agg_raster_draw_ms','png_export_ms','peak_rss_mib_median']]] for r in scientific]) + lines += ['XGI preserves all model records in every tested input. HNX omits isolated vertices and empty groups. ' + 'HGX identifies groups by member tuple and merges parallel groups: Diseasome becomes 481 groups instead of 903, ' + 'and the arity-1,000 fixture becomes one group instead of 200. Its lossy timings cannot rank equivalent work. ' + 'Original records stored in opaque metadata do not repair those native identities.', '', + '## Browser incidence rendering', '', + 'All incidences are rendered, forces and labels are off, and the camera moves. Values describe rAF callback cadence; ' + 'they are not GPU durations or verified presented frames. Average callbacks/s uses total elapsed intervals. ' + 'The p95 range spans per-run p95 values. “Completed” requires sampling and the shared update to finish within the 90-second job budget.', ''] + browser = [r for r in rows if r['track'] == 'browser'] + table(lines, ['Dataset', 'Tool', 'Completed / attempts', 'p50 ms', 'p95 ms', 'p95 run range ms', 'Avg callbacks/s', 'Update command ms', 'Failed stage'], + [[r['dataset'], r['tool'], f"{r['successful_runs']}/{r['attempts']}", number(r.get('frame_p50_ms')), number(r.get('frame_p95_ms')), + f"{number(r.get('frame_p95_run_min_ms'))}–{number(r.get('frame_p95_run_max_ms'))}", number(r.get('average_fps')), + number(r.get('update_commit_ms')), ', '.join(r.get('failure_stages',[])) or '—'] for r in browser]) + lines += ['Sigma uses WebGL 2D, Cytoscape uses Canvas2D, and 3d-force-graph uses perspective spheres and WebGL. ' + 'The corrected Sigma block and original Cytoscape block recorded different small-case callback cadences (roughly 60 versus 120 callbacks/s); display refresh was not locked, so their rate ratio cannot rank rendering efficiency. No link sampling is applied. Update command timings include different adapter commit strategies and sometimes unchanged-record work; see the method. A failed batch has no completed rendering score; its timeout is not a single-frame measurement.', '', + '## Hyper native renderer', '', + 'Physical 1920 × 1080, AutoVsync, 120 warmup and 300 recorded application frames. ' + 'HUD and picking remain installed. Basic modes disable labels; `native-interactive` enables the default Capped policy (count threshold 250, with selected/hovered exemptions). ' + 'Frozen modes use fixed coordinates; live modes perform one layout step per frame. ' + 'All lines are enabled. Hulls cap input at 24 vertices and therefore approximate large groups.', ''] + native = [r for r in rows if r['track'].startswith('native')] + table(lines, ['Track / dataset', 'Mode', 'Completed / attempts', 'p50 app interval ms', 'p95 app interval ms', 'p95 run range ms', 'Avg app frames/s', 'Update commit ms', 'Next app interval ms', 'Peak MiB'], + [[r['track'] + ' / ' + r['dataset'], r['mode'], f"{r['successful_runs']}/{r['attempts']}", + number(r.get('frame_p50_ms')), number(r.get('frame_p95_ms')), + f"{number(r.get('frame_p95_run_min_ms'))}–{number(r.get('frame_p95_run_max_ms'))}", + *[number(r.get(k)) for k in ['average_fps','update_commit_ms','update_next_frame_ms','peak_rss_mib_median']]] for r in native]) + lines += ['`native` is bipartite incidence rendering; `native-star` and `native-interactive` use group/star rendering. ' + 'Star draws pair lines for dyadic groups and hulls for larger groups; its scene has no spring links. Star without hulls is an ablation that hides larger groups, not a full-group rendering score. ' + 'Update commit and the next application interval exclude file-watch polling and parse/I/O; they are not input-to-visible latency. ' + 'Native and browser settings, geometry and vsync differ, so these tables do not establish a native-versus-browser speed ratio.', '', + '## Hyper parsing, conversion and geometry', '', + 'Fresh release processes, one discarded warmup and five measured runs. HIF schema validation is offline. ' + 'Solver timings use the bipartite scene; hull timings use the production 24-vertex cap. ' + 'Worker RSS includes multiple retained documents for correctness checks.', ''] + core = [r for r in rows if r['track'] == 'core'] + table(lines, ['Dataset', 'Native parse ms', 'HIF parse / validate ms', 'HIF conversion ms', 'Projection ms', 'All hulls CPU ms', 'Layout step p50 ms', 'Peak MiB'], + [[r['dataset'], *[number(r.get(k)) for k in ['native_parse_ms','hif_parse_validate_ms','hif_viewer_conversion_ms','bipartite_projection_ms','all_hulls_cpu_ms','layout_step_p50_ms','peak_rss_mib_median']]] for r in core]) + lines += ['## Desktop alternatives', '', + 'Graphia measures stock headless import/save including process startup, then independently checks saved memberships and node attributes. ' + 'The adapter retains incidence topology and arbitrary `attrs`, but omits document metadata and top-level native label/kind/status/weight fields; XYZ are attributes, and Graphia saves this graph as directed. ' + 'These are import workflow timings, not rendering FPS.', ''] + table(lines, ['Dataset', 'Tool', 'Completed / attempts', 'Import + save ms', 'Exact incidence records', 'Peak MiB'], + [[r['dataset'],r['tool'],f"{r['successful_runs']}/{r['attempts']}",number(r.get('import_save_wall_ms')), + 'yes' if r['exact_incidence_records'] else 'no / unavailable',number(r.get('peak_rss_mib_median'))] + for r in rows if r['track']=='desktop-import']) + lines += ['HyperGodot measures its production 2D group drawing with camera motion, fixed XY coordinates and stock labels/UI. ' + 'It disables vsync and uses different node/hull geometry. Its five repetitions share one process, with a new warmup each. ' + 'A disposable source copy removes a missing unused autoload. Original IDs are mapped to safe tokens/line-order group IDs; ' + 'weights and group categories are normalized for this fixture. Stock HiDPI is enabled and the physical render texture is verified at 1920 × 1080; earlier clamped-window timings are excluded.', ''] + table(lines, ['Dataset', 'Tool', 'Completed / attempts', 'p50 ms', 'p95 ms', 'Avg frames/s', 'Whole-session peak MiB', 'Render target pixels'], + [[r['dataset'],r['tool'],f"{r['successful_runs']}/{r['attempts']}",number(r.get('frame_p50_ms')),number(r.get('frame_p95_ms')), + number(r.get('average_fps')),number(r.get('whole_session_peak_rss_mib')),r.get('render_target_size')] + for r in rows if r['track']=='desktop-group']) + lines += ['## Coverage and remaining experiments', ''] + table(lines, ['Alternative', 'Measured scope / reason for no comparable score'], [ + ['HyperNetX, XGI, Hypergraphx','Model construction and scientific figure workflow; no GUI FPS claim'], + ['3d-force-graph, Cytoscape.js, Sigma','Frozen full-incidence camera motion and scripted update'], + ['HyperGodot','Native 2D group drawing on the moderate dataset'], + ['Graphia','Headless incidence import/save; common render coordinates not established'], + ['HNX Widget','Installation and dictionary-input smoke test; public position API does not freeze all hubs; isolated vertices omitted'], + ['HGPolyVis','Supplied Windows-only executable; requires a Windows run'], + ['SimpleHypergraphs.jl','Julia runtime verified; package timings not collected'], + ['PAOHVis','Needs a temporal dataset and temporal exploration task'], + ['Gephi','Outside this automated render harness; requires a separate plugin/task comparison'], + ['egui_graphs','Embedding framework reference; no matched application benchmark']]) + lines += ['Human task accuracy, dense-diagram readability, layout convergence, GPU completion, input-to-visible latency and ' + 'other machines remain unmeasured. Metadata retention does not establish that a viewer exposes or filters it.', '', + '## Roadmap implications', '', + '1. **Interoperability:** keep original HIF documents and stable group identities through scientific workflows. ' + 'The identity-loss cases demonstrate why an incidence adapter needs correctness gates before timing.', + '2. **Inspection and filtering:** expose provenance, arbitrary attributes and membership before expanding visual effects. ' + 'Rendering every relation does not establish readability or a successful scientific task.', + '3. **Responsiveness:** profile the confirmed quadratic hub-status scan in `animation::node_motion` first; the existing `GraphLayout::hub_status` cache offers a direct replacement. Then use the frozen/live split and update traces to separate solver, hull rebuild/upload and redraw costs. ' + 'Add a controlled input-to-visible study before advertising latency; keep hull approximation and line budgets explicit.', + '4. **Installation:** ship verified wheels and a clear separately installed viewer path. The widget and desktop packaging probes ' + 'show that dependency/version friction can block evaluation before performance matters.', '', + 'These priorities are interpretations of this exploratory run, not proof of adoption or a universal winner.', ''] + return '\n'.join(lines) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--directory', type=Path, default=Path('docs/benchmarks/ecosystem-2026-10-03')) + args = parser.parse_args() + (args.directory / 'README.md').write_text(render(json.loads((args.directory / 'summary.json').read_text()))) diff --git a/scripts/benchmarks/restore_evidence.py b/scripts/benchmarks/restore_evidence.py new file mode 100644 index 0000000..b78ed13 --- /dev/null +++ b/scripts/benchmarks/restore_evidence.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Restore archived observations for aggregation, without running benchmarks.""" +import argparse +import gzip +from pathlib import Path + +FILES = { + 'core/raw.json': 'core-raw.json', + 'native/raw.json': 'native-raw.json.gz', + 'native-star/raw.json': 'native-star-raw.json.gz', + 'native-interactive/raw.json': 'native-interactive-raw.json.gz', + 'science/final-raw.json': 'scientific-raw.json', + 'browser-final/results.json': 'browser-final-results.json.gz', + 'graphia/raw.json': 'graphia-raw.json', + 'hypergodot/raw.json': 'hypergodot-raw.json', +} + + +def restore(source, destination): + if destination.exists() and any(destination.iterdir()): + raise ValueError('Choose an empty destination to avoid replacing local runs.') + payloads = {} + for target, name in FILES.items(): + raw = (source / name).read_bytes() + payloads[target] = gzip.decompress(raw) if name.endswith('.gz') else raw + for target, payload in payloads.items(): + path = destination / target + path.parent.mkdir(parents=True,exist_ok=True) + path.write_bytes(payload) + print('Restored',len(payloads),'observation files to',destination) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--evidence',type=Path,default=Path('docs/benchmarks/ecosystem-2026-10-03/evidence')) + parser.add_argument('--output',type=Path,default=Path('target/benchmark/restored-evidence')) + args = parser.parse_args() + restore(args.evidence,args.output) diff --git a/scripts/benchmarks/run_local.py b/scripts/benchmarks/run_local.py new file mode 100644 index 0000000..a5f0b5a --- /dev/null +++ b/scripts/benchmarks/run_local.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +"""Sequential fresh-process Hyper benchmarks; never overlap with other tracks.""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import random +import re +import signal +import subprocess +import time + +def run(args): + output = Path(args.output) + output.mkdir(parents=True, exist_ok=True) + datasets = args.datasets.split(',') + rng = random.Random(20261003) + if args.track == 'core': + specs = [(name, False, False) for name in datasets] + else: + specs = [(name, hulls, False) for name in datasets for hulls in (False, True)] + specs += [(name, hulls, True) for name in args.live_datasets.split(',') if name in datasets for hulls in (False, True)] + if args.only_hulls: specs = [spec for spec in specs if spec[1]] + schedule = [] + for repetition in range(-1 if args.track == 'core' else 0, 5): + batch = list(specs) + rng.shuffle(batch) + schedule += [(repetition, *item) for item in batch] + rows = [] + for order, (repetition, dataset, hulls, live) in enumerate(schedule): + raw = output / f'{dataset}-hulls{int(hulls)}-live{int(live)}-{repetition}.json' + source = Path('target/benchmark/datasets') / (dataset + '.json') + executable = 'target/release/examples/ecosystem_core' if args.track == 'core' else 'target/release/ecosystem-benchmark' + command = ['/usr/bin/time', '-l', executable, str(source), str(raw)] + if args.track == 'native': + command += [str(int(hulls)), str(int(live)), '300', '-', args.projection, str(int(args.labels))] + started = time.time() + row = {'dataset': dataset, 'hulls': hulls, 'live': live, 'repetition': repetition, 'warmup': repetition == -1, 'order': order, 'started_unix': started, 'sha256': hashlib.sha256(source.read_bytes()).hexdigest()} + raw.unlink(missing_ok=True) + process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, start_new_session=True, env={**os.environ, 'HYPER_VIZ_SESSION': 'off'}) + try: + stdout, stderr = process.communicate(timeout=180) + row.update(status='ok' if process.returncode == 0 and raw.exists() else 'failed', returncode=process.returncode) + row['stderr'] = stderr[-12000:] + rss = re.search(r'(\d+)\s+maximum resident set size', stderr) + row['process_peak_rss_bytes'] = int(rss.group(1)) if rss else None + if raw.exists(): + row['measurements'] = json.loads(raw.read_text()) + except subprocess.TimeoutExpired: + os.killpg(process.pid, signal.SIGKILL) + stdout, stderr = process.communicate() + row.update(status='watchdog_timeout', timeout_seconds=180) + row['stderr'] = stderr[-12000:] + row['process_wall_seconds'] = time.time() - started + rows.append(row) + (output / 'raw.json').write_text(json.dumps(rows, indent=2) + '\n') + print(args.track, order + 1, '/', len(schedule), dataset, 'hulls', int(hulls), 'live', int(live), repetition, row['status'], flush=True) + metadata = {'track': args.track, 'seed': 20261003, 'repetitions': 5, 'fresh_process_per_run': True, 'warmup': '5 solver steps plus1 discarded run per dataset' if args.track == 'core' else '120 render frames per run', 'schedule': schedule, 'commit': subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip(), 'binary_sha256': hashlib.sha256(Path(executable).read_bytes()).hexdigest(), 'conditions': os.environ.get('BENCHMARK_CONDITIONS', 'unrecorded'), 'peak_memory': '/usr/bin/time -l process RSS high-water; bytes on macOS; includes startup', 'build_profile': 'release'} + metadata.update(projection=args.projection, labels=args.labels, only_hulls=args.only_hulls, + live_datasets=args.live_datasets.split(',')) + (output / 'metadata.json').write_text(json.dumps(metadata, indent=2) + '\n') + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('track', choices=['core', 'native']) + parser.add_argument('--datasets', default='moderate,large,high-arity-10,high-arity-100,high-arity-1000,scientific,mathlib') + parser.add_argument('--output', required=True) + parser.add_argument('--projection', choices=['bipartite','star'], default='bipartite') + parser.add_argument('--live-datasets', default='moderate') + parser.add_argument('--labels', action='store_true') + parser.add_argument('--only-hulls', action='store_true') + run(parser.parse_args()) diff --git a/scripts/benchmarks/scientific.py b/scripts/benchmarks/scientific.py new file mode 100644 index 0000000..05250d0 --- /dev/null +++ b/scripts/benchmarks/scientific.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 +"""Isolated scientific figure benchmark; canonical native JSON, never layout/FPS. + +Install PINNED in a dedicated Python 3.12 environment. --validate performs one +untimed fixture per tool. Batch mode requires --run; parent controls contention. +Each observation is a new process; imports/startup and correctness are excluded +from named stage timings, but included in process high-water RSS. PNG compression +uses an already rasterized RGBA buffer, so export does not redraw the figure. +""" +import argparse +import csv +import hashlib +import importlib.metadata +import json +import os +from pathlib import Path +import platform +import random +import resource +import subprocess +import sys +import time +import traceback + +PINNED = {'hypernetx': '2.4.3', 'xgi': '0.10.2', 'hypergraphx': '1.8.0', + 'matplotlib': '3.11.2', 'pillow': '12.3.0'} +TOOLS = ['hypernetx', 'xgi', 'hypergraphx'] +SOURCES = { + 'hypernetx': 'https://hypernetx.readthedocs.io/en/latest/classes/classes.html', + 'xgi': 'https://xgi.readthedocs.io/en/stable/api/core/xgi.core.hypergraph.Hypergraph.html', + 'hypergraphx': 'https://hypergraphx.readthedocs.io/en/latest/api/hypergraphx.core.html', +} + +def canonical(data): + nodes = {v['id']: v for v in data['vertices']} + edges = {e['id']: e for e in data['hyperedges']} + if len(nodes) != len(data['vertices']) or len(edges) != len(data['hyperedges']): + raise ValueError('Duplicate record IDs') + node_ids = set(nodes) + for e in edges.values(): + if len(set(e['vertices'])) != len(e['vertices']): + raise ValueError('Duplicate incidence in ' + e['id']) + if set(e['vertices']) - node_ids: + raise ValueError('Unknown member ID in ' + e['id']) + return nodes, edges + + +def construct(tool, data, records=None): + nodes, edges = records if records is not None else canonical(data) + if tool == 'hypernetx': + import hypernetx as hnx + graph = hnx.Hypergraph({k: e['vertices'] for k, e in edges.items()}, + node_properties={k: {'canonical': v} for k, v in nodes.items()}, + edge_properties={k: {'canonical': e} for k, e in edges.items()}) + graph.benchmark_meta = data.get('meta', {}) + elif tool == 'xgi': + import xgi + graph = xgi.Hypergraph() + graph['canonical_meta'] = data.get('meta', {}) + for k, v in nodes.items(): + graph.add_node(k, canonical=v) + for k, e in edges.items(): + # Use the public bulk API; actual empty-edge support is checked below. + graph.add_edges_from([(e['vertices'], k, {'canonical': e})]) + else: + from hypergraphx import Hypergraph + graph = Hypergraph(weighted=False, hypergraph_metadata={'canonical_meta': data.get('meta', {})}) + for k, v in nodes.items(): + graph.add_node(k, metadata={'canonical': v}) + for e in edges.values(): + graph.add_edge(tuple(set(e['vertices'])), metadata={'canonical': e}) + return graph + + +def check(tool, graph, data): + nodes, edges = canonical(data) + actual_nodes, actual_edges, node_meta, edge_meta = set(), {}, {}, {} + if tool == 'hypernetx': + actual_nodes = set(graph.nodes) + actual_edges = {k: set(v) for k, v in graph.incidence_dict.items()} + # Element .properties recursively flattens nested dictionaries; the + # public DataFrame view retains the original opaque JSON shape. + node_props, edge_props = graph.nodes.to_dataframe, graph.edges.to_dataframe + node_meta = {k: node_props.loc[k, 'misc_properties'].get('canonical') for k in actual_nodes} + edge_meta = {k: edge_props.loc[k, 'misc_properties'].get('canonical') for k in actual_edges} + meta = graph.benchmark_meta + elif tool == 'xgi': + actual_nodes = set(graph.nodes) + actual_edges = {k: set(graph.edges.members(k)) for k in graph.edges} + node_meta = {k: graph.nodes[k].get('canonical') for k in actual_nodes} + edge_meta = {k: graph.edges[k].get('canonical') for k in actual_edges} + meta = graph['canonical_meta'] + else: + actual_nodes = set(graph.get_nodes()) + node_meta = {k: graph.get_node_metadata(k).get('canonical') for k in actual_nodes} + for members in graph.get_edges(): + record = graph.get_edge_metadata(members).get('canonical', {}) + # HGX merges duplicate-edge metadata into lists. Recover opaque + # records while separately reporting the loss of native multiplicity. + for item in record if isinstance(record, list) else [record]: + key = item.get('id', repr(members)) + actual_edges[key] = set(members) + edge_meta[key] = item + meta = graph.get_hypergraph_metadata().get('canonical_meta') + missing_nodes = sorted(nodes.keys() - actual_nodes) + missing_edges = sorted(edges.keys() - actual_edges.keys()) + wrong_members = [k for k in edges.keys() & actual_edges.keys() + if set(edges[k]['vertices']) != actual_edges[k]] + bad_metadata = [k for k in nodes.keys() & actual_nodes if nodes[k] != node_meta[k]] + bad_metadata += [k for k in edges.keys() & actual_edges.keys() if edges[k] != edge_meta[k]] + extra_nodes = sorted(actual_nodes - nodes.keys()) + extra_edges = sorted(actual_edges.keys() - edges.keys()) + opaque_exact = not (missing_nodes or missing_edges or wrong_members or bad_metadata or extra_nodes or extra_edges) and meta == data.get('meta', {}) + native_edge_count = len(graph.get_edges()) if tool == 'hypergraphx' else len(actual_edges) + exact = opaque_exact and native_edge_count == len(edges) + return {'exact_canonical_roundtrip': exact, 'missing_nodes': missing_nodes, + 'opaque_record_values_preserved': opaque_exact, + 'native_hyperedge_count': native_edge_count, + 'native_multiplicity_preserved': native_edge_count == len(edges), + 'missing_edges': missing_edges, 'extra_nodes': extra_nodes, 'extra_edges': extra_edges, + 'wrong_memberships': wrong_members, 'wrong_metadata': bad_metadata, + 'graph_metadata_equal': meta == data.get('meta', {}), + 'native_edge_ids': tool != 'hypergraphx', + 'edge_id_storage': 'native IDs' if tool != 'hypergraphx' else 'canonical metadata; HGX uses member tuples', + 'empty_edges_expected': sum(not e['vertices'] for e in edges.values()), + 'isolated_nodes_expected': len(nodes.keys() - {m for e in edges.values() for m in e['vertices']}), + 'duplicate_member_occurrences': sum(len(e['vertices']) - len(set(e['vertices'])) for e in edges.values()), + 'membership_semantics': 'unordered unique-member sets; original record retained as opaque metadata'} + + +def figure(tool, graph, data): + import matplotlib + matplotlib.use('Agg') + import matplotlib.pyplot as plt + pos = {v['id']: v['attrs']['position'][:2] for v in data['vertices']} + fig, ax = plt.subplots(figsize=(19.2, 10.8), dpi=100) + if tool == 'hypernetx': + import hypernetx as hnx + hnx.draw(graph, pos=pos, ax=ax, with_node_labels=False, with_edge_labels=False, + fill_edges=True, fill_edge_alpha=-0.85) + elif tool == 'xgi': + import xgi + xgi.draw(graph, pos=pos, ax=ax, node_labels=False, hyperedge_labels=False, + hull=True, alpha=0.15) + else: + from hypergraphx.viz.draw_hypergraph import draw_hypergraph + draw_hypergraph(graph, pos=pos, ax=ax, with_node_labels=False, hyperedge_alpha=0.15) + xs, ys = zip(*pos.values()) + xpad = max(max(xs) - min(xs), 1) * 0.05 + ypad = max(max(ys) - min(ys), 1) * 0.05 + ax.set_xlim(min(xs)-xpad, max(xs)+xpad) + ax.set_ylim(min(ys)-ypad, max(ys)+ypad) + ax.set_aspect('equal', adjustable='box') + ax.axis('off') + return fig + + +def worker(args): + import faulthandler + faulthandler.dump_traceback_later(max(1, args.timeout - 10)) + result = {'tool': args.tool, 'dataset': Path(args.dataset).stem, + 'timed': not args.validate, 'status': 'failed', 'stages_ms': {}, + 'versions': {p: importlib.metadata.version(p) for p in PINNED}} + def stage(name, fn): + start = time.perf_counter_ns() + value = fn() + if not args.validate: + result['stages_ms'][name] = (time.perf_counter_ns() - start) / 1e6 + return value + try: + # Dependency imports are a separate cold-process stage, not construction. + stage('tool_import', lambda: __import__(args.tool)) + import matplotlib + matplotlib.use('Agg') + import matplotlib.pyplot + from PIL import Image + raw = stage('file_read', lambda: Path(args.dataset).read_bytes()) + result['sha256'] = hashlib.sha256(raw).hexdigest() + data = stage('json_parse', lambda: json.loads(raw)) + nodes, edges = stage('canonical_validate', lambda: canonical(data)) + result['seed'] = data.get('meta', {}).get('attrs', {}).get('benchmark_seed') + result['counts'] = {'vertices': len(nodes), 'hyperedges': len(edges), + 'memberships': sum(len(set(e['vertices'])) for e in edges.values()), + 'maximum_arity': max((len(set(e['vertices'])) for e in edges.values()), default=0)} + graph = stage('adapter_construct', lambda: construct(args.tool, data, (nodes, edges))) + result['correctness'] = check(args.tool, graph, data) + result['status'] = 'ok' if result['correctness']['exact_canonical_roundtrip'] else 'unsupported_records' + result['plot_semantics'] = {'empty_edges_have_no_geometry': True, + 'hypergraphx_singleton_groups_have_no_group_geometry': args.tool == 'hypergraphx', + 'common_coordinates': 'canonical XY; Z discarded for this 2D track'} + if args.plot: + fig = stage('artist_setup', lambda: figure(args.tool, graph, data)) + stage('agg_raster_draw', fig.canvas.draw) + png = Path(args.output).with_suffix('.png') + # Pixel buffer retrieval is recorded independently; save has no draw. + image = stage('pixel_buffer_copy', lambda: Image.frombytes('RGBA', fig.canvas.get_width_height(), fig.canvas.buffer_rgba().tobytes())) + stage('png_export', lambda: image.save(png, dpi=(100, 100))) + result['png'] = str(png) + result['png_dimensions'] = list(image.size) + except Exception as exc: + result['status'] = 'failed' + result['failure'] = {'type': type(exc).__name__, 'message': str(exc), 'traceback': traceback.format_exc()} + finally: + rss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss + result['process_peak_rss_bytes'] = rss if sys.platform == 'darwin' else rss * 1024 + result['rss_scope'] = 'whole isolated worker, includes imports and validation' + faulthandler.cancel_dump_traceback_later() + Path(args.output).write_text(json.dumps(result, indent=2)) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--dataset', action='append') + parser.add_argument('--datasets-dir', default='target/benchmark/datasets') + parser.add_argument('--output', default='target/benchmark/science/results') + parser.add_argument('--tool', choices=TOOLS) + parser.add_argument('--worker', action='store_true') + parser.add_argument('--plot', action='store_true') + parser.add_argument('--validate', action='store_true') + parser.add_argument('--run', action='store_true') + parser.add_argument('--seed', type=int, default=20261003) + parser.add_argument('--repetitions', type=int, default=5) + parser.add_argument('--timeout', type=int, default=120) + args = parser.parse_args() + installed = {p: importlib.metadata.version(p) for p in PINNED} + if installed != PINNED: + parser.error(f'Pinned environment mismatch: {installed}') + if args.worker: + args.dataset = args.dataset[0] + worker(args) + return + if not args.validate and not args.run: + parser.error('Choose --validate (untimed) or --run (coordinated batch)') + if args.run and args.repetitions < 5: + parser.error('Protocol requires at least five repetitions') + out = Path(args.output) + out.mkdir(parents=True, exist_ok=True) + datasets = [Path(p) for p in args.dataset] if args.dataset else sorted(Path(args.datasets_dir).glob('*.json')) + datasets = [p for p in datasets if p.stem in ['correctness', 'moderate', 'large', 'high-arity-10', 'high-arity-100', 'high-arity-1000', 'scientific', 'mathlib']] + if not datasets: + parser.error('No canonical datasets found') + rng = random.Random(args.seed) + jobs = [(p, tool, mode) for p in datasets for tool in ([args.tool] if args.tool else TOOLS) + for mode in (['construct', 'plot'] if p.stem in ['correctness', 'moderate', 'scientific'] else ['construct'])] + rows = [] + schedule = [] + for repetition in range(-1, 1 if args.validate else args.repetitions): + if args.validate and repetition == -1: + continue + rng.shuffle(jobs) + for path, tool, mode in jobs: + name = f'{path.stem}-{tool}-{mode}-{repetition}' + destination = out / (name + '.json') + command = [sys.executable, str(Path(__file__).resolve()), '--worker', '--tool', tool, + '--dataset', str(path), '--output', str(destination), '--timeout', str(args.timeout)] + if mode == 'plot': command += ['--plot'] + if args.validate: command += ['--validate'] + env = dict(os.environ, MPLCONFIGDIR=str(out.resolve() / 'mpl-cache'), + OPENBLAS_NUM_THREADS='1', OMP_NUM_THREADS='1', MKL_NUM_THREADS='1', + XDG_CACHE_HOME=str(out.resolve() / 'xdg-cache')) + schedule.append({'order': len(schedule), 'repetition': repetition, 'dataset': path.stem, 'tool': tool, 'mode': mode}) + destination.unlink(missing_ok=True) + try: + completed = subprocess.run(command, env=env, capture_output=True, text=True, timeout=args.timeout) + row = json.loads(destination.read_text()) if destination.exists() else {'status': 'process_failed', 'returncode': completed.returncode} + if completed.returncode != 0: + row.update(status='process_failed', returncode=completed.returncode) + row['stderr'] = completed.stderr[-8000:] + except subprocess.TimeoutExpired: + row = {'status': 'watchdog_timeout', 'timeout_seconds': args.timeout} + row.update(schedule[-1], warmup=repetition == -1) + rows.append(row) + (out / 'raw.json').write_text(json.dumps(rows, indent=2)) + print(name, row['status'], flush=True) + metadata = {'seed': args.seed, 'warmup_per_combination': 0 if args.validate else 1, + 'repetitions': 1 if args.validate else args.repetitions, 'untimed_validation': args.validate, + 'platform': platform.platform(), 'python': sys.version, 'pins': PINNED, + 'installed_packages': {d.metadata['Name']: d.version for d in importlib.metadata.distributions()}, + 'documentation': SOURCES, 'schedule': schedule, + 'settings': {'resolution': [1920,1080], 'dpi': 100, 'backend': 'Agg', 'labels': False, + 'layout': 'none; canonical XYZ projected to XY', 'thread_limit': 1, 'hyperedge_alpha': 0.15, + 'hypergraphx_smoothing_refinements': 12, + 'hypergraphx_polygon_expansion_triangle': 2.5, + 'hypergraphx_polygon_expansion_other': 1.8}, + 'limitations': ['Different documented polygon/hull artists; no identical raster workload claim', + 'No static figure vs interactive FPS ranking', 'Peak RSS includes imports', + 'power/thermal/background conditions recorded by coordinator', + 'HGX canonical edge IDs stored as metadata; native IDs differ']} + (out / 'metadata.json').write_text(json.dumps(metadata, indent=2)) + with (out / 'raw.csv').open('w', newline='') as f: + writer = csv.DictWriter(f, fieldnames=['dataset','tool','mode','repetition','warmup','status','process_peak_rss_bytes','stages_ms','correctness','failure']) + writer.writeheader() + for row in rows: + writer.writerow({k: json.dumps(row.get(k)) if isinstance(row.get(k),(dict,list)) else row.get(k) for k in writer.fieldnames}) + +if __name__ == '__main__': + main() diff --git a/scripts/benchmarks/summarize.py b/scripts/benchmarks/summarize.py new file mode 100644 index 0000000..0e1bb7c --- /dev/null +++ b/scripts/benchmarks/summarize.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +"""Summarize raw observations; median of per-run quantiles, never inverse median FPS.""" +import argparse +import csv +import json +from pathlib import Path +import statistics +from collections import defaultdict + +def quantile(values, q): + values = sorted(values) + if not values: return None + position = (len(values) - 1) * q + left = int(position) + right = min(left + 1, len(values) - 1) + return values[left] + (values[right] - values[left]) * (position - left) + +def median(values): + values = [v for v in values if v is not None] + return statistics.median(values) if values else None + +def load(path): + return json.loads(path.read_text()) if path.exists() else [] + +def summarize(root): + rows = [] + for track in ['native', 'native-star', 'native-interactive', 'core']: + groups = defaultdict(list) + for row in load(root / track / 'raw.json'): + if not row.get('warmup'): groups[(row['dataset'], row['hulls'], row['live'])].append(row) + for (dataset, hulls, live), group in groups.items(): + ok = [r for r in group if r['status'] == 'ok'] + row = {'track': track, 'tool': 'Hyper', 'dataset': dataset, 'mode': ('live' if live else 'frozen') + ('+hulls' if hulls else ''), 'attempts': len(group), 'successful_runs': len(ok), 'statuses': sorted({r['status'] for r in group}), 'peak_rss_mib_median': median([r['process_peak_rss_bytes'] / 2**20 for r in ok if r.get('process_peak_rss_bytes') is not None])} + if track != 'core': + traces = [r['measurements']['frame_interval_ms'] for r in ok] + row.update(frame_p50_ms=median([quantile(t, .5) for t in traces]), frame_p95_ms=median([quantile(t, .95) for t in traces]), frame_p95_run_min_ms=min([quantile(t,.95) for t in traces], default=None), frame_p95_run_max_ms=max([quantile(t,.95) for t in traces], default=None), average_fps=median([1000 * len(t) / sum(t) for t in traces if sum(t)]), update_commit_ms=median([r['measurements']['update_commit_ms'][0] for r in ok]), update_next_frame_ms=median([r['measurements'].get('update_next_frame_ms') for r in ok]), layout_step_p50_ms=median([quantile(r['measurements']['layout_step_ms'][:r['measurements']['frames']],.5) for r in ok])) + else: + for key in ['native_parse_ms', 'hif_parse_validate_ms', 'hif_viewer_conversion_ms', 'bipartite_projection_ms', 'all_hulls_cpu_ms']: + row[key] = median([r['measurements'][key] for r in ok]) + row['layout_step_p50_ms'] = median([quantile(r['measurements']['layout_step_ms'], .5) for r in ok]) + rows.append(row) + browser_path = root / 'browser-final/results.json' + browser = load(browser_path if browser_path.exists() else root / 'browser/results.json') + if browser: + groups = defaultdict(list) + for r in browser['runs']: groups[(r['dataset'],r['tool'],r['mode'])].append(r) + for (dataset,tool,mode), group in groups.items(): + ok = [r for r in group if 'trace' in r and r.get('status','ok') in ['ok','completed']] + traces = [r['trace']['rafIntervalMs'] for r in ok] + rows.append({'track': 'browser', 'tool': tool, 'dataset': dataset, 'mode': mode, 'attempts': len(group), 'successful_runs': len(ok), 'statuses': sorted({r.get('status','ok') for r in group}), 'frame_p50_ms': median([quantile(t,.5) for t in traces]), 'frame_p95_ms': median([quantile(t,.95) for t in traces]), 'average_fps': median([1000 * len(t) / sum(t) for t in traces if sum(t)]), 'frame_p95_run_min_ms': min([quantile(t,.95) for t in traces],default=None), 'frame_p95_run_max_ms': max([quantile(t,.95) for t in traces],default=None), 'load_wall_ms': median([r.get('loadWallMs') for r in ok]), 'update_commit_ms': median([r.get('incremental',{}).get('commandMs') for r in ok]), 'update_two_raf_ms': median([r.get('incremental',{}).get('twoRafFeedbackSurrogateMs') for r in ok]), 'failure_stages': sorted({r.get('stage','unknown') for r in group if r.get('status')=='failed'})}) + groups = defaultdict(list) + science_path = root / 'science/final-raw.json' + for r in load(science_path if science_path.exists() else root / 'science/results/raw.json'): + if not r.get('warmup'): groups[(r['dataset'],r['tool'],r['mode'])].append(r) + for (dataset,tool,mode), group in groups.items(): + measured = [r for r in group if r['status'] in ['ok','unsupported_records'] and not r.get('failure')] + row = {'track': 'scientific', 'tool': tool, 'dataset': dataset, 'mode': mode, 'attempts': len(group), 'successful_runs': len(measured), 'statuses': sorted({r['status'] for r in group}), 'exact_model_records': all(r.get('correctness',{}).get('exact_canonical_roundtrip',False) for r in measured) if measured else False, 'peak_rss_mib_median': median([r['process_peak_rss_bytes'] / 2**20 for r in measured])} + for key in ['tool_import','file_read','json_parse','canonical_validate','adapter_construct','artist_setup','agg_raster_draw','pixel_buffer_copy','png_export']: + row[key + '_ms'] = median([r.get('stages_ms',{}).get(key) for r in measured]) + rows.append(row) + groups = defaultdict(list) + for row in load(root / 'graphia/raw.json'): + if not row.get('warmup'): groups[row['dataset']].append(row) + for dataset, group in groups.items(): + ok = [r for r in group if r['status'] == 'ok'] + rows.append({'track':'desktop-import', 'tool':'Graphia', 'dataset':dataset, + 'mode':'headless-import-save', 'attempts':len(group), 'successful_runs':len(ok), + 'statuses':sorted({r['status'] for r in group}), + 'import_save_wall_ms':median([r['process_wall_seconds'] * 1000 for r in ok]), + 'peak_rss_mib_median':median([r['process_peak_rss_bytes'] / 2**20 for r in ok if r.get('process_peak_rss_bytes') is not None]), + 'exact_incidence_records':all(r['correctness']['exact_incidence_and_node_attributes'] for r in ok) if ok else False}) + for run in load(root / 'hypergodot/raw.json'): + measurements = run.get('measurements',{}) + repetitions = measurements.get('repetitions',[]) + traces = [r['frame_ms'] for r in repetitions] + rows.append({'track':'desktop-group', 'tool':'HyperGodot', 'dataset':run['dataset'], + 'mode':'frozen-camera-motion', 'attempts':5, 'successful_runs':len(repetitions), + 'statuses':[run['status']], 'frame_p50_ms':median([quantile(t,.5) for t in traces]), + 'frame_p95_ms':median([quantile(t,.95) for t in traces]), + 'frame_p95_run_min_ms':min([quantile(t,.95) for t in traces],default=None), + 'frame_p95_run_max_ms':max([quantile(t,.95) for t in traces],default=None), + 'average_fps':median([1000*len(t)/sum(t) for t in traces if sum(t)]), + 'whole_session_peak_rss_mib':run.get('process_peak_rss_bytes',0)/2**20 if run.get('process_peak_rss_bytes') else None, + 'exact_parser_memberships':measurements.get('exact_parser_memberships_and_rendered_group_count',False), + 'viewport_size':measurements.get('viewport_size'), + 'render_target_size':measurements.get('render_target_size'), + 'matched_requested_viewport':measurements.get('matched_requested_viewport',False), + 'startup_construction_ms':measurements.get('startup_construction_ms')}) + return sorted(rows, key=lambda r: (r['track'],r['dataset'],r['tool'],r['mode'])) + +if __name__ == '__main__': + p = argparse.ArgumentParser() + p.add_argument('--input', type=Path, default=Path('target/benchmark')) + p.add_argument('--output', type=Path, default=Path('docs/benchmarks/ecosystem-2026-10-03')) + args = p.parse_args() + args.output.mkdir(parents=True, exist_ok=True) + rows = summarize(args.input) + (args.output / 'summary.json').write_text(json.dumps(rows, indent=2) + '\n') + keys = sorted({key for r in rows for key in r}) + with (args.output / 'summary.csv').open('w', newline='') as f: + writer = csv.DictWriter(f, fieldnames=keys, lineterminator="\n") + writer.writeheader() + for row in rows: writer.writerow({k: json.dumps(v) if isinstance(v,list) else v for k,v in row.items()}) diff --git a/scripts/benchmarks/test_datasets.py b/scripts/benchmarks/test_datasets.py new file mode 100644 index 0000000..ac5e646 --- /dev/null +++ b/scripts/benchmarks/test_datasets.py @@ -0,0 +1,24 @@ +import unittest +from datasets import synthetic, incidence, hif, changed + +class Datasets(unittest.TestCase): + def test_seed_and_membership_counts_are_stable(self): + a=synthetic('moderate',1000,2000,8) + self.assertEqual(a,synthetic('moderate',1000,2000,8)) + self.assertEqual(len(a['vertices']),1000) + self.assertEqual(len(a['hyperedges']),2000) + self.assertTrue(all(len(e['vertices'])==len(set(e['vertices'])) for e in a['hyperedges'])) + def test_incidence_keeps_empty_edges_and_isolates(self): + graph=synthetic('correctness',30,15,8) + graph['hyperedges'][0]['vertices']=[] + scene=incidence(graph) + self.assertEqual(len(scene['nodes']),45) + self.assertEqual(len(scene['links']),sum(len(e['vertices']) for e in graph['hyperedges'])) + self.assertEqual(len(hif(graph)['nodes']),30) + self.assertEqual(hif(graph)['edges'][0]['edge'],'g0') + def test_updates_change_one_percent_without_renaming_vertices(self): + graph=synthetic('moderate',1000,2000,8) + updated=changed(graph) + self.assertEqual([v['id'] for v in graph['vertices']], [v['id'] for v in updated['vertices']]) + self.assertEqual(sum(a!=b for a,b in zip(graph['hyperedges'],updated['hyperedges'])),20) +if __name__=='__main__': unittest.main() diff --git a/scripts/benchmarks/test_scientific.py b/scripts/benchmarks/test_scientific.py new file mode 100644 index 0000000..ea42e95 --- /dev/null +++ b/scripts/benchmarks/test_scientific.py @@ -0,0 +1,27 @@ +import unittest +from datasets import synthetic +from scientific import canonical, construct, check + +class ScientificAdapters(unittest.TestCase): + def test_duplicate_incidence_is_rejected_before_library_normalization(self): + graph = synthetic('invalid', 3, 1, 2, fixed=True) + graph['hyperedges'][0]['vertices'].append(graph['hyperedges'][0]['vertices'][0]) + with self.assertRaisesRegex(ValueError, 'Duplicate incidence'): + canonical(graph) + + def test_hnx_nested_properties_use_unflattened_public_dataframe(self): + graph = synthetic('metadata', 3, 2, 3, fixed=True) + result = check('hypernetx', construct('hypernetx', graph), graph) + self.assertTrue(result['exact_canonical_roundtrip']) + self.assertEqual(result['wrong_metadata'], []) + + def test_hgx_parallel_edges_are_reported_without_checker_failure(self): + graph = synthetic('parallel', 3, 2, 2, fixed=True) + graph['hyperedges'][1]['vertices'] = graph['hyperedges'][0]['vertices'][:] + result = check('hypergraphx', construct('hypergraphx', graph), graph) + self.assertTrue(result['opaque_record_values_preserved']) + self.assertFalse(result['native_multiplicity_preserved']) + self.assertFalse(result['exact_canonical_roundtrip']) + self.assertEqual(result['native_hyperedge_count'], 1) + +if __name__ == '__main__': unittest.main() diff --git a/scripts/benchmarks/test_summarize.py b/scripts/benchmarks/test_summarize.py new file mode 100644 index 0000000..a090312 --- /dev/null +++ b/scripts/benchmarks/test_summarize.py @@ -0,0 +1,35 @@ +import json +from pathlib import Path +import tempfile +import unittest +from summarize import summarize + +class Summaries(unittest.TestCase): + def test_completed_browser_rows_and_average_throughput(self): + with tempfile.TemporaryDirectory() as temp: + root = Path(temp) + (root / 'browser').mkdir() + (root / 'browser/results.json').write_text(json.dumps({'runs': [{'dataset':'tiny','tool':'sigma','mode':'camera-motion','status':'completed','trace':{'rafIntervalMs':[10,10,40]}}]})) + row = summarize(root)[0] + self.assertEqual(row['successful_runs'], 1) + self.assertEqual(row['frame_p50_ms'], 10) + self.assertEqual(row['average_fps'], 50) + + def test_native_layout_excludes_post_update_steps_and_failed_runs(self): + with tempfile.TemporaryDirectory() as temp: + root = Path(temp) + (root / 'native').mkdir() + common = {'dataset':'tiny','hulls':True,'live':True,'warmup':False} + measured = {**common,'status':'ok','measurements':{ + 'frames':300,'frame_interval_ms':[20]*300, + 'layout_step_ms':list(range(1,301))+[10000]*4, + 'update_commit_ms':[5], 'update_next_frame_ms':25}} + failed = {**common,'status':'watchdog_timeout'} + (root / 'native/raw.json').write_text(json.dumps([measured,failed])) + row = summarize(root)[0] + self.assertEqual(row['attempts'],2) + self.assertEqual(row['successful_runs'],1) + self.assertEqual(row['layout_step_p50_ms'],150.5) + self.assertEqual(row['average_fps'],50) + +if __name__ == '__main__': unittest.main() diff --git a/scripts/benchmarks/verify_scientific.py b/scripts/benchmarks/verify_scientific.py new file mode 100644 index 0000000..abe150b --- /dev/null +++ b/scripts/benchmarks/verify_scientific.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +"""Untimed semantic checks against independently parsed immutable input bytes.""" +import hashlib +import json +from pathlib import Path +from scientific import construct, check, TOOLS + +rows = [] +for path in sorted(Path('target/benchmark/datasets').glob('*.json')): + if path.stem not in ['correctness','moderate','large','high-arity-10','high-arity-100','high-arity-1000','scientific','mathlib']: + continue + raw = path.read_bytes() + for tool in TOOLS: + data = json.loads(raw) + graph = construct(tool, data) + expected = json.loads(raw) + unmutated = data == expected + assert unmutated, f'{tool} mutated source data in {path}' + result = check(tool, graph, expected) + rows.append({'dataset': path.stem, 'tool': tool, 'sha256': hashlib.sha256(raw).hexdigest(), 'input_unmutated': unmutated, 'correctness': result}) + print(path.stem, tool, result['exact_canonical_roundtrip'], flush=True) +Path('target/benchmark/science/independent-validation.json').write_text(json.dumps(rows, indent=2) + '\n') diff --git a/scripts/releases/build_python_sdist.py b/scripts/releases/build_python_sdist.py new file mode 100644 index 0000000..64503d9 --- /dev/null +++ b/scripts/releases/build_python_sdist.py @@ -0,0 +1,54 @@ +"""Build a standalone core/Python workspace before creating the PyPI sdist.""" +from pathlib import Path +import argparse +import shutil +import subprocess +import tempfile +import tomllib + + +def build(output: Path) -> None: + root = Path(__file__).resolve().parents[2] + output.mkdir(parents=True, exist_ok=True) + original_lock = tomllib.loads((root / "Cargo.lock").read_text()) + original_versions = { + (p["name"], p["version"], p.get("source", "")) + for p in original_lock["package"] + } + with tempfile.TemporaryDirectory(prefix="hyper-python-sdist-") as folder: + staged = Path(folder) + for name in ("hyper-viz", "hyper-viz-python"): + shutil.copytree( + root / "crates" / name, + staged / "crates" / name, + ignore=shutil.ignore_patterns("__pycache__", "*.pyc", "target"), + ) + for name in ("Cargo.lock", "LICENSE", "LICENSE-MIT", "LICENSE-APACHE", "NOTICE"): + shutil.copy2(root / name, staged / name) + # This root is a virtual workspace: no CLI or Bevy source is needed. + workspace = (root / "Cargo.toml").read_text().split("[package]", 1)[0] + workspace = workspace.replace( + 'members = ["crates/hyper-viz", "crates/hyper-viz-bevy", "crates/hyper-viz-python"]', + 'members = ["crates/hyper-viz", "crates/hyper-viz-python"]', + ) + workspace = workspace.replace('hyper-viz-bevy = { path = "crates/hyper-viz-bevy" }\n', "") + (staged / "Cargo.toml").write_text(workspace) + # Prune unused viewer packages while retaining the existing locked versions. + subprocess.run(["cargo", "metadata", "--format-version", "1"], + cwd=staged, check=True, stdout=subprocess.DEVNULL) + lock = tomllib.loads((staged / "Cargo.lock").read_text()) + for package in lock["package"]: + identity = (package["name"], package["version"], package.get("source", "")) + if identity not in original_versions: + raise RuntimeError(f"Source packaging changed a dependency: {identity}") + subprocess.run([ + "maturin", "sdist", "--manifest-path", + str(staged / "crates/hyper-viz-python/Cargo.toml"), + "--out", str(output.resolve()), + ], cwd=staged, check=True) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--out", type=Path, default=Path("dist")) + build(parser.parse_args().out) diff --git a/scripts/releases/check_desktop_dependencies.py b/scripts/releases/check_desktop_dependencies.py new file mode 100644 index 0000000..c08db4c --- /dev/null +++ b/scripts/releases/check_desktop_dependencies.py @@ -0,0 +1,9 @@ +"""Reject unused native audio/gamepad dependencies in the desktop executable.""" +import subprocess + +packages = subprocess.check_output([ + 'cargo', 'tree', '--locked', '-p', 'hyper', '--prefix', 'none', +], text=True) +for package in ('bevy_audio', 'bevy_gilrs', 'alsa-sys', 'libudev-sys'): + assert not any(line.startswith(package + ' ') for line in packages.splitlines()), f'Unexpected desktop dependency: {package}' +print('Desktop dependency graph excludes native audio/gamepad libraries') diff --git a/scripts/releases/check_desktop_wheel.py b/scripts/releases/check_desktop_wheel.py new file mode 100644 index 0000000..bc4fddf --- /dev/null +++ b/scripts/releases/check_desktop_wheel.py @@ -0,0 +1,34 @@ +"""Check the portable desktop wheel before installing or publishing it.""" +from email.parser import Parser +from pathlib import Path +import sys +import zipfile + +wheels = list(Path(sys.argv[1]).glob('*.whl')) +assert wheels, 'No desktop wheels built' +for wheel in wheels: + with zipfile.ZipFile(wheel) as archive: + names = archive.namelist() + assert not any('.libs/' in n or '.so' in n for n in names), 'Unexpected bundled shared libraries: audit without repair' + metadata = Parser().parsestr(archive.read(next(n for n in names if n.endswith('/METADATA'))).decode()) + assert metadata['Name'] == 'hypergraph-viz-viewer', metadata['Name'] + assert metadata['Requires-Python'] == '>=3.10' + binary = [n for n in names if '.data/scripts/' in n and n.rsplit('/', 1)[-1] in ('hyper', 'hyper.exe')] + assert len(binary) == 1, binary + data = archive.read(binary[0]) + assert data[:4] in (b'\x7fELF', b'\xcf\xfa\xed\xfe', b'\xfe\xed\xfa\xcf', b'\xca\xfe\xba\xbe') or data[:2] == b'MZ' + if wheel.name.endswith('win_amd64.whl'): + import pefile + pe = pefile.PE(data=data, fast_load=True) + pe.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']]) + imports = [entry.dll.decode().lower() for entry in pe.DIRECTORY_ENTRY_IMPORT] + assert not any(name.startswith(('vcruntime', 'msvcp')) for name in imports), f'External Visual C++ runtime required: {imports}' + else: + assert (archive.getinfo(binary[0]).external_attr >> 16) & 0o111, 'Executable permission missing' + assert any('LICENSE-MIT' in n for n in names) + assert any('LICENSE-APACHE' in n for n in names) + assert any(n.endswith('/schemas/HIF-LICENSE') for n in names), 'Missing HIF schema license' + assert any(n.endswith('NOTICE') for n in names) + assert '-py3-none-' in wheel.name and not wheel.name.endswith('-any.whl'), wheel.name + assert wheel.stat().st_size < 100 * 1024 * 1024, 'Wheel exceeds default PyPI file limit' + print(f'Checked desktop wheel {wheel.name} ({wheel.stat().st_size // 1024 // 1024} MiB)') diff --git a/scripts/releases/smoke_desktop.py b/scripts/releases/smoke_desktop.py new file mode 100644 index 0000000..3422e07 --- /dev/null +++ b/scripts/releases/smoke_desktop.py @@ -0,0 +1,62 @@ +"""Verify installed desktop identity; optionally render a finite real window.""" +import argparse +from importlib.metadata import distribution, version +import os +from pathlib import Path +import subprocess +import tempfile +import time + +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument('--render', action='store_true') +parser.add_argument('--viewer-only', action='store_true') +parser.add_argument('--output', type=Path, default=Path('target/desktop-smoke')) +args = parser.parse_args() + +companion = distribution('hypergraph-viz-viewer') +if args.viewer_only: + assert not args.render, '--render requires the core package' + core = companion.version +else: + import hyper_viz + from hyper_viz._viewer import _resolve_executable + core = version('hypergraph-viz') + assert companion.version == core +# Empty PATH deliberately proves that activation is unnecessary. +os.environ['PATH'] = '' +os.environ['HYPER_VIZ_SESSION'] = 'off' +if args.viewer_only: + binaries = [Path(str(companion.locate_file(f))).resolve() for f in companion.files or [] if f.name in ('hyper', 'hyper.exe')] + assert len(binaries) == 1, binaries + executable = str(binaries[0]) +else: + executable = _resolve_executable(None) +result = subprocess.run([executable, '--version'], check=True, capture_output=True, text=True, timeout=30) +assert result.stdout.strip() == f'hyper {core}', result.stdout +subprocess.run([executable, '--help'], check=True, stdout=subprocess.DEVNULL, timeout=30) +print(f'Installed desktop executable found without PATH: {executable}') + +if args.render: + args.output.mkdir(parents=True, exist_ok=True) + fixture = Path(__file__).resolve().parents[2] / 'fixtures/notebook-small.hif.json' + doc = hyper_viz.HifDocument.load(str(fixture)) + doc.to_hypergraph_json() + with tempfile.TemporaryDirectory(dir=args.output, prefix='render-') as directory: + folder = Path(directory) + subprocess.run([ + executable, str(fixture), '--format', 'hif', '--tour', '--frames', '3', + '--warmup', '0', '--capture', str(folder / 'frames'), '--report', str(folder / 'frames.csv'), + ], check=True, timeout=180) + frames = list((folder / 'frames').glob('*.png')) + assert len(frames) == 3, frames + assert all(frame.read_bytes().startswith(b'\x89PNG\r\n\x1a\n') for frame in frames) + assert (folder / 'frames.csv').is_file() + # Preserve the final frame for CI/manual visual inspection. + (args.output / 'render.png').write_bytes(sorted(frames)[-1].read_bytes()) + with hyper_viz.show(doc) as viewer: + snapshot = Path(viewer._snapshot.name) + time.sleep(2) + assert viewer.poll() is None, 'Viewer exited before explicit close' + assert viewer.poll() is not None + assert not snapshot.exists(), 'Temporary snapshot was not cleaned up' + print('Native graphics capture and Python launch/close/cleanup passed.') diff --git a/src/main.rs b/src/main.rs index 5ab493f..0ad502b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,20 +1,25 @@ use clap::Parser; -use hyper_viz::{Projection, load_json, project, sample_coauthorship}; +use hyper_viz::{InputFormat, Projection, load_json_with_format, project, sample_coauthorship}; use hyper_viz_bevy::{ - VisualizerConfig, run_from_graph, run_visualizer, run_visualizer_from_path_with, + VisualizerConfig, run_from_graph, run_visualizer, run_visualizer_from_path_with_format, }; use tracing_subscriber::EnvFilter; #[derive(Parser, Debug)] #[command( name = "hyper", + version, about = "General-purpose 3D hypergraph viewer", after_help = "Omit FILE to open the built-in coauthorship demo." )] struct Args { - /// Path to hypergraph JSON (`hypergraph.v1`; legacy host exports also accepted). + /// Path to native hypergraph or HIF JSON (legacy host exports also accepted). file: Option, + /// Input format (also applies to watched reloads). + #[arg(long, default_value = "auto", value_parser = ["auto", "hypergraph", "hif"])] + format: String, + /// Reload when the file changes. #[arg(short, long)] watch: bool, @@ -62,6 +67,7 @@ fn main() { .init(); let args = Args::parse(); + let format = InputFormat::parse(&args.format).expect("validated input format"); let projection = Projection::parse(&args.projection).unwrap_or_else(|| { tracing::warn!( projection = %args.projection, @@ -74,7 +80,7 @@ fn main() { let graph = args .file .as_deref() - .map(|p| load_json(p).expect("load hypergraph JSON")) + .map(|p| load_json_with_format(p, format).expect("load hypergraph JSON")) .unwrap_or_else(sample_coauthorship); hyper_viz_bevy::showcase::run_showcase( project(&graph, projection), @@ -96,15 +102,16 @@ fn main() { run_visualizer(scene); } (Some(path), true) => { - run_visualizer_from_path_with( + run_visualizer_from_path_with_format( path.to_string(), true, projection, VisualizerConfig::default(), + format, ); } (Some(path), false) => { - let graph = load_json(path).expect("load hypergraph JSON"); + let graph = load_json_with_format(path, format).expect("load hypergraph JSON"); if projection == Projection::Bipartite { run_from_graph(graph); } else {