Skip to content

Publish the graph API as cuda.core.graph#1858

Open
Andy-Jost wants to merge 31 commits intoNVIDIA:mainfrom
Andy-Jost:graph-public-api
Open

Publish the graph API as cuda.core.graph#1858
Andy-Jost wants to merge 31 commits intoNVIDIA:mainfrom
Andy-Jost:graph-public-api

Conversation

@Andy-Jost
Copy link
Copy Markdown
Contributor

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

Summary

Graduates the internal cuda.core._graph package to the public cuda.core.graph module.

Closes #111.

Changes

Public API (cuda.core.graph)

  • Renamed cuda.core._graph to cuda.core.graph and flattened the contents (removed the _graph_def/ subdirectory). All public types (GraphDef, GraphNode, Condition, GraphAllocOptions, node subclasses) are now importable from cuda.core.graph
  • Re-exported core types (Graph, GraphBuilder, GraphDef, Condition, GraphAllocOptions, GraphCompleteOptions, GraphDebugPrintOptions) from cuda.core top-level

Docs and references

  • New "CUDA graphs" section in api.rst covering all graph types and 15 node subclasses
  • AdjacencySetProxy added to api_private.rst
  • Fixed 26 stale :obj:\~_graph.X`Sphinx cross-references →:obj:`~graph.X``
  • Release notes for 0.7.x

Test Coverage

  • 314 graph tests pass (mutation, identity, lifetime, integration, errors, options)
  • 2540 non-graph tests pass (full regression)

Related Work

Made with Cursor

Andy-Jost added 18 commits April 2, 2026 09:43
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
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
…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
Nodes retrieved via GraphDef.nodes(), edges(), or pred/succ traversal
now return the same Python object that was originally created, enabling
identity checks with `is`. A C++ HandleRegistry deduplicates
CUgraphNode handles, and a Cython WeakValueDictionary caches the
Python wrapper objects.

Made-with: Cursor
Move the graph package from cuda.core._graph to cuda.core.graph and
flatten the _graph_def subdirectory. GraphDef, Condition, GraphAllocOptions,
GraphNode, and all node subclasses are now importable from cuda.core.graph.
GraphDef, Condition, and GraphAllocOptions are also re-exported from
cuda.core directly.

Break the circular import (_stream → graph → _graph_node →
_kernel_arg_handler → _buffer → _device → _stream) by making _device.pyx
and _stream.pyx use local imports for GraphBuilder.

Made-with: Cursor
…sion

Sentinel (entry) nodes use NULL as their CUgraphNode, so caching them
under a NULL key caused all sentinels across different graphs to share
the same handle. This made nodes built from the wrong graph's entry
point, causing CUDA_ERROR_INVALID_VALUE for conditional nodes and hash
collisions in equality tests.

Made-with: Cursor
When a node is destroyed, the driver may reuse its CUgraphNode pointer
for a new node. Without unregistering the old entry, the registry
returns a stale handle pointing to the wrong node type and graph.

Made-with: Cursor
The _graph -> graph rename left behind broken Sphinx cross-references
and the new graph types were missing from the API reference.

Made-with: Cursor
@Andy-Jost Andy-Jost added this to the cuda.core v0.7.0 milestone Apr 3, 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 3, 2026
@Andy-Jost Andy-Jost self-assigned this Apr 3, 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 3, 2026
@copy-pr-bot
Copy link
Copy Markdown
Contributor

copy-pr-bot bot commented Apr 3, 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.

@Andy-Jost Andy-Jost changed the title Make graph API public: cuda.core.graph module with edge mutation and node identity Publish the graph API as cuda.core.graph Apr 3, 2026
@Andy-Jost
Copy link
Copy Markdown
Contributor Author

/ok to test

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 3, 2026

Andy-Jost added 12 commits April 3, 2026 13:14
Every subclass repr now starts with handle=0x... (the CUgraphNode
pointer) followed by type-specific identity/parameter data. Dynamic
queries (pred counts, subnode counts) are removed in favor of
deterministic, cheap fields. This makes set comparison failures in
test output readable when debugging graph mutation tests.

Made-with: Cursor
Resolve conflicts: keep _cached() wrappers and _node_cache.pop() from
graph-node-identity, keep identity comparison (is not) in test.

Made-with: Cursor
Aligns Python-side terminology with the C++ graph_node_registry.

Made-with: Cursor
unregister_handle: remove the expired() guard that prevented erasure
when the shared_ptr was still alive. This caused stale registry
entries after destroy(), leading to CUDA_ERROR_INVALID_VALUE when
the driver reused CUgraphNode pointer values.

Rename invalidate_graph_node_handle -> invalidate_graph_node for
consistency with the rest of the graph node API.

Made-with: Cursor
Add _AdjacencySetCore.contains() that checks membership by comparing
raw CUgraphNode handles at the C level, avoiding Python object
construction. Uses a 16-element stack buffer for a single driver call
in the common case.

Move the type check in update() inline next to the extend loop so
invalid input is rejected immediately.

Made-with: Cursor
- Add GraphDef.empty() for creating entry-point empty nodes; replace
  all no-arg join() calls on GraphDef with empty() in tests.
- Optimize _AdjacencySetCore.query() to use a 16-element stack buffer,
  matching the contains() optimization.
- Add test_registry_cleanup exercising destroy(), graph deletion, and
  weak-reference cleanup of the node registry.

Made-with: Cursor
Add REGISTRY_DESIGN.md explaining how the C++ HandleRegistry (Level 1)
and Cython _node_registry (Level 2) work together to preserve Python
object identity through driver round-trips. Add cross-references at
each registry instantiation site.

Made-with: Cursor
Pre-allocate vectors to 128 entries and pass them on the first call.
Only fall back to a second call if the graph exceeds 128 nodes/edges.

Made-with: Cursor
@Andy-Jost
Copy link
Copy Markdown
Contributor Author

/ok to test

- Rename "Memory" section to "Memory management" in api.rst
- Add introductory paragraph under "Node types" subsection
- Update node docstrings to concise noun phrases
- Update graph API class docstrings (GraphBuilder, Graph, etc.)
- Revise release notes highlights and new features sections
- Fix test_registry_cleanup import path (cuda.core._graph -> cuda.core.graph)

Made-with: Cursor
@Andy-Jost
Copy link
Copy Markdown
Contributor Author

/ok to test

@Andy-Jost Andy-Jost marked this pull request as ready for review April 4, 2026 00:41
@Andy-Jost Andy-Jost requested a review from leofang April 4, 2026 00:41
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.

[EPIC] Support CUDA graphs

1 participant