{"id":47701698,"url":"https://github.com/mik1810/nxpp","last_synced_at":"2026-04-07T20:02:33.891Z","repository":{"id":289540486,"uuid":"971606347","full_name":"Mik1810/nxpp","owner":"Mik1810","description":"nxpp is a modular C++20 graph library built on top of Boost Graph Library, offering a practical NetworkX-inspired API for graph construction, traversal, shortest paths, components, flows, and generators, with a generated single-header release asset also available.","archived":false,"fork":false,"pushed_at":"2026-04-05T15:14:02.000Z","size":409,"stargazers_count":4,"open_issues_count":17,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-05T15:26:10.676Z","etag":null,"topics":["algorithms","boost","boost-graph-library","c-plus-plus","cpp20","graph","graph-algorithms","graph-library","header-only","networkx"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Mik1810.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-04-23T19:23:33.000Z","updated_at":"2026-04-05T15:14:05.000Z","dependencies_parsed_at":"2025-04-23T20:41:38.949Z","dependency_job_id":null,"html_url":"https://github.com/Mik1810/nxpp","commit_stats":null,"previous_names":["mik1810/nx-"],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/Mik1810/nxpp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mik1810%2Fnxpp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mik1810%2Fnxpp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mik1810%2Fnxpp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mik1810%2Fnxpp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Mik1810","download_url":"https://codeload.github.com/Mik1810/nxpp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mik1810%2Fnxpp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31526666,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T16:28:08.000Z","status":"ssl_error","status_checked_at":"2026-04-07T16:28:06.951Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithms","boost","boost-graph-library","c-plus-plus","cpp20","graph","graph-algorithms","graph-library","header-only","networkx"],"created_at":"2026-04-02T17:22:18.409Z","updated_at":"2026-04-07T20:02:33.884Z","avatar_url":"https://github.com/Mik1810.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# nxpp — NetworkX-inspired graph utilities for modern C++\n\nCurrent release: `v0.7.13` (April 3, 2026)\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"imgs/logo.svg\" alt=\"nxpp logo\" width=\"220\"\u003e\n\u003c/p\u003e\n\n`nxpp` is a **header-only C++20** library built on top of the **Boost Graph Library (BGL)**.\n\nIt does **not** try to reimplement all of NetworkX.  \nIt aims to provide a **small, practical, C++-friendly graph API** that feels closer to NetworkX in day-to-day use while still delegating most graph work to Boost.\n\nThe core abstraction is:\n\n```cpp\nnxpp::Graph\u003cNodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector\u003e\n```\n\nwith public aliases such as:\n\n- `nxpp::WeightedGraphInt`\n- `nxpp::WeightedGraphStr`\n- `nxpp::WeightedDiGraph`\n- `nxpp::WeightedDiGraphInt`\n- `nxpp::GraphInt`\n- `nxpp::GraphStr`\n- `nxpp::DiGraph`\n- `nxpp::DiGraphInt`\n- `nxpp::MultiGraph`\n- `nxpp::MultiDiGraph`\n- `nxpp::UnweightedGraphInt`\n- `nxpp::UnweightedDiGraphInt`\n\n---\n\n## Project status\n\n`nxpp` is now released as `v0.7.13`.\n\n\u003e [!WARNING]\n\u003e The public API is still settling.\n\u003e The biggest open work is no longer the critical multigraph block or the first\n\u003e formal-test block, but the next round of documentation cleanup, generated-docs\n\u003e scaffolding, packaging polish, and broader CI / usage clarity.\n\nToday, the project is strongest as:\n\n- a **header-only wrapper over BGL**\n- a **NetworkX-inspired convenience layer**\n- a library that often returns **materialized C++ results**\n- a project with a **snippet-based parity / regression harness**\n- a codebase that now also has a **real assertion-based test suite with CI**\n\nThe most important open issue groups right now are:\n\n- documentation/generated-docs/testing-story cleanup: `#28`\n- API safety / attribute-system follow-up: `#25`, `#27`\n- packaging / CI follow-up: none immediately blocking\n\nDetailed API tables now live in [`docs/API_REFERENCE.md`](docs/API_REFERENCE.md).\nRuntime validation errors now use a more consistent `X failed: ...` wording\nacross the public surface.\n\n---\n\n## Read this before using `nxpp`\n\n### 1. Complexity in this README means **public-call complexity**\n\nThis README documents the cost of calling an `nxpp` function, not only the underlying graph algorithm.\n\nThat matters because `nxpp` often does extra work around the Boost call itself, for example:\n\n- NodeID ↔ descriptor translation\n- `std::any`-based attribute lookups\n- result materialization into containers\n- cleanup of metadata on destructive operations\n- descriptor remapping after `remove_node()`\n\nSo the complexity tables below describe the cost of the **full `nxpp` function call**.\n\nWhere `nxpp` now materializes ordered map-like public results, those notes also\ncount the real `O(log n)` insertion/lookup behavior of the underlying\n`std::map` storage instead of treating hash-table behavior as expected `O(1)`.\nSome other results now use indexed wrappers that keep linear materialization\ncost while still giving `O(log n)` key lookup. The detailed API reference calls\nout which helpers use ordered maps and which use indexed wrappers.\n\nSee [`docs/COMPLEXITY.md`](docs/COMPLEXITY.md) for the fuller distinction\nbetween Boost's algorithmic complexity and `nxpp`'s full public-call cost.\n\n### 2. Multigraph APIs are still the weakest part of the public surface\n\nFor simple graphs, calls such as `has_edge(u, v)`, `get_edge_weight(u, v)`, `get_edge_attr(u, v, key)`, `G[u][v]`, and `remove_edge(u, v)` are straightforward.\n\nFor multigraphs, the current implementation still addresses edges through `(u, v)` in several places, which is not a stable public way to identify one specific parallel edge.\nIn particular:\n\n- `has_edge(u, v)` means \"at least one parallel edge exists\"\n- `remove_edge(u, v)` removes all parallel edges between `u` and `v`\n- `get_edge_weight(u, v)`, `get_edge_attr(u, v, key)`, and `G[u][v]` resolve through a single edge returned by `boost::edge(u, v, g)`, so they do not identify a specific parallel edge in a stable public way\n- `add_edge(..., attrs)` on multigraphs attaches attributes through that same `(u, v)` resolution path, so attribute assignment is also not yet a precise per-parallel-edge API\n\nTreat multigraph mutation / lookup as a **known caveat**, not as a fully polished API.\n\nFor precise multigraph work, prefer the explicit `edge_id` API:\n\n- create a specific edge with `add_edge_with_id(...)`\n- enumerate parallel edges with `edge_ids(u, v)`\n- inspect a specific edge with `get_edge_endpoints(edge_id)`, `get_edge_weight(edge_id)`, `get_edge_attr(edge_id, key)`\n- mutate a specific edge with `set_edge_weight(edge_id, ...)`, `set_edge_attr(edge_id, ...)`, and `remove_edge(edge_id)`\n\n### 3. `remove_node()` is intentionally not cheap\n\n`nxpp` now supports custom `OutEdgeSelector` and `VertexSelector` values on `Graph\u003c...\u003e`,\nbut destructive updates still require wrapper-side bookkeeping.\n\nIn particular, `remove_node()` must also:\n\n- clear incident edge metadata\n- erase node metadata\n- rebuild the NodeID \u003c-\u003e descriptor map\n- rebuild the maintained vertex-index map used by wrapper algorithms\n\nIn practice, this is still an **`O(V + E)` public operation**.\n\n---\n\n## What `nxpp` is and what it is not\n\n`nxpp` is best understood as:\n\n- a **BGL-backed convenience layer**\n- a **partial NetworkX-inspired API**, not full parity\n- a library that prefers **materialized C++ containers** over lazy iterator-heavy public APIs\n- a place for a few **C++-oriented utility wrappers** that are easier to consume than raw Boost output\n\nIt is **not**:\n\n- a drop-in replacement for NetworkX\n- a full abstraction over all BGL concepts\n- a zero-cost wrapper in every public function\n- a fully stabilized graph framework yet\n\n---\n\n## Project documents\n\nThese repository files should have clearly separated roles:\n\n- [`README.md`](README.md): user-facing overview, quick start, caveats, and document navigation\n- [`TODO.md`](TODO.md): compact local priority index; GitHub Issues are the full backlog source of truth\n- [`CHANGELOG.md`](CHANGELOG.md): dated change history\n- [`SESSION.md`](SESSION.md): historical development log\n- [`docs/README.md`](docs/README.md): index for longer-form and future generated documentation\n- [`docs/API_REFERENCE.md`](docs/API_REFERENCE.md): detailed public API tables and technical reference\n- [`docs/API_ARCHITECTURE.md`](docs/API_ARCHITECTURE.md): public API placement policy for graph methods and namespace-scope helpers\n- [`docs/GRAPH_CONFIGURATION.md`](docs/GRAPH_CONFIGURATION.md): supported BGL configurability surface and advanced-knob policy\n- [`docs/COMPLEXITY.md`](docs/COMPLEXITY.md): complexity policy and Boost-vs-`nxpp` cost model\n- [`docs/EXTERNAL_USAGE.md`](docs/EXTERNAL_USAGE.md): how to consume `nxpp` from another project today\n- [`docs/TEST.md`](docs/TEST.md): testing layers, test commands, and verification scope\n\nThe external-consumption story now also includes a minimal root\n[`CMakeLists.txt`](CMakeLists.txt) for vendored `add_subdirectory(...)` usage,\nin addition to the plain compiler examples documented in\n[`docs/EXTERNAL_USAGE.md`](docs/EXTERNAL_USAGE.md).\n\nIf these files disagree, `README.md` should describe the **current user-facing reality**, while `TODO.md` should describe what is still open.\n\n---\n\n## API architecture policy\n\nThe intended public shape of `nxpp` is:\n\n- **graph methods** as the primary API for graph ownership, mutation, local lookups, traversals, algorithms, and attribute/proxy access on an existing graph\n- **free functions under `nxpp::`** only where namespace-scope factories/generators are the cleaner fit\n\nIn other words:\n\n- `G.add_edge(...)`, `G.remove_node(...)`, `G.neighbors(...)`, `G.get_edge_attr\u003cT\u003e(...)`, `G.bfs_edges(...)`, and `G.dijkstra_shortest_paths(...)` belong on the graph object\n- calls such as `nxpp::complete_graph(...)`, `nxpp::path_graph(...)`, and `nxpp::erdos_renyi_graph(...)` remain free functions because they construct and return graphs\n- old free-function forms for existing-graph operations are deprecated and should not be treated as canonical entry points\n\nSee [`docs/API_ARCHITECTURE.md`](docs/API_ARCHITECTURE.md) for the fuller rule and future-addition placement policy.\n\n---\n\n## Graph configuration policy\n\n`nxpp` intentionally exposes a narrow graph-configuration surface.\n\nToday, the supported public knobs are:\n\n- `NodeID`\n- `EdgeWeight`\n- `Directed`\n- `Multi`\n- `Weighted`\n- `OutEdgeSelector`\n- `VertexSelector`\n\nThe existing aliases such as `WeightedDiGraphInt`, `Graph`, and `MultiGraph` still use the standard `boost::vecS` / `boost::vecS` backend.\nAdvanced users can override selectors by instantiating `Graph\u003c...\u003e` directly.\n\nToday, selector customization is broader than the default aliases, but it is still not \"anything goes\":\n\n- `OutEdgeSelector` can be changed for advanced use cases such as `boost::listS` or `boost::setS`\n- `VertexSelector` can also be changed for advanced use cases such as `boost::listS` or `boost::setS`\n- `Multi=true` is rejected with `boost::setS` because `setS` suppresses parallel edges by design\n\n`NodeID` now has an explicit compile-time contract too:\n\n- it must be copy-constructible\n- it must support equality comparison\n- it must be orderable through `std::less`\n\nThat is the contract used by the wrapper-owned translation maps, ordered result\nwrappers, and on-demand shortest-path reconstruction.\n\nThe free graph generators (`complete_graph`, `path_graph`, and\n`erdos_renyi_graph`) have one extra requirement on top of that:\n\n- `NodeID` must be constructible from `std::size_t`, because those helpers\n  synthesize node IDs `0..n-1`\n\nSo `Graph\u003cstd::string\u003e` remains a valid graph type, but the numeric generator\nhelpers are intentionally aimed at integer-like node IDs unless you provide a\ncustom generator path yourself.\n\nFor example:\n\n```cpp\nnxpp::WeightedDiGraphInt G_default;\nnxpp::Graph\u003cint, int, true, false, true, boost::listS, boost::listS\u003e G_custom;\n```\n\nSee [`docs/GRAPH_CONFIGURATION.md`](docs/GRAPH_CONFIGURATION.md) for the full policy, rationale, and scoped future extension path.\n\n---\n\n## Current scope\n\nThe current public surface covers these areas:\n\n1. **Graph construction and mutation**\n2. **Node and edge attributes through proxies and checked accessors**\n3. **Traversal wrappers**\n4. **Shortest paths**\n5. **Connected / strongly connected components**\n6. **Topological sort and minimum spanning tree helpers**\n7. **Maximum flow, minimum cut, and min-cost flow wrappers**\n8. **Graph generators and extra utilities**\n\nThe most important implemented functionality currently includes:\n\n- graph aliases for directed / undirected / multi / unweighted variants\n- `add_node`, `add_edge`, `remove_edge`, `remove_node`, `clear`\n- `neighbors`, `successors`, `predecessors`\n- `has_*_attr`, `get_*_attr\u003cT\u003e`, `try_get_*_attr\u003cT\u003e`\n- `bfs_edges`, `dfs_edges`, `bfs_tree`, `dfs_tree`\n- `breadth_first_search`, `depth_first_search`, `bfs_visit`, `dfs_visit`\n- `shortest_path`, `dijkstra_path`, `bellman_ford_path`, `dag_shortest_paths`\n- `dijkstra_shortest_paths`, `bellman_ford_shortest_paths`\n- `floyd_warshall_all_pairs_shortest_paths`\n- `connected_components`, `connected_component_groups`\n- `strongly_connected_components`, `strongly_connected_component_map`, `strongly_connected_component_roots`\n- `topological_sort`, `kruskal_minimum_spanning_tree`, `prim_minimum_spanning_tree`\n- `maximum_flow`, `minimum_cut`, `max_flow_min_cost`, `cycle_canceling`\n- `complete_graph`, `path_graph`, `erdos_renyi_graph`\n- `degree_centrality`, `two_sat_satisfiable`\n\nThe canonical umbrella header is `include/nxpp.hpp`.\n\nThe repo now also has a semantic include layout under `include/nxpp/`:\n\n- `nxpp/graph.hpp`\n- `nxpp/attributes.hpp`\n- `nxpp/traversal.hpp`\n- `nxpp/shortest_paths.hpp`\n- `nxpp/components.hpp`\n- `nxpp/spanning_tree.hpp`\n- `nxpp/flow.hpp`\n- `nxpp/generators.hpp`\n- `nxpp/multigraph.hpp`\n- `nxpp/sat.hpp`\n\nThat split is now real for the main public surface:\n\n- `nxpp/graph.hpp` owns the core `Graph` type, its base structural operations,\n  and the public graph aliases\n- `nxpp/attributes.hpp` owns the attribute-aware overloads and checked\n  attribute accessors\n- `nxpp/multigraph.hpp` owns the precise `edge_id`-based multigraph API\n- `nxpp/traversal.hpp` owns the traversal visitors, deprecated BFS/DFS wrappers,\n  and the `Graph` traversal method definitions\n- `nxpp/shortest_paths.hpp` owns the shortest-path helpers, deprecated shortest-path wrappers,\n  and the `Graph` shortest-path method definitions\n- `nxpp/components.hpp` owns the component helpers, deprecated component wrappers,\n  and the `Graph` component method definitions\n- `nxpp/spanning_tree.hpp` owns topological sort, minimum-spanning-tree helpers,\n  deprecated wrappers, and the matching `Graph` method definitions\n- `nxpp/flow.hpp` owns flow/cut/min-cost helpers, deprecated wrappers,\n  supporting result types, and the matching `Graph` method definitions\n- `nxpp/generators.hpp` owns `complete_graph`, `path_graph`, and\n  `erdos_renyi_graph`\n- `nxpp/sat.hpp` owns the 2-SAT helpers\n\n`include/nxpp.hpp` is the real umbrella include for the modular public surface.\n\nThat means both of these forms currently work:\n\n```cpp\n#include \"include/nxpp/flow.hpp\"\n```\n\n```cpp\n#include \"include/nxpp.hpp\"\n```\n\nThe repository also now has a small helper script for rebuilding a distributable\nsingle header from the umbrella rooted at `include/nxpp.hpp`:\n\n```bash\nbash scripts/build_single_header.sh\n```\n\nThat command now generates a standalone `dist/nxpp.hpp` by expanding local\n`nxpp` headers recursively while leaving standard-library and Boost includes\nalone. The modular headers remain the source of truth; the generated file is a\ndistribution artifact and is intentionally not versioned in git.\n\nThe repo now keeps three aligned showcase entry points:\n\n- `main_boost.cpp` for the raw Boost version\n- `main_nxpp.cpp` for the `nxpp` wrapper version\n- `main.py` as the NetworkX/Python companion to the two C++ demos\n\nThe assertion-based test suite now has a dedicated runner:\n\n```bash\nbash scripts/run_tests.sh\n```\n\nUse [`docs/TEST.md`](docs/TEST.md) as the source of truth for the testing\nstory. It now separates:\n\n- showcase programs\n- snippet parity / regression\n- formal assertion-based tests\n- single-header validation\n- large-graph raw-Boost comparison\n\nThere is also an opt-in large-graph comparison runner:\n\n```bash\nbash scripts/run_large_graph_compare.sh\n```\n\nThat path compiles `tests/test_large_graph_compare.cpp`, generates\ndeterministic larger graph instances, and compares `nxpp` against raw Boost for:\n\n- BFS depth results\n- DFS tree edges\n- connected-component partitions\n- strongly connected-component partitions\n- Dijkstra distance maps\n- Bellman-Ford distance maps\n- DAG shortest-path distance maps\n- reachable negative-cycle detection behavior\n- post-`remove_node()` graph state on large graphs\n- large multigraph mutation and cleanup behavior\n- large attribute preservation and cleanup after repeated mutations\n- large combined weighted-graph mutation sequences with raw-Boost comparison\n- large Floyd-Warshall all-pairs agreement with a raw-Boost all-pairs baseline\n- large maximum-flow / minimum-cut agreement with Boost\n- large successive-shortest-path min-cost-flow agreement with Boost\n\nIt is intentionally kept outside `bash scripts/run_tests.sh` so the default\nformal suite stays fast while the larger comparison path remains available when\nyou want extra confidence on wrapper behavior at scale.\n\nThe large-graph driver now also re-runs representative BFS, connected-component,\nstrongly-connected-component, Dijkstra, and `remove_node()` comparisons across\nmultiple fixed seeds instead of relying on one seed per scenario, and it\nincludes a non-default `boost::listS` / `boost::listS` selector regression so\nthe comparison path exercises more of the supported wrapper configuration\nsurface than the default aliases alone.\n\nand a dedicated GitHub Actions workflow:\n\n- `.github/workflows/tests.yml`\n\nThere is also a dedicated workflow for the generated standalone header:\n\n- `.github/workflows/single-header.yml`\n- `.github/workflows/large-graph-compare.yml`\n- `.github/workflows/release.yml`\n\nThe formal-test workflow is intentionally narrow: it installs Boost, runs the\nformal test suite, and publishes the test output as a Markdown job summary.\n\nThe standalone-header workflow is equally narrow in scope:\n\n- runs `bash scripts/build_single_header.sh`\n- smoke-tests the generated `dist/nxpp.hpp`\n- uploads the generated header as a workflow artifact\n\nThe large-graph comparison workflow is dedicated to the new scale-oriented\nverification path:\n\n- runs `bash scripts/run_large_graph_compare.sh` against the modular headers\n- builds `dist/nxpp.hpp`\n- reruns the same large-graph comparison against the generated single header\n- publishes both outputs as a separate Markdown job summary\n\nThe release workflow is now fully automated from pushes to `main`:\n\n- reads the top version from `RELEASE_NOTES.md` and `CHANGELOG.md`\n- fails if those top versions do not match\n- skips publication if the matching `vX.Y.Z` tag already exists\n- builds `dist/nxpp.hpp`\n- runs `bash scripts/run_tests.sh`\n- runs `bash scripts/run_single_header_tests.sh` against `dist/nxpp.hpp`\n- creates and pushes the matching `vX.Y.Z` tag only after the release checks pass\n- creates the GitHub release from `RELEASE_NOTES.md`\n- uploads the tested file as `nxpp.hpp`\n\n`workflow_dispatch` remains available as a manual fallback, but it follows the\nsame self-contained path: read the top version, skip if the tag already exists,\notherwise build, test, tag, and publish in the same run.\n\nThis means the top version in `RELEASE_NOTES.md` and `CHANGELOG.md` should now\nbe treated as the concrete next release that the release workflow will publish\non the next push to `main`.\n\nThese are showcase demos, not formal tests or parity harnesses.\n\nThe `*_nxpp.cpp` snippets now also follow the semantic-header split instead of\npulling in the full umbrella header by default. For example:\n\n- traversal snippets include `nxpp/traversal.hpp`\n- shortest-path snippets include `nxpp/shortest_paths.hpp`\n- component snippets include `nxpp/components.hpp`\n- flow snippets include `nxpp/flow.hpp`\n- the topological-sort snippet includes `nxpp/topological_sort.hpp`\n\nThis keeps the snippet layer closer to the real modular include story and makes\ncompile-time comparisons more meaningful.\n\nThe intended split is:\n\n- `main_*` files are showcase demos\n- `snippet/` is the curated example-plus-parity layer\n- `tests/` is the formal regression suite\n- `test_large_graph_compare.cpp` is the separate large deterministic raw-Boost comparison path\n\nThere is now also a minimal assertion-based test entry point:\n\n- `tests/test_core.cpp`\n- `tests/test_attributes.cpp`\n- `tests/test_edge_cases.cpp`\n- `tests/test_flow.cpp`\n- `tests/test_remove_node.cpp`\n- `tests/test_multigraph.cpp`\n- `tests/test_large_graph_compare.cpp`\n- `scripts/run_tests.sh`\n- `scripts/run_large_graph_compare.sh`\n\nWhere it stays readable, the test files now mirror the semantic-header split too:\n\n- `tests/test_attributes.cpp` includes `nxpp/attributes.hpp`\n- `tests/test_multigraph.cpp` includes `nxpp/multigraph.hpp`\n- `tests/test_flow.cpp` includes `nxpp/flow.hpp`\n- `tests/test_edge_cases.cpp` includes only the graph/traversal/shortest-path/components headers it actually exercises\n\nThat test binary is intentionally small for now, but it gives the project a real\nformal test path in addition to snippets, showcases, and benchmarks.\n\nThe large-graph comparison path is intentionally separate:\n\n- it reuses deterministic generated graphs instead of committed fixtures\n- it compares `nxpp` against raw Boost implementations directly\n- it is meant as scale-oriented verification, not as a benchmark claim\n- it stays opt-in so the normal test path remains quick to run locally and in CI\n\nFor benchmark runs that should produce both CSV data and a ready-to-read report,\nyou can use:\n\n```bash\npython3 scripts/run_benchmark_report.py --all --iterations 3 --jobs \"$(nproc)\"\n```\n\nThat command:\n\n- runs the serial benchmark first and the parallel benchmark second\n- moves any previous benchmark CSVs, `benchmark/BENCHMARK.md`, and `benchmark/imgs/` into `backups/benchmark/\u003ctimestamp\u003e/`\n- generates exactly two timestamped CSV files: one serial and one parallel\n- writes the report to `benchmark/BENCHMARK.md` and the plots to `benchmark/imgs/`\n\nThe multigraph-specific part of the suite now checks the exact behavior that the\nrecent critical issue work stabilized:\n\n- distinct `edge_id` values for parallel edges\n- precise single-edge removal with `remove_edge(edge_id)`\n- all-edge removal with `remove_edge(u, v)`\n- per-edge attribute separation across parallel edges\n- consistent endpoint lookup after partial multigraph removal\n\nThe attribute-specific part of the suite now also checks failure behavior:\n\n- missing node/edge attribute lookups that should throw\n- type mismatches that should throw\n- `try_get_*_attr(...)` returning empty on missing keys or wrong types\n- numeric edge-attribute lookup rejecting non-numeric stored values\n\nThe edge-case part of the suite now checks common boundary conditions:\n\n- empty graphs returning empty collections and component views\n- singleton graphs producing empty neighbor/traversal results\n- missing-node operations throwing in a defined way\n- disconnected shortest paths preserving unreachable distances and missing paths\n- disconnected component grouping staying stable across separate subgraphs\n\nThe flow-specific part of the suite now checks the wrapper layer around the\nsmall reference flow networks already used in the snippets:\n\n- `maximum_flow(...)`\n- `minimum_cut(...)`\n- `push_relabel_maximum_flow(...)`\n- `cycle_canceling(...)`\n- `successive_shortest_path_nonnegative_weights(...)`\n- `max_flow_min_cost(...)` and `max_flow_min_cost_successive_shortest_path(...)`\n\nThe `remove_node()` regression part of the suite now checks the descriptor-remap\narea directly:\n\n- node and edge views after removing a middle vertex\n- cleanup of node and incident edge attributes\n- traversal and shortest-path behavior after internal remapping\n- component views after node removal\n- multigraph cleanup of incident `edge_id` state\n\nString attributes passed as `\"...\"` are normalized internally, so typed reads like\n`get_node_attr\u003cstd::string\u003e(...)` and `get_edge_attr\u003cstd::string\u003e(...)` work without\nhaving to spell `std::string(...)` at the call site.\n\n---\n\n## Build and requirements\n\n`nxpp` is a modular library in the repository, and releases also ship a generated\nsingle-header distribution asset. It depends on **Boost Graph Library**.\n\n### Minimal support matrix\n\nThis matrix is intentionally conservative and reflects what the repository\ncurrently verifies directly:\n\n| Area | Current status |\n|---|---|\n| Language level | C++20 required |\n| Standard library / toolchain used in repo scripts | local scripts are still primarily `g++`-oriented |\n| Continuous integration | GitHub Actions on `ubuntu-latest` and `macos-latest` |\n| Boost dependency | `libboost-graph-dev` on Ubuntu CI |\n| Standalone single header | Generated in CI and tested on Ubuntu |\n| macOS | Covered by the formal test workflow with Homebrew Boost |\n| Windows | Expected to work with vcpkg Boost, but not covered by CI yet |\n| Clang | Covered by the formal test workflow on Ubuntu and macOS |\n\nIn practice, the repository now has a stronger verified story on Linux and\nmacOS, with both `g++` and `clang++` covered in the formal test workflow, while\nWindows still remains an expected but not yet CI-backed path.\n\n### Minimal local build\n\n```bash\ng++ -std=c++20 -Wall -Wextra -pedantic -O3 main_boost.cpp -o main_boost\ng++ -std=c++20 -Wall -Wextra -pedantic -O3 main_nxpp.cpp -o main_nxpp\npython3 main.py\nbash scripts/run_tests.sh\n```\n\n### Install Boost Graph Library\n\n**Ubuntu / Debian**\n\n```bash\nsudo apt-get install libboost-graph-dev\n```\n\n**macOS (Homebrew)**\n\n```bash\nbrew install boost\n```\n\n**Windows (vcpkg)**\n\n```bash\nvcpkg install boost-graph\n```\n\nThe header performs a compile-time include check and fails early if BGL headers are missing.\n\n### External usage\n\nToday there are two supported ways to consume `nxpp` from another project:\n\n1. the modular header tree under `include/`\n2. the tested single-header release asset `nxpp.hpp`\n\nFor the modular layout, add the repo's `include/` directory to your compiler\ninclude path and write includes like:\n\n```cpp\n#include \u003cnxpp.hpp\u003e\n```\n\nor, when you only need one area:\n\n```cpp\n#include \u003cnxpp/shortest_paths.hpp\u003e\n```\n\nMinimal modular compile example:\n\n```bash\ng++ -std=c++20 -I/path/to/nxpp/include app.cpp -o app\n```\n\nFor the single-header path, use the tested `nxpp.hpp` asset attached to a GitHub\nrelease, place it in a vendor/include directory, and compile with that\ndirectory on your include path:\n\n```cpp\n#include \u003cnxpp.hpp\u003e\n```\n\n```bash\ng++ -std=c++20 -I/path/to/vendor/include app.cpp -o app\n```\n\nIn both cases, `nxpp` is still header-only and still depends on **Boost Graph\nLibrary** headers being available to the compiler.\n\nSee [`docs/EXTERNAL_USAGE.md`](docs/EXTERNAL_USAGE.md) for the fuller external\nconsumer story, including the difference between the repo-local generated\n`dist/nxpp.hpp` artifact and the tested release asset.\n\n---\n\n## Quick start\n\nThe old graph-weights / direct-path style example is no longer representative enough.\nA more up-to-date \"small but real\" example is:\n\n```cpp\n#include \"include/nxpp.hpp\"\n#include \u003ciostream\u003e\n#include \u003cstring\u003e\n\nint main() {\n    auto G = nxpp::DiGraph();\n\n    G.add_edge(\"Milan\", \"Rome\", 5.0, {\n        {\"capacity\", 8},\n        {\"company\", \"Trenitalia\"}\n    });\n    G.add_edge(\"Rome\", \"Naples\", 2.5);\n    G.add_edge(\"Milan\", \"Florence\", 2.0);\n    G.add_edge(\"Florence\", \"Naples\", 4.0);\n\n    G.node(\"Rome\")[\"population\"] = 2800000;\n\n    auto routes = G.dijkstra_shortest_paths(\"Milan\");\n    auto dist_to_naples = routes.distance.at(\"Naples\");\n    auto path_to_naples = routes.path_to(\"Naples\");\n\n    auto operator_name =\n        G.get_edge_attr\u003cstd::string\u003e(\"Milan\", \"Rome\", \"company\");\n\n    std::cout \u003c\u003c \"Distance Milan -\u003e Naples: \" \u003c\u003c dist_to_naples \u003c\u003c \"\\n\";\n    std::cout \u003c\u003c \"Rail operator on Milan -\u003e Rome: \" \u003c\u003c operator_name \u003c\u003c \"\\n\";\n    std::cout \u003c\u003c \"Path:\";\n    for (const auto\u0026 node : path_to_naples) {\n        std::cout \u003c\u003c \" \" \u003c\u003c node;\n    }\n    std::cout \u003c\u003c \"\\n\";\n}\n```\n\nThis shows the three main ideas of the library:\n\n- graph mutation through a small wrapper API\n- checked node / edge attribute access\n- richer result wrappers such as `dijkstra_shortest_paths()` with on-demand path reconstruction and indexed component views\n\n---\n\n## Internal model in one minute\n\n`nxpp` keeps these synchronized structures:\n\n- `id_to_bgl`: `NodeID -\u003e vertex_descriptor`\n- `bgl_to_id`: index-ordered `NodeID` storage used for normalized wrapper results\n- a maintained wrapper index property on each vertex, exposed as `vertex_index_map` for BGL algorithms\n\nThis lets the public API use `std::string`, `int`, or other orderable node IDs while running algorithms on a normal BGL graph internally.\n\n### Consequence\n\nThe backend now uses:\n\n```cpp\nboost::adjacency_list\u003cOutEdgeSelector, VertexSelector, ...\u003e\n```\n\nThe default aliases still resolve to `boost::vecS` / `boost::vecS`,\nbut direct `Graph\u003c...\u003e` instantiation can now use non-default selectors.\n\nThat flexibility means `nxpp` cannot rely on descriptor-as-index assumptions anymore,\nso the wrapper maintains its own vertex index normalization layer.\nThat is why some public-call costs differ from the textbook complexity of the wrapped algorithm alone.\n\n---\n\n## Further documentation\n\nThe root README is intentionally kept focused on overview, quick start, caveats, and repository navigation.\n\nFor deeper technical detail, use:\n\n- [`docs/API_REFERENCE.md`](docs/API_REFERENCE.md): detailed API tables, aliases, proxy syntax, result wrappers, and algorithm reference\n- [`docs/API_ARCHITECTURE.md`](docs/API_ARCHITECTURE.md): policy for graph methods vs namespace-scope helpers\n- [`docs/GRAPH_CONFIGURATION.md`](docs/GRAPH_CONFIGURATION.md): graph selector/configuration policy\n- [`docs/README.md`](docs/README.md): docs index\n\nThe most important topics moved out of the README are:\n\n- public API tables and complexity notes\n- alias/reference detail\n- traversal / shortest-path / flow reference tables\n- proxy syntax detail\n- result-wrapper reference\n- repository layout / parity harness detail\n- deeper caveat and trade-off sections\n\n---\n\n## What is still clearly open\n\nThe most important open work visible from the repository today is:\n\n- a real assertion-based test suite\n- stronger edge-case coverage\n- packaging / install story beyond manual header inclusion\n- clearer documentation generation and source-of-truth discipline\n- a minimal compiler/platform support matrix in the README\n- eventual API stabilization\n\nSee [`TODO.md`](TODO.md) for the open list.\n\n---\n\n## License\n\nThis project is licensed under the MIT License. See [`LICENSE`](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmik1810%2Fnxpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmik1810%2Fnxpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmik1810%2Fnxpp/lists"}