|
nxpp
Header-only graph utilities on top of Boost Graph Library
|
This document is the curated markdown companion to the generated API reference.
Use the repository README.md for:
Use the generated Doxygen site for:
Use this file for:
For complexity guarantees and the side-by-side Boost vs nxpp discussion, use `COMPLEXITY.md`. This file intentionally focuses on API shape, return types, and behavioral notes.
Useful generated-reference cross-links:
It should not try to become a second full copy of the generated declaration reference. When this file and the generated docs overlap, the generated docs win on declaration-level detail.
nxpp graph objects are not thread-safe by default.
add_node(...), add_edge(...), remove_node(...), edge removal, and node or edge attribute updates, requires external synchronization if the graph instance is shared across threads.push_relabel_maximum_flow(...) followed by cycle_canceling(...) cache residual state. Do not run overlapping staged-flow sequences on the same graph instance without external synchronization.Internal synchronization around the staged-flow cache prevents races between different graph instances of the same type, but it does not make one shared graph instance safe for concurrent mutation.
| Type | Meaning |
|---|---|
nxpp::Graph<NodeID, EdgeWeight, false, false> | Undirected simple weighted graph with default selectors |
nxpp::Graph<NodeID, EdgeWeight, true, false> | Directed simple weighted graph with default selectors |
nxpp::Graph<NodeID, EdgeWeight, false, true> | Undirected multigraph with default selectors |
nxpp::Graph<NodeID, EdgeWeight, true, true> | Directed multigraph with default selectors |
nxpp::Graph<NodeID, EdgeWeight, Directed, Multi, false> | Unweighted variant with default selectors |
nxpp::Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector> | Advanced form with explicit BGL selectors |
| Alias | Expands to | Role |
|---|---|---|
WeightedGraphInt | Graph<int, int> | Explicit weighted preset |
WeightedGraphStr | Graph<std::string> | Explicit weighted preset |
WeightedDiGraphInt | Graph<int, int, true> | Explicit weighted preset |
WeightedDiGraphStr | Graph<std::string, double, true> | Explicit weighted preset |
WeightedDiGraph | Graph<std::string, double, true> | Explicit weighted preset |
WeightedMultiGraphInt | Graph<int, int, false, true> | Explicit weighted preset |
WeightedMultiDiGraphInt | Graph<int, int, true, true> | Explicit weighted preset |
WeightedMultiGraphStr | Graph<std::string, double, false, true> | Explicit weighted preset |
WeightedMultiDiGraphStr | Graph<std::string, double, true, true> | Explicit weighted preset |
WeightedMultiGraph | Graph<std::string, double, false, true> | Explicit weighted preset |
WeightedMultiDiGraph | Graph<std::string, double, true, true> | Explicit weighted preset |
GraphInt | Graph<int, int> | Thin synonym of WeightedGraphInt |
GraphStr | Graph<std::string> | Thin synonym of WeightedGraphStr |
DiGraphInt | Graph<int, int, true> | Thin synonym of WeightedDiGraphInt |
DiGraph | Graph<std::string, double, true> | Thin synonym of WeightedDiGraphStr |
MultiGraphInt | Graph<int, int, false, true> | Thin synonym of WeightedMultiGraphInt |
MultiDiGraphInt | Graph<int, int, true, true> | Thin synonym of WeightedMultiDiGraphInt |
MultiGraph | Graph<std::string, double, false, true> | Thin synonym of WeightedMultiGraphStr |
MultiDiGraph | Graph<std::string, double, true, true> | Thin synonym of WeightedMultiDiGraphStr |
UnweightedGraphInt | Graph<int, double, false, false, false> | Explicit unweighted preset |
UnweightedDiGraphInt | Graph<int, double, true, false, false> | Explicit unweighted preset |
UnweightedGraphStr | Graph<std::string, double, false, false, false> | Explicit unweighted preset |
UnweightedDiGraph | Graph<std::string, double, true, false, false> | Explicit unweighted preset |
UnweightedMultiGraphInt | Graph<int, double, false, true, false> | Explicit unweighted preset |
UnweightedMultiDiGraphInt | Graph<int, double, true, true, false> | Explicit unweighted preset |
UnweightedMultiGraph | Graph<std::string, double, false, true, false> | Explicit unweighted preset |
UnweightedMultiDiGraph | Graph<std::string, double, true, true, false> | Explicit unweighted preset |
The Weighted* aliases are the clearest explicit names. The shorter aliases such as GraphInt and DiGraph are kept as compatibility-friendly synonyms. All aliases intentionally stay on the default boost::vecS / boost::vecS backend.
This section makes the thin alias story explicit so the canonical entry points stay easier to recognize.
Weighted* aliases are the clearest named presets for the default graph surfaceGraphInt, DiGraph, MultiGraph, and MultiDiGraph are thin compatibility-friendly synonyms of those weighted presetsUnweighted* aliases are explicit presets for graphs without the built-in edge-weight propertyThese methods are supported, but they mainly forward to a more explicit primary name:
| Alias | Primary entry point | Note |
|---|---|---|
single_source_dijkstra(source) | dijkstra_shortest_paths(source) | Same result wrapper under a shorter compatibility-friendly name |
single_source_bellman_ford(source) | bellman_ford_shortest_paths(source) | Same result wrapper under a shorter compatibility-friendly name |
connected_component_map() | connected_components() | Same node -> component_id result |
strongly_connected_components() | strongly_connected_component_groups() | Grouped SCC output under a shorter familiar name |
strongly_connected_component_map() | strong_component_map() | Same SCC map result under a longer explicit alias |
strongly_connected_component_roots() | strong_components() | Same SCC representative/root map |
minimum_spanning_tree() | kruskal_minimum_spanning_tree() | Default thin alias for the Kruskal path |
minimum_spanning_tree(root) | prim_minimum_spanning_tree(root) | Rooted thin alias for the Prim path |
max_flow_min_cost_successive_shortest_path(...) | successive_shortest_path_nonnegative_weights(...) | Compatibility-friendly SSP alias |
max_flow_min_cost(...) | max_flow_min_cost_cycle_canceling(...) | Current default min-cost max-flow wrapper |
The repository still exposes deprecated namespace-scope wrappers such as:
nxpp::bfs_edges(G, start)nxpp::dijkstra_path(G, source, target)nxpp::connected_components(G)nxpp::topological_sort(G)Those exist as migration-friendly compatibility aliases for the method-based API. For existing-graph operations, the canonical public form remains G.foo(...).
Lifecycle policy:
Current deprecated alias families include traversal, shortest paths, components, spanning trees, topological sort, centrality, flow, and graph count helpers. The replacement for each alias is named in its deprecation message and generated API documentation.
nxpp::Graph<NodeID, ...> currently expects NodeID to be:
std::lessThose requirements come from the wrapper's ordered translation maps, ordered result wrappers, and shortest-path predecessor/path reconstruction helpers.
Practical clarifications:
NodeID to be hashableNodeID to provide a public std::hash<NodeID> specializationExamples that fit the current public contract include:
std::stringintstd::less-compatible orderingThe free numeric graph generators in nxpp/generators.hpp have one additional requirement:
NodeID must be constructible from std::size_tThat extra generator constraint is not a global Graph requirement; it only applies to helpers that synthesize node IDs 0..n-1 themselves.
nxpp follows a consistent write-creates / read-does-not-create policy across the public API.
Write-style accessors create missing nodes or edges:
| Call | What gets created |
|---|---|
G.add_node(u) | node u |
G.add_edge(u, v, ...) | both endpoints if absent |
G.node(u)[key] = val | node u (via NodeAttrProxy::operator=) |
G[u][v] = weight | edge (u,v) and both endpoints (via EdgeProxy::operator=) |
G[u][v][key] = val | edge (u,v) if absent (via EdgeAttrProxy::operator=) |
Read-style accessors never create:
| Call | Behavior when absent |
|---|---|
G.has_node(u) | returns false |
G.has_edge(u, v) | returns false |
G.get_node_attr<T>(u, key) | throws std::runtime_error |
G.try_get_node_attr<T>(u, key) | returns std::nullopt |
G.get_edge_attr<T>(u, v, key) | throws std::runtime_error |
G.try_get_edge_attr<T>(u, v, key) | returns std::nullopt |
G.neighbors(u) | throws std::runtime_error |
G.bfs_edges(u) / G.dfs_edges(u) | throws std::runtime_error |
G.shortest_path(u, v) | throws std::runtime_error |
This matches the NetworkX convention: indexing and assignment create implicitly, while pure-read calls assume the element already exists.
remove_node(u) is O(V + E) for each public call: it drops incident edge state, erases the vertex, and rebuilds the internal NodeID to vertex-descriptor and vertex-index maps. That cost profile differs from NetworkX-style expectations where removing one vertex may look more like O(degree(u)).
If you call remove_node for many nodes, one at a time, the repeated map work makes the overall time roughly O(V^2 + V*E) in the worst case, not O(V) independent removals. Treat repeated single-node removal as a hot path only on small graphs or for rare updates.
Anti-pattern (sequential many removals on one graph):
Typical better direction for large batch filtering: build a new graph with the remaining nodes and the edges you still want, instead of in-place subtraction:
For a structured complexity discussion, see also `COMPLEXITY.md`.
| Function | Parameters | Returns | Description | Example |
|---|---|---|---|---|
Graph() | — | graph object | Creates an empty wrapper and initializes internal state. | nxpp::GraphInt G; |
add_node | (const NodeID& id) | void | Inserts the node if absent. | G.add_node(42); |
add_nodes_from | (const std::vector<NodeID>& nodes) | void | Inserts k nodes by repeated add_node. | G.add_nodes_from({1,2,3}); |
has_node | (const NodeID& u) | bool | Checks whether a node exists. | G.has_node(1) |
add_edge | (u, v, w = 1.0) | void | Creates missing endpoints automatically. In simple graphs, a repeated (u, v) overwrites weight. | G.add_edge(1, 2, 3.5); |
add_edge | (u, v) on unweighted graphs | void | Inserts an unweighted edge. | UG.add_edge(1, 2); |
add_edge | (u, v, {"key", value}) | void | Adds one edge attribute with default built-in weight where applicable. In multigraphs this endpoint-based attr-bearing form is now rejected as ambiguous; use add_edge_with_id(...) then set_edge_attr(edge_id, ...). | G.add_edge(0, 1, {"capacity", 5L}); |
add_edge | (u, v, {{"key", value}, ...}) | void | Adds multiple edge attributes with default built-in weight where applicable. In multigraphs this endpoint-based attr-bearing form is now rejected as ambiguous. | G.add_edge(0, 1, {{"capacity", 5L}}); |
add_edge | (u, v, w, {"key", value}) | void | Adds / updates weight and one edge attribute. In multigraphs this endpoint-based attr-bearing form is now rejected as ambiguous. | G.add_edge(0, 1, 3.5, {"capacity", 5L}); |
add_edge | (u, v, w, {{"key", value}, ...}) | void | Adds / updates weight and multiple edge attributes. In multigraphs this endpoint-based attr-bearing form is now rejected as ambiguous. | G.add_edge(0, 1, 3.5, {{"capacity", 5L}}); |
add_edges_from | vector<tuple<u,v,w>> | void | Bulk weighted insertion. | G.add_edges_from({{1,2,2},{2,3,4}}); |
add_edges_from | vector<pair<u,v>> | void | Bulk insertion with default weight or unweighted insertion depending on graph type. | G.add_edges_from({{1,2},{2,3}}); |
has_edge | (u, v) | bool | Checks whether an edge exists. In multigraphs, this means "at least one edge exists". | G.has_edge("A","B") |
has_edge_id | (edge_id) | bool | Checks whether a specific wrapper-tracked edge ID still exists. This is the precise multigraph edge existence check. | G.has_edge_id(eid) |
get_edge_weight | (u, v) | EdgeWeight | Returns the built-in edge weight. In multigraphs, this resolves through one edge returned by boost::edge(u, v, g) and should not be treated as a stable single-parallel-edge lookup. | auto w = G.get_edge_weight(1,2); |
get_edge_weight | (edge_id) | EdgeWeight | Returns the built-in edge weight for one specific wrapper-tracked edge ID. | auto w = G.get_edge_weight(eid); |
nodes | () | std::vector<NodeID> | Materializes all node IDs. | auto ns = G.nodes(); |
edges | () | std::vector<std::pair<NodeID, NodeID>> | Materializes all edges as endpoint pairs for both weighted and unweighted graphs. | auto es = G.edges(); |
num_edges | () | std::size_t | Returns the current edge count without materializing the edge list. | auto m = G.num_edges(); |
edge_pairs | () | std::vector<std::pair<NodeID, NodeID>> | Compatibility alias for edges(). | auto ep = G.edge_pairs(); |
weighted_edges | () on weighted graphs | std::vector<std::tuple<NodeID, NodeID, EdgeWeight>> | Materializes weighted edges with built-in weights. | auto wes = G.weighted_edges(); |
edge_ids | () | std::vector<size_t> | Returns every wrapper-tracked edge ID currently present in the graph. | auto ids = G.edge_ids(); |
edge_ids | (u, v) | std::vector<size_t> | Returns the tracked edge IDs between two endpoints. In multigraphs, this is the main way to enumerate parallel edges precisely. | auto ids = G.edge_ids("A","B"); |
get_edge_endpoints | (edge_id) | std::pair<NodeID, NodeID> | Returns the endpoints of one specific wrapper-tracked edge ID. | auto [u, v] = G.get_edge_endpoints(eid); |
neighbors | (u) | std::vector<NodeID> | Returns out-neighbors. For directed graphs this matches successor semantics. | G.neighbors("A") |
successors | (u) | std::vector<NodeID> | Explicit directed-style successor helper. | G.successors("A") |
predecessors | (u) | std::vector<NodeID> | Returns predecessor IDs in directed graphs. | G.predecessors("B") |
remove_edge | (u, v) | void | Removes the edge and tracked edge metadata. In multigraphs, this removes all parallel edges between u and v and cleans all tracked metadata for that pair. | G.remove_edge(1,2); |
remove_edge | (edge_id) | void | Removes one specific wrapper-tracked edge ID. This is the precise multigraph removal API. | G.remove_edge(eid); |
remove_node | (u) | void | Removes the node, clears incident metadata, erases the vertex, then repairs shifted mappings. O(V + E) per public call. | G.remove_node("Rome"); |
clear | () | void | Resets graph structure, translation maps, attribute stores, and edge-ID state. | G.clear(); |
subgraph | (nodes) | same Graph type | Returns an independent node-induced subgraph. Copies selected nodes, internal edges, built-in weights, and wrapper-managed node/edge attributes. Throws std::invalid_argument if a requested node is missing. | auto H = G.subgraph({"A","B"}); |
node | (u) | NodeAttrBaseProxy | Returns node-attribute proxy access. Creates the node if absent. | G.node("A")["x"] = 7; |
operator[] | (u) | NodeProxy / ConstNodeProxy | Mutable access returns a write-creates proxy for G[u][v] and G[u][v]["key"]; const access never creates and throws if u is missing. | G["A"]["B"] = 2.0; |
get_impl | () | const GraphType& | Exposes the internal BGL graph for wrapper implementation or advanced inspection. | auto& impl = G.get_impl(); |
get_bgl_to_id_map | () | const std::vector<NodeID>& | Exposes the wrapper's maintained index-ordered node list used by result normalization. | auto& map = G.get_bgl_to_id_map(); |
get_id_to_bgl_map | () | const std::map<NodeID, VertexDesc>& | Exposes ID-to-descriptor mapping. | auto& map = G.get_id_to_bgl_map(); |
get_node_id | (vertex_descriptor) | const NodeID& | Returns the user-facing node ID for a descriptor. Mostly useful for advanced integrations. | auto id = G.get_node_id(v); |
get_vertex_index | (vertex_descriptor) | size_t | Returns the wrapper-maintained vertex index used by normalized result containers. | auto i = G.get_vertex_index(v); |
The get_impl() / translation-map getters above are advanced const escape hatches for integrations and wrapper-level utilities. They intentionally expose read-only internal state. The mutable wrapper-owned attribute stores are no longer part of the public surface.
G.subgraph(nodes) materializes a node-induced subgraph with the same graph type as G. The result is a new independent graph, not a view: later mutations to either graph do not affect the other.
The returned graph contains only the requested nodes and the edges whose endpoints are both in that set. Built-in edge weights, node attributes, and edge attributes are copied. Multigraph subgraphs preserve parallel edge instances, but assign fresh edge IDs in the returned graph. Duplicate requested node IDs are ignored. Requesting a node that is not present in the source graph throws std::invalid_argument.
The visualization header is included by nxpp.hpp and can also be included directly:
nxpp::viz::to_dot(G) returns a DOT string and nxpp::viz::write_dot(G, path) writes the same representation to disk. The exporter is header-only, does not require Graphviz at build time, and is the supported DOT export API.
Weighted edges are emitted as both weight=... and label=... by default:
The weight attribute preserves Graphviz layout semantics, while label is what Graphviz renders into SVG/PNG output. nxpp::viz::DotOptions can hide node labels, edge labels, weights, select a Graphviz layout engine, or expose wrapper edge IDs for multigraph inspection. It can also emit graph-level Graphviz attributes through graph_attrs and, when show_user_attrs is enabled, include user-defined node and edge attributes stored through the graph attribute APIs. Render a generated file with Graphviz, for example:
For the generated declaration pages behind this policy, see:
In multigraph mode, nxpp distinguishes between two public API categories:
(u, v) ergonomics but do not promise stable selection of one particular parallel edge unless a function explicitly documents a stronger guaranteePractical rule:
edge_id when one edge instance matters(u, v) forms as existence / convenience / parity helpers in multigraphsG[u][v], get_edge_weight(u, v), or endpoint-based edge-attribute lookups as precise single-parallel-edge handlesExamples of the precise path:
add_edge_with_id(...)has_edge_id(...)edge_ids(...)get_edge_endpoints(edge_id)remove_edge(edge_id)get_edge_attr(edge_id, ...)try_get_edge_attr(edge_id, ...)get_edge_numeric_attr(edge_id, ...)get_edge_weight(edge_id)set_edge_attr(edge_id, ...)set_edge_weight(edge_id, ...)The table below makes the endpoint-based multigraph behavior explicit.
| API | Multigraph meaning | Precise alternative |
|---|---|---|
has_edge(u, v) | Answers only whether at least one parallel edge exists between u and v. | edge_ids(u, v) / has_edge_id(edge_id) |
add_edge(u, v, ...) | Endpoint-based insertion/update convenience form. Not a stable handle to one later edge instance. | add_edge_with_id(...) |
add_edge(u, v, attrs...) | Endpoint-based attr-bearing form. In multigraphs this is now rejected as ambiguous. | add_edge_with_id(...) then set_edge_attr(edge_id, ...) |
remove_edge(u, v) | Removes all parallel edges between u and v. | remove_edge(edge_id) |
get_edge_weight(u, v) | Reads one edge selected through endpoint-based resolution. Not a stable single-edge lookup. | get_edge_weight(edge_id) |
get_edge_attr<T>(u, v, key) | Reads one endpoint-resolved edge attribute. Not a stable single-edge lookup. | get_edge_attr<T>(edge_id, key) |
try_get_edge_attr<T>(u, v, key) | Same endpoint-based ambiguity as get_edge_attr<T>(u, v, key), but with optional-return behavior. | try_get_edge_attr<T>(edge_id, key) |
get_edge_numeric_attr(u, v, key) | Same endpoint-based ambiguity as the other edge lookup helpers. | get_edge_numeric_attr(edge_id, key) |
G[u][v] | Proxy convenience form only. Do not treat as a stable handle to one parallel edge. | edge_ids(u, v) then edge_id APIs |
G[u][v]["key"] | Proxy convenience form only. Not precise per-parallel-edge targeting. | set_edge_attr(edge_id, key, value) / get_edge_attr<T>(edge_id, key) |
Node and edge attributes are stored outside the BGL graph using std::any in ordered std::map-backed stores.
Current direction:
std::any remains the pragmatic storage model for nowedge_id-based access is the precise path while (u, v) access remains convenience-oriented| Function | Parameters | Returns | Description | Example |
|---|---|---|---|---|
has_node_attr | (u, key) | bool | Checks whether node u has attribute key. Here A is the attribute count stored on node u. | G.has_node_attr("A", "color") |
has_edge_attr | (u, v, key) | bool | Checks whether the resolved (u,v) edge has attribute key. In multigraphs, this uses the same non-stable (u, v) resolution as other edge lookups. | G.has_edge_attr("A","B","capacity") |
has_edge_attr | (edge_id, key) | bool | Checks whether a specific wrapper-tracked edge ID has attribute key. | G.has_edge_attr(eid, "capacity") |
get_node_attr<T> | (u, key) | T | Returns node attribute key with checked std::any_cast. Throws on missing key or type mismatch. | G.get_node_attr<int>("Rome", "population") |
get_edge_attr<T> | (u, v, key) | T | Returns edge attribute key with checked std::any_cast. In multigraphs, this resolves through one edge returned by boost::edge(u, v, g) and is not a stable single-parallel-edge API. | G.get_edge_attr<std::string>("A","B","company") |
get_edge_attr<T> | (edge_id, key) | T | Returns edge attribute key for one specific wrapper-tracked edge ID. | G.get_edge_attr<std::string>(eid, "company") |
try_get_node_attr<T> | (u, key) | std::optional<T> | Safe optional-return node attribute lookup. | G.try_get_node_attr<int>(1, "rank") |
try_get_edge_attr<T> | (u, v, key) | std::optional<T> | Safe optional-return edge attribute lookup. In multigraphs, this uses the same non-stable (u, v) resolution as get_edge_attr<T>. | G.try_get_edge_attr<long>(0,1,"capacity") |
try_get_edge_attr<T> | (edge_id, key) | std::optional<T> | Safe optional-return edge attribute lookup for one specific wrapper-tracked edge ID. | G.try_get_edge_attr<long>(eid, "capacity") |
get_edge_numeric_attr | (u, v, key) | double | Returns a signed integer, unsigned integer, floating-point edge attribute, or the built-in "weight" as double. In multigraphs, this follows the same non-stable (u, v) resolution path as the other edge lookup helpers. | G.get_edge_numeric_attr(0, 1, "capacity") |
get_edge_numeric_attr | (edge_id, key) | double | Returns a signed integer, unsigned integer, floating-point edge attribute, or built-in "weight" for one specific wrapper-tracked edge ID. | G.get_edge_numeric_attr(eid, "capacity") |
set_edge_attr<T> | (edge_id, key, value) | void | Sets one attribute on a specific wrapper-tracked edge ID after validating that the edge ID still exists. | G.set_edge_attr(eid, "capacity", 5L) |
set_edge_weight | (edge_id, weight) | void | Sets the built-in weight on a specific wrapper-tracked edge ID. | G.set_edge_weight(eid, 3.5) |
| Syntax | Meaning |
|---|---|
G[u][v] = w; | set built-in edge weight; in multigraphs this is not a stable single-parallel-edge handle |
auto w = (EdgeWeight)G[u][v]; | read built-in edge weight through proxy conversion; in multigraphs this follows the same non-stable (u, v) resolution path |
G[u][v]["key"] = value; | set an edge attribute; in multigraphs this is not yet a precise per-parallel-edge API |
auto x = (T)G[u][v]["key"]; | read an edge attribute through proxy conversion; in multigraphs this follows the same non-stable (u, v) resolution path |
G.node(u)["key"] = value; | set a node attribute |
auto x = (T)G.node(u)["key"]; | read a node attribute through proxy conversion |
For multigraph-safe edge access:
Proxy syntax is convenient for writes and demos.
For reads, prefer:
has_*_attrget_*_attr<T>try_get_*_attr<T>because they make type expectations and failure behavior much clearer.
This is the intended long-term direction for the current attribute system too:
std::any as the pragmatic storage backend unless a clearer replacement justifies a larger redesignFor a pragmatic comparison of the current model against future alternatives (std::variant, typed schema) and a conservative migration path, see the Attribute System Design Evaluation.
Weight-name note:
"weight" is treated specially"weight" refers to the built-in edge-weight propertyFor multigraphs, prefer the precise edge_id path whenever:
Why the attr-bearing endpoint forms now throw in multigraphs:
(u, v) is not enough to identify one concrete parallel edgeKeep endpoint-based (u, v) forms for:
These types are part of the public API and are worth knowing because they make some results easier to consume than raw BGL output.
| Type | Main fields / behavior | What it is for |
|---|---|---|
MaximumFlowResult<NodeID> | value, flow, edge_flows_by_id | max-flow total plus aggregate endpoint view and precise per-edge-ID flow map |
MinCostMaxFlowResult<NodeID> | flow, cost, edge_flows, edge_flows_by_id | min-cost max-flow total flow, cost, aggregate endpoint view, and precise per-edge-ID flows |
MinimumCutResult<NodeID> | value, reachable, non_reachable, cut_edges, cut_edge_ids | cut value, partition information, aggregate endpoint cut view, and precise cut-edge IDs |
SingleSourceShortestPathResult<NodeID, Distance> | ordered distance, predecessor, plus has_path_to(target) / path_to(target) | single-source shortest-path results in a C++-friendly shape with tree-based map bounds and on-demand path reconstruction |
lookup_map<Key, Value> | operator[], at, iterators over ordered storage | const-friendly ordered lookup wrapper returned by some component helpers; const operator[] throws like at() on missing keys |
indexed_lookup_map<Key, Value> | at, operator[], contains, iterators over key-sorted storage | const-friendly indexed result wrapper that preserves linear materialization while keeping O(log n) key lookup; operator[] is a read-style accessor and throws like at() on missing keys |
visitor | no-op hooks examine_vertex, tree_edge, back_edge | small visitor base for traversal entry points |
This is one of the design directions that makes nxpp more than a thin parity layer: some wrappers return results that are easier to work with directly in C++ than raw Boost primitives.
The public surface intentionally includes a few utility wrappers that are not best described as direct one-to-one ports of a single NetworkX or Boost entry point.
These wrappers exist because they make the result shape more usable from C++:
| Wrapper / helper | Why it exists |
|---|---|
SingleSourceShortestPathResult<NodeID, Distance> | Returns ordered distance / predecessor data plus on-demand path_to(...), instead of forcing eager all-path materialization or lower-level reconstruction code into the caller |
MaximumFlowResult<NodeID> | Bundles the total flow value with both an endpoint-keyed convenience view and a precise edge_id-keyed flow view |
MinimumCutResult<NodeID> | Bundles cut value, partitions, endpoint cut edges, and precise cut-edge IDs into one directly usable return object |
MinCostMaxFlowResult<NodeID> | Bundles total flow, total cost, an endpoint-keyed convenience view, and a precise edge_id-keyed flow view into one C++-friendly return type |
indexed_lookup_map<Key, Value> | Keeps linear materialization and ordered key lookup for public results without baking hash-table assumptions into the API |
degree_centrality() | Exposes a normalized C++-friendly wrapper result rather than raw lower-level bookkeeping |
pagerank() | Exposes a ready-to-consume PageRank wrapper result keyed by NodeID instead of forcing callers to manage property maps and iteration state directly |
betweenness_centrality() | Exposes normalized betweenness scores keyed by NodeID using a self-contained Brandes BFS implementation without requiring callers to configure BGL property maps |
two_sat_satisfiable(...) | Exposes a direct utility surface built on top of the SCC machinery instead of requiring users to assemble the implication-graph workflow themselves |
The intended project shape is:
Use this wrapper when you want:
NodeIDGenerated-reference example page:
Use these wrappers when you want the aggregate answer plus the structured side information in one return object, instead of re-deriving it from lower-level algorithm output.
In multigraphs:
flow and cut_edges are endpoint-keyed convenience viewsedge_flows_by_id and cut_edge_ids are the precise parallel-edge-safe viewGenerated-reference example pages:
This wrapper is the one-shot return shape for the min-cost flow helpers.
In multigraphs:
edge_flows is the endpoint-keyed aggregate convenience viewedge_flows_by_id is the precise parallel-edge-safe viewGenerated-reference example page:
indexed_lookup_map::operator[] is intentionally equivalent to at() and throws on missing keys. Use contains(key) or find(key) when absence is expected.
Use this wrapper when you want:
NodeID-keyed lookup without the public API depending on hash-table assumptionsGenerated-reference page:
For operations on an existing graph, the canonical form is method-based: G.foo(...).
| Function | Parameters | Returns | Description | Example |
|---|---|---|---|---|
bfs_edges | (start) | std::vector<std::pair<NodeID, NodeID>> | Runs BFS and returns discovered tree edges. | auto es = G.bfs_edges(0); |
bfs_tree | (start) | Graph<NodeID, EdgeWeight, Directed> | Builds a new graph containing the BFS tree rooted at start, preserving the source graph's edge-weight type. | auto T = G.bfs_tree(0); |
bfs_successors | (start) | indexed_lookup_map<NodeID, std::vector<NodeID>> | Groups BFS tree edges by parent with linear materialization and O(log n) key lookup. | auto s = G.bfs_successors(0); |
dfs_edges | (start) | std::vector<std::pair<NodeID, NodeID>> | Runs DFS and returns DFS tree edges. | auto es = G.dfs_edges(0); |
dfs_tree | (start) | Graph<NodeID, EdgeWeight, Directed> | Builds a new graph containing the DFS tree rooted at start, preserving the source graph's edge-weight type. | auto T = G.dfs_tree(0); |
dfs_predecessors | (start) | indexed_lookup_map<NodeID, NodeID> | Returns DFS predecessor map with linear materialization and O(log n) key lookup. | auto p = G.dfs_predecessors(0); |
dfs_successors | (start) | indexed_lookup_map<NodeID, std::vector<NodeID>> | Groups DFS tree edges by parent with linear materialization and O(log n) key lookup. | auto s = G.dfs_successors(0); |
| Function | Parameters | Returns | Description | Example |
|---|---|---|---|---|
breadth_first_search | (start, visitor) | void | Visitor-object BFS entry point. | G.breadth_first_search(0, vis); |
depth_first_search | (start, visitor) | void | Visitor-object DFS entry point. | G.depth_first_search(0, vis); |
bfs_visit | (start, on_vertex, on_tree_edge) | void | Callback-style BFS adapter around the visitor layer. | G.bfs_visit(0, on_v, on_e); |
dfs_visit | (start, on_tree_edge, on_back_edge) | void | Callback-style DFS adapter around the visitor layer. | G.dfs_visit(0, on_t, on_b); |
Use nxpp::WeightMode for new source-target shortest-path calls that need an explicit weighting choice:
WeightMode::Unweighted means edge-count shortest pathWeightMode::BuiltIn means the graph's built-in edge-weight propertyThe legacy string "weight" has a narrow compatibility meaning:
That means calls such as:
shortest_path(..., "weight")dijkstra_path(..., "weight")bellman_ford_path(..., "weight")still route through the built-in weighted edge channel rather than an arbitrary user-defined numeric edge attribute.
| Function | Parameters | Returns | Description | Example |
|---|---|---|---|---|
shortest_path | (source, target) | std::vector<NodeID> | Unweighted shortest path by edge count, with guarded path reconstruction. | auto p = G.shortest_path(0, 3); |
shortest_path_length | (source, target) | double | Unweighted shortest-path length in edge count. | auto d = G.shortest_path_length(0, 3); |
shortest_path | (source, target, WeightMode) | std::vector<NodeID> | Explicit mode overload for edge-count or built-in-weight shortest paths. | auto p = G.shortest_path(0, 3, nxpp::WeightMode::BuiltIn); |
shortest_path_length | (source, target, WeightMode) | double | Explicit mode overload for edge-count or built-in-weight shortest-path length. | auto d = G.shortest_path_length(0, 3, nxpp::WeightMode::BuiltIn); |
shortest_path | (source, target, "weight") | std::vector<NodeID> | Weighted shortest path through the built-in edge weight. The string "weight" is a compatibility name for the built-in weight property, not an arbitrary custom key. | auto p = G.shortest_path(0, 3, "weight"); |
shortest_path_length | (source, target, "weight") | double | Weighted shortest-path length through the built-in edge weight. The string "weight" is a compatibility name for the built-in weight property, not an arbitrary custom key. | auto d = G.shortest_path_length(0, 3, "weight"); |
dijkstra_path | (source, target) | std::vector<NodeID> | Direct Dijkstra source-target path wrapper with guarded path reconstruction. | auto p = G.dijkstra_path(0, 3); |
dijkstra_path | (source, target, WeightMode) | std::vector<NodeID> | Explicit mode overload; BuiltIn uses Dijkstra and Unweighted routes to the edge-count helper. | auto p = G.dijkstra_path(0, 3, nxpp::WeightMode::BuiltIn); |
dijkstra_path | (source, target, "weight") | std::vector<NodeID> | Same as above; explicit "weight" overload for compatibility-shaped usage around the built-in edge weight. | auto p = G.dijkstra_path(0, 3, "weight"); |
dijkstra_path_length | (source, target) | Distance | Dijkstra distance to one target. | auto d = G.dijkstra_path_length(0, 3); |
dijkstra_path_length | (source, target, WeightMode) | Distance | Explicit mode overload; BuiltIn uses Dijkstra and Unweighted returns edge-count distance converted to the graph weight type. | auto d = G.dijkstra_path_length(0, 3, nxpp::WeightMode::BuiltIn); |
dijkstra_path_length | (source, target, "weight") | Distance | Same as above with explicit "weight" overload around the built-in edge weight. | auto d = G.dijkstra_path_length(0, 3, "weight"); |
bellman_ford_path | (source, target) | std::vector<NodeID> | Bellman-Ford path wrapper with guarded path reconstruction. Throws on negative cycle. | auto p = G.bellman_ford_path(0, 3); |
bellman_ford_path | (source, target, WeightMode) | std::vector<NodeID> | Explicit mode overload; BuiltIn uses Bellman-Ford and Unweighted routes to the edge-count helper. | auto p = G.bellman_ford_path(0, 3, nxpp::WeightMode::BuiltIn); |
bellman_ford_path | (source, target, "weight") | std::vector<NodeID> | Same as above with explicit "weight" overload around the built-in edge weight. | auto p = G.bellman_ford_path(0, 3, "weight"); |
bellman_ford_path_length | (source, target) | Distance | Bellman-Ford distance wrapper with a final accumulation over the reconstructed path. | auto d = G.bellman_ford_path_length(0, 3); |
bellman_ford_path_length | (source, target, WeightMode) | Distance | Explicit mode overload; BuiltIn uses Bellman-Ford and Unweighted returns edge-count distance converted to the graph weight type. | auto d = G.bellman_ford_path_length(0, 3, nxpp::WeightMode::BuiltIn); |
bellman_ford_path_length | (source, target, "weight") | Distance | Same as above with explicit "weight" overload around the built-in edge weight. | auto d = G.bellman_ford_path_length(0, 3, "weight"); |
| Function | Parameters | Returns | Description | Example |
|---|---|---|---|---|
dijkstra_shortest_paths | (source) | SingleSourceShortestPathResult<NodeID, Distance> | Returns distances and predecessors, with on-demand path reconstruction through the result wrapper. | auto r = G.dijkstra_shortest_paths(0); |
single_source_dijkstra | (source) | SingleSourceShortestPathResult<NodeID, Distance> | Thin alias to dijkstra_shortest_paths. | auto r = G.single_source_dijkstra(0); |
bellman_ford_shortest_paths | (source) | SingleSourceShortestPathResult<NodeID, Distance> | Returns distances and predecessors, with on-demand path reconstruction through the result wrapper. | auto r = G.bellman_ford_shortest_paths(0); |
single_source_bellman_ford | (source) | SingleSourceShortestPathResult<NodeID, Distance> | Thin alias to bellman_ford_shortest_paths. | auto r = G.single_source_bellman_ford(0); |
dag_shortest_paths | (source) | SingleSourceShortestPathResult<NodeID, Distance> | DAG shortest-path helper returning distances and predecessors, with on-demand path reconstruction through the result wrapper. | auto r = G.dag_shortest_paths(0); |
floyd_warshall_all_pairs_shortest_paths | () | std::vector<std::vector<Distance>> | Returns an all-pairs distance matrix. | auto fw = G.floyd_warshall_all_pairs_shortest_paths(); |
floyd_warshall_all_pairs_shortest_paths_map | () | std::map<NodeID, std::map<NodeID, Distance>> | Convenience map wrapper around the Floyd-Warshall matrix. | auto fw = G.floyd_warshall_all_pairs_shortest_paths_map(); |
| Function | Parameters | Returns | Description | Example |
|---|---|---|---|---|
connected_component_groups | () | std::vector<std::vector<NodeID>> | Groups vertices by connected component. | auto cc = G.connected_component_groups(); |
connected_components | () | indexed_lookup_map<NodeID, int> | Returns node -> component_id with linear materialization and O(log n) key lookup. | auto map = G.connected_components(); |
connected_component_map | () | indexed_lookup_map<NodeID, int> | Thin alias to connected_components. | auto map = G.connected_component_map(); |
strongly_connected_component_groups | () | std::vector<std::vector<NodeID>> | Groups vertices by SCC. | auto scc = G.strongly_connected_component_groups(); |
strongly_connected_components | () | std::vector<std::vector<NodeID>> | Thin alias to grouped SCC output. | auto scc = G.strongly_connected_components(); |
strong_component_map | () | indexed_lookup_map<NodeID, int> | Returns node -> component_id for SCCs with linear materialization and O(log n) key lookup. | auto map = G.strong_component_map(); |
strongly_connected_component_map | () | indexed_lookup_map<NodeID, int> | Thin alias to strong_component_map. | auto map = G.strongly_connected_component_map(); |
strong_components | () | indexed_lookup_map<NodeID, NodeID> | Returns a representative/root per SCC with linear materialization and O(log n) key lookup. | auto roots = G.strong_components(); |
strongly_connected_component_roots | () | indexed_lookup_map<NodeID, NodeID> | Thin alias to SCC root map. | auto roots = G.strongly_connected_component_roots(); |
| Function | Parameters | Returns | Description | Example |
|---|---|---|---|---|
topological_sort | () | std::vector<NodeID> | Returns a topological ordering. | auto order = G.topological_sort(); |
kruskal_minimum_spanning_tree | () | std::vector<std::pair<NodeID, NodeID>> | Returns MST edges as pairs. | auto mst = G.kruskal_minimum_spanning_tree(); |
prim_minimum_spanning_tree | (root) | std::map<NodeID, NodeID> | Returns a node -> parent map rooted at root; the root maps to itself and should be skipped when treating entries as edges. | auto p = G.prim_minimum_spanning_tree(0); |
minimum_spanning_tree | () | std::vector<std::pair<NodeID, NodeID>> | Thin default wrapper delegating to Kruskal. | auto mst = G.minimum_spanning_tree(); |
minimum_spanning_tree | (root) | std::map<NodeID, NodeID> | Thin rooted wrapper delegating to Prim, including the same root self-entry. | auto p = G.minimum_spanning_tree(0); |
The flow/cost wrappers follow the same rule:
weight_attr = "weight" refers to the built-in edge-weight propertySo the "weight" default in min-cost-flow helpers should be read as a narrow built-in cost channel, not as an open-ended attribute-name policy.
Flow capacity attributes must be non-negative integral values representable as long. Fractional, negative, non-finite, non-numeric, and out-of-range values are rejected during flow-graph setup instead of being silently truncated.
| Function | Parameters | Returns | Description | Example |
|---|---|---|---|---|
edmonds_karp_maximum_flow | (source, sink, capacity_attr = "capacity") | MaximumFlowResult<NodeID> | Max-flow wrapper returning total flow plus both an endpoint-keyed convenience view and a precise edge_id-keyed flow view. | auto f = G.edmonds_karp_maximum_flow(0, 5); |
maximum_flow | (source, sink, capacity_attr = "capacity") | MaximumFlowResult<NodeID> | Backward-compatible default max-flow wrapper returning both aggregate endpoint and precise edge_id views. | auto f = G.maximum_flow(0, 5); |
push_relabel_maximum_flow_result | (source, sink, capacity_attr = "capacity") | MaximumFlowResult<NodeID> | Push-Relabel wrapper returning both aggregate endpoint and precise edge_id views. | auto f = G.push_relabel_maximum_flow_result(0, 5); |
minimum_cut | (source, sink, capacity_attr = "capacity") | MinimumCutResult<NodeID> | Returns cut value, partition, endpoint cut-edge view, and precise cut-edge IDs. In multigraph mode the internal capacity builder now uses precise edge_id lookup per concrete edge instance. | auto c = G.minimum_cut(0, 5); |
| Function | Parameters | Returns | Description | Example |
|---|---|---|---|---|
push_relabel_maximum_flow | (source, sink, capacity_attr = "capacity", weight_attr = "weight") | long | Computes max flow and stages residual state for a later cycle_canceling(). Any later graph mutation invalidates that staged state. The default "weight" still refers to the built-in edge-weight property. | long f = G.push_relabel_maximum_flow(0, 5); |
cycle_canceling | (weight_attr = "weight") | deduced cost type | Runs cycle-canceling over staged state prepared by push_relabel_maximum_flow. If the graph changed in the meantime, this now throws and asks the caller to rerun the push-relabel stage first. The default "weight" still refers to the built-in edge-weight property. | long c = G.cycle_canceling(); |
The staged min-cost-flow cache used by this path is synchronized internally, so different graph instances of the same type do not race on the cache container. This synchronization is not a general graph thread-safety guarantee. The graph object itself still requires external synchronization for concurrent mutation, and callers should serialize push_relabel_maximum_flow(...) / cycle_canceling(...) staged-flow sequences that operate on the same instance.
| Function | Parameters | Returns | Description | Example |
|---|---|---|---|---|
max_flow_min_cost_cycle_canceling | (source, sink, capacity_attr = "capacity", weight_attr = "weight") | MinCostMaxFlowResult<NodeID> | One-shot min-cost max-flow wrapper using cycle canceling. Returns both an endpoint-keyed aggregate view and a precise edge_id-keyed flow view. The default "weight" still refers to the built-in edge-weight property. | auto r = G.max_flow_min_cost_cycle_canceling(0, 5); |
successive_shortest_path_nonnegative_weights | (source, sink, capacity_attr = "capacity", weight_attr = "weight") | MinCostMaxFlowResult<NodeID> | One-shot min-cost max-flow wrapper using SSP. Returns both an endpoint-keyed aggregate view and a precise edge_id-keyed flow view. The default "weight" still refers to the built-in edge-weight property. | auto r = G.successive_shortest_path_nonnegative_weights(0, 5); |
max_flow_min_cost_successive_shortest_path | (source, sink, capacity_attr = "capacity", weight_attr = "weight") | MinCostMaxFlowResult<NodeID> | Thin alias to the SSP wrapper. Returns both an endpoint-keyed aggregate view and a precise edge_id-keyed flow view. The default "weight" still refers to the built-in edge-weight property. | auto r = G.max_flow_min_cost_successive_shortest_path(0, 5); |
max_flow_min_cost | (source, sink, capacity_attr = "capacity", weight_attr = "weight") | MinCostMaxFlowResult<NodeID> | Default min-cost max-flow wrapper; currently delegates to cycle canceling. Returns both an endpoint-keyed aggregate view and a precise edge_id-keyed flow view. The default "weight" still refers to the built-in edge-weight property. | auto r = G.max_flow_min_cost(0, 5); |
These are good examples of public helpers that are useful in real C++ code even when they are not exact one-to-one ports of a single NetworkX or BGL entry point.
| Function | Parameters | Returns | Description | Example |
|---|---|---|---|---|
complete_graph | (n) | GraphType | Generates a complete graph for the chosen graph type template. | auto K5 = nxpp::complete_graph(5); |
path_graph | (n) | GraphType | Generates a path graph. | auto P4 = nxpp::path_graph(4); |
erdos_renyi_graph | (n, p, seed = 42) | GraphType | Generates an Erdős–Rényi random graph and preserves isolated nodes. | auto G = nxpp::erdos_renyi_graph(100, 0.05); |
num_vertices | () | int | Convenience wrapper over boost::num_vertices. | auto n = G.num_vertices(); |
num_edges | () | std::size_t | Convenience wrapper over boost::num_edges that avoids edges().size() allocation. | auto m = G.num_edges(); |
degree_centrality | () | indexed_lookup_map<NodeID, double> | Returns degree centrality with NetworkX-like normalization by n - 1, using linear materialization plus O(log n) key lookup. | auto c = G.degree_centrality(); |
pagerank | () | indexed_lookup_map<NodeID, double> | Returns PageRank scores keyed by NodeID, using a small fixed-iteration wrapper result instead of raw property-map plumbing. | auto rank = G.pagerank(); |
betweenness_centrality | () | indexed_lookup_map<NodeID, double> | Returns normalized betweenness centrality for each node, matching NetworkX betweenness_centrality(G, normalized=True) semantics. Implemented via Brandes BFS without BGL property-map setup. | auto bc = G.betweenness_centrality(); |
to_2sat_vertex_id | (literal) | int | Internal/public helper mapping a literal to its implication-graph vertex index. Throws std::invalid_argument for literal 0, which is not valid in 2-SAT. | auto id = nxpp::to_2sat_vertex_id(-2); |
two_sat_satisfiable | (num_variables, clauses) | bool | 2-SAT satisfiability helper built on SCC computation. | bool ok = nxpp::two_sat_satisfiable(2, {{1,2},{-1,2}}); |