Skip to content

Add edge mutation and MutableSet interface for graph nodes#1850

Open
Andy-Jost wants to merge 8 commits intoNVIDIA:mainfrom
Andy-Jost:graph-edge-mutation
Open

Add edge mutation and MutableSet interface for graph nodes#1850
Andy-Jost wants to merge 8 commits intoNVIDIA:mainfrom
Andy-Jost:graph-edge-mutation

Conversation

@Andy-Jost
Copy link
Copy Markdown
Contributor

@Andy-Jost Andy-Jost commented Apr 2, 2026

Summary

Implements step 2 of #1330 (graph updates): edge mutation on GraphDef. Graph node adjacencies (pred/succ) are now exposed as AdjacencySetProxy, a collections.abc.MutableSet proxy backed by cuGraphAddDependencies/cuGraphRemoveDependencies. Nodes can be removed via GraphNode.destroy() (wraps cuGraphDestroyNode), and property setters on pred/succ support bulk edge replacement.

Examples

# Inspect adjacencies
b in node.pred     # predecessors
b in node.succ     # successors

# Mutate edges
node.succ.add(b)           # add a single edge
node.succ |= {b, c}        # bulk add
node.succ.update({b, c})   # same thing
node.pred.discard(a)       # remove one predecessor
node.pred -= {a, b}        # bulk remove
node.succ = {x, y}         # replace all successors
node.pred.clear()          # remove all predecessors

# Remove a node and all its edges from a graph
node.destroy()

Changes

  • AdjacencySetProxy: full MutableSet implementation with bulk-efficient clear(), __isub__(), __ior__(), update(), and remove_edges(). Includes _from_iterable override for binary ops and duplicate-add guard to avoid CUDA_ERROR_INVALID_VALUE.
  • GraphNode.destroy(): removes a node and its edges from the graph definition via cuGraphDestroyNode, then zeroes the underlying handle to prevent stale memory access. Safe to call on already-destroyed nodes. Properties (type, pred, succ, handle) degrade gracefully to None or empty sets.
  • GraphNode.is_valid: returns False after destroy() has been called
  • GraphNode.pred/succ setters: clear existing edges and add new ones in bulk
  • GraphDef.nodes()/edges(): return set instead of tuple for consistency
  • Test helpers: compile_parallel_kernels() in graph_kernels.py, assert_mutable_set_interface() in collection_interface_testers.py

Test Coverage

  • TestMutateYRig class: baseline, node destroy (single/multiple), edge insertion, join-node removal with rewiring
  • test_destroyed_node: verifies handle invalidation, graceful property degradation, edge-add failure on destroyed nodes, and double-destroy safety
  • test_adjacency_set_interface: exercises every MutableSet method against a reference set
  • test_adjacency_set_pred_direction: verifies bidirectional edge visibility
  • test_adjacency_set_property_setter: verifies bulk edge replacement via setters
  • test_add_wrong_type, test_cross_graph_edge, test_self_edge: error-path coverage
  • test_convert_linear_to_fan_in: rewires a sequential graph into parallel fan-in topology

Related Work

@Andy-Jost Andy-Jost added this to the cuda.core v1.0.0 milestone Apr 2, 2026
@Andy-Jost Andy-Jost added P0 High priority - Must do! feature New feature or request cuda.core Everything related to the cuda.core module labels Apr 2, 2026
@Andy-Jost Andy-Jost self-assigned this Apr 2, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 2, 2026

@Andy-Jost Andy-Jost marked this pull request as draft April 2, 2026 14:39
@copy-pr-bot
Copy link
Copy Markdown
Contributor

copy-pr-bot bot commented Apr 2, 2026

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

Rename test files to reflect what they actually test:
- test_basic -> test_graph_builder (stream capture tests)
- test_conditional -> test_graph_builder_conditional
- test_advanced -> test_graph_update (moved child_graph and
  stream_lifetime tests into test_graph_builder)
- test_capture_alloc -> test_graph_memory_resource
- test_explicit* -> test_graphdef*

Made-with: Cursor
- Extend Graph.update() to accept both GraphBuilder and GraphDef sources
- Surface CUgraphExecUpdateResultInfo details on update failure instead
  of a generic CUDA_ERROR_GRAPH_EXEC_UPDATE_FAILURE message
- Release the GIL during cuGraphExecUpdate via nogil block
- Add parametrized happy-path test covering both GraphBuilder and GraphDef
- Add error-case tests: unfinished builder, topology mismatch, wrong type

Made-with: Cursor
Replace cached tuple-based pred/succ with mutable AdjacencySet backed
by direct CUDA driver calls. Add GraphNode.remove() wrapping
cuGraphDestroyNode.

Made-with: Cursor
…cencies

Enable adding/removing edges between graph nodes via AdjacencySet (a
MutableSet proxy on GraphNode.pred/succ), node removal via discard(),
and property setters for bulk edge replacement. Includes comprehensive
mutation and interface tests.

Closes part of NVIDIA#1330 (step 2: edge mutation on GraphDef).

Made-with: Cursor
Replace inline skipif version check with requires_module(np, "2.1")
from the shared test helpers, consistent with other test files.

Made-with: Cursor
@Andy-Jost Andy-Jost force-pushed the graph-edge-mutation branch from 4b837a9 to 3aef7e0 Compare April 2, 2026 16:47
Rename class and file to AdjacencySetProxy to clarify write-through
semantics. Add bulk-efficient clear(), __isub__(), __ior__() overrides
and remove_edges() on the Cython core. Guard GraphNode.discard() against
double-destroy via membership check. Filter duplicates in update(). Add
error-path tests for wrong types, cross-graph edges, and self-edges.

Made-with: Cursor
@Andy-Jost Andy-Jost force-pushed the graph-edge-mutation branch from 3aef7e0 to 8554d30 Compare April 2, 2026 17:03
@Andy-Jost
Copy link
Copy Markdown
Contributor Author

/ok to test

@Andy-Jost Andy-Jost requested review from mdboom and rparolin April 2, 2026 18:14
@Andy-Jost Andy-Jost marked this pull request as ready for review April 2, 2026 18:15
…INEL

Replace discard() with destroy() which calls cuGraphDestroyNode and then
zeroes the CUgraphNode resource in the handle box via
invalidate_graph_node_handle. This prevents stale memory access on
destroyed nodes. Properties (type, pred, succ, handle) degrade gracefully
to None/empty for destroyed nodes.

Remove the GRAPH_NODE_SENTINEL (0x1) approach in favor of using NULL for
both sentinels and destroyed nodes, which is simpler and avoids the risk
of passing 0x1 to driver APIs that treat it as a valid pointer.

Made-with: Cursor
GraphHandle graph_node_get_graph(const GraphNodeHandle& h) noexcept;

// Zero the CUgraphNode resource inside the handle, marking it invalid.
void invalidate_graph_node_handle(const GraphNodeHandle& h) noexcept;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make the invalidate_graph_node_handle take a mutable reference? Rather than internally mutating through a const& handle?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cuda.core Everything related to the cuda.core module feature New feature or request P0 High priority - Must do!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants