Coverage for cuda/core/graph/_graph_builder.pyx: 89.33%

431 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-19 01:12 +0000

1# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 

2# 

3# SPDX-License-Identifier: Apache-2.0 

4  

5from dataclasses import dataclass 

6from typing import TYPE_CHECKING 

7  

8from libc.stdint cimport intptr_t 

9  

10from cuda.bindings cimport cydriver 

11  

12from cuda.core.graph._graph_definition cimport GraphCondition, GraphDefinition 

13from cuda.core.graph._host_callback cimport _attach_host_callback_owners, _resolve_host_callback 

14from cuda.core._resource_handles cimport ( 

15 GraphHandle, 

16 OpaqueHandle, 

17 as_cu, as_py, 

18 create_graph_exec_handle, create_graph_handle, create_graph_handle_ref, 

19) 

20from cuda.core._stream cimport Stream 

21from cuda.core._utils.cuda_utils cimport HANDLE_RETURN 

22from cuda.core._utils.version cimport cy_binding_version, cy_driver_version 

23  

24from cuda.core._utils.cuda_utils import ( 

25 CUDAError, 

26 driver, 

27 handle_return, 

28) 

29  

30if TYPE_CHECKING: 

31 from cuda.core.graph._graph_definition import GraphDefinition 

32  

33__all__ = ['Graph', 'GraphBuilder', 'GraphCompleteOptions', 'GraphDebugPrintOptions'] 

34  

35  

36@dataclass 

37class GraphDebugPrintOptions: 

38 """Options for debug_dot_print(). 

39  

40 Attributes 

41 ---------- 

42 verbose : bool 

43 Output all debug data as if every debug flag is enabled (Default to False) 

44 runtime_types : bool 

45 Use CUDA Runtime structures for output (Default to False) 

46 kernel_node_params : bool 

47 Adds kernel parameter values to output (Default to False) 

48 memcpy_node_params : bool 

49 Adds memcpy parameter values to output (Default to False) 

50 memset_node_params : bool 

51 Adds memset parameter values to output (Default to False) 

52 host_node_params : bool 

53 Adds host parameter values to output (Default to False) 

54 event_node_params : bool 

55 Adds event parameter values to output (Default to False) 

56 ext_semas_signal_node_params : bool 

57 Adds external semaphore signal parameter values to output (Default to False) 

58 ext_semas_wait_node_params : bool 

59 Adds external semaphore wait parameter values to output (Default to False) 

60 kernel_node_attributes : bool 

61 Adds kernel node attributes to output (Default to False) 

62 handles : bool 

63 Adds node handles and every kernel function handle to output (Default to False) 

64 mem_alloc_node_params : bool 

65 Adds memory alloc parameter values to output (Default to False) 

66 mem_free_node_params : bool 

67 Adds memory free parameter values to output (Default to False) 

68 batch_mem_op_node_params : bool 

69 Adds batch mem op parameter values to output (Default to False) 

70 extra_topo_info : bool 

71 Adds edge numbering information (Default to False) 

72 conditional_node_params : bool 

73 Adds conditional node parameter values to output (Default to False) 

74  

75 """ 

76  

77 verbose: bool = False 

78 runtime_types: bool = False 

79 kernel_node_params: bool = False 

80 memcpy_node_params: bool = False 

81 memset_node_params: bool = False 

82 host_node_params: bool = False 

83 event_node_params: bool = False 

84 ext_semas_signal_node_params: bool = False 

85 ext_semas_wait_node_params: bool = False 

86 kernel_node_attributes: bool = False 

87 handles: bool = False 

88 mem_alloc_node_params: bool = False 

89 mem_free_node_params: bool = False 

90 batch_mem_op_node_params: bool = False 

91 extra_topo_info: bool = False 

92 conditional_node_params: bool = False 

93  

94 def _to_flags(self) -> int: 

95 """Convert options to CUDA driver API flags (internal use).""" 

96 flags = 0 2xba

97 if self.verbose: 2xba

98 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_VERBOSE 2xba

99 if self.runtime_types: 2xba

100 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_RUNTIME_TYPES 1a

101 if self.kernel_node_params: 2xba

102 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_KERNEL_NODE_PARAMS 1a

103 if self.memcpy_node_params: 2xba

104 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_MEMCPY_NODE_PARAMS 1a

105 if self.memset_node_params: 2xba

106 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_MEMSET_NODE_PARAMS 1a

107 if self.host_node_params: 2xba

108 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_HOST_NODE_PARAMS 1a

109 if self.event_node_params: 2xba

110 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_EVENT_NODE_PARAMS 1a

111 if self.ext_semas_signal_node_params: 2xba

112 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_EXT_SEMAS_SIGNAL_NODE_PARAMS 1a

113 if self.ext_semas_wait_node_params: 2xba

114 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_EXT_SEMAS_WAIT_NODE_PARAMS 1a

115 if self.kernel_node_attributes: 2( xba

116 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_KERNEL_NODE_ATTRIBUTES 1a

117 if self.handles: 2xba

118 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_HANDLES 2xba

119 if self.mem_alloc_node_params: 2xba

120 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_MEM_ALLOC_NODE_PARAMS 1a

121 if self.mem_free_node_params: 2xba

122 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_MEM_FREE_NODE_PARAMS 1a

123 if self.batch_mem_op_node_params: 2xba

124 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_BATCH_MEM_OP_NODE_PARAMS 1a

125 if self.extra_topo_info: 2xba

126 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_EXTRA_TOPO_INFO 1a

127 if self.conditional_node_params: 2xba

128 flags |= driver.CUgraphDebugDot_flags.CU_GRAPH_DEBUG_DOT_FLAGS_CONDITIONAL_NODE_PARAMS 1a

129 return flags 2xba

130  

131  

132@dataclass 

133class GraphCompleteOptions: 

134 """Options for graph instantiation. 

135  

136 Attributes 

137 ---------- 

138 auto_free_on_launch : bool, optional 

139 Automatically free memory allocated in a graph before relaunching. (Default to False) 

140 upload_stream : Stream, optional 

141 Stream to use to automatically upload the graph after completion. (Default to None) 

142 device_launch : bool, optional 

143 Configure the graph to be launchable from the device. This flag can only 

144 be used on platforms which support unified addressing. This flag cannot be 

145 used in conjunction with auto_free_on_launch. (Default to False) 

146 use_node_priority : bool, optional 

147 Run the graph using the per-node priority attributes rather than the 

148 priority of the stream it is launched into. (Default to False) 

149  

150 """ 

151  

152 auto_free_on_launch: bool = False 

153 upload_stream: Stream | None = None 

154 device_launch: bool = False 

155 use_node_priority: bool = False 

156  

157  

158def _instantiate_graph(h_graph, options: GraphCompleteOptions | None = None) -> Graph: 

159 cdef cydriver.CUgraphExec c_exec 

160 params = driver.CUDA_GRAPH_INSTANTIATE_PARAMS() 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

161 if options: 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

162 flags = 0 2` { fb| } gb~ abbbhbcbdbibsbtbQ N O S

163 if options.auto_free_on_launch: 2` { fb| } gb~ abbbhbcbdbibsbtbQ N O S

164 flags |= driver.CUgraphInstantiate_flags.CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH 2` | abcbsbtbQ N O S

165 if options.upload_stream: 2` { fb| } gb~ abbbhbcbdbibsbtbQ N O S

166 flags |= driver.CUgraphInstantiate_flags.CUDA_GRAPH_INSTANTIATE_FLAG_UPLOAD 2fbgbhbibS

167 params.hUploadStream = options.upload_stream.handle 2fbgbhbibS

168 if options.device_launch: 2` { fb| } gb~ abbbhbcbdbibsbtbQ N O S

169 flags |= driver.CUgraphInstantiate_flags.CUDA_GRAPH_INSTANTIATE_FLAG_DEVICE_LAUNCH 1~S

170 if options.use_node_priority: 2` { fb| } gb~ abbbhbcbdbibsbtbQ N O S

171 flags |= driver.CUgraphInstantiate_flags.CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY 2{ } bbdbsbtbS

172 params.flags = flags 2` { fb| } gb~ abbbhbcbdbibsbtbQ N O S

173  

174 py_exec = handle_return(driver.cuGraphInstantiateWithParams(h_graph, params)) 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

175 # Check result_out before wrapping the exec: on a non-SUCCESS result the exec 

176 # may be invalid, and Graph._init's RAII deleter would call cuGraphExecDestroy 

177 # on it during the exception unwind below. 

178 if params.result_out == driver.CUgraphInstantiateResult.CUDA_GRAPH_INSTANTIATE_ERROR: 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

179 raise RuntimeError( 

180 "Instantiation failed for an unexpected reason which is described in the return value of the function." 

181 ) 

182 elif params.result_out == driver.CUgraphInstantiateResult.CUDA_GRAPH_INSTANTIATE_INVALID_STRUCTURE: 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

183 raise RuntimeError("Instantiation failed due to invalid structure, such as cycles.") 

184 elif params.result_out == driver.CUgraphInstantiateResult.CUDA_GRAPH_INSTANTIATE_NODE_OPERATION_NOT_SUPPORTED: 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

185 raise RuntimeError( 

186 "Instantiation for device launch failed because the graph contained an unsupported operation." 

187 ) 

188 elif params.result_out == driver.CUgraphInstantiateResult.CUDA_GRAPH_INSTANTIATE_MULTIPLE_CTXS_NOT_SUPPORTED: 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

189 raise RuntimeError("Instantiation for device launch failed due to the nodes belonging to different contexts.") 

190 elif ( 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

191 cy_binding_version() >= (12, 8, 0) 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

192 and params.result_out == driver.CUgraphInstantiateResult.CUDA_GRAPH_INSTANTIATE_CONDITIONAL_HANDLE_UNUSED 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

193 ): 

194 raise RuntimeError("One or more conditional handles are not associated with conditional builders.") 

195 elif params.result_out != driver.CUgraphInstantiateResult.CUDA_GRAPH_INSTANTIATE_SUCCESS: 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

196 raise RuntimeError(f"Graph instantiation failed with unexpected error code: {params.result_out}") 

197  

198 c_exec = <cydriver.CUgraphExec><intptr_t>int(py_exec) 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

199 return Graph._init(c_exec) 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

200  

201  

202# Distinguishes the three kinds of GraphBuilder, which differ in how they 

203# begin/end stream capture and whether they own the resulting CUgraph. 

204# Each kind progresses through _CaptureState as follows: 

205# 

206# PRIMARY: NOT_STARTED -> CAPTURING -> ENDED 

207# FORKED: CAPTURING (never transitions; joined and closed) 

208# CONDITIONAL_BODY: NOT_STARTED -> CAPTURING -> ENDED 

209# 

210cdef enum _BuilderKind: 

211 # PRIMARY: The top-level builder created by Device or Stream. Owns the 

212 # captured CUgraph via an owning GraphHandle. Progresses through all three 

213 # capture states; responsible for ending capture if destroyed early. 

214 PRIMARY = 0 

215 # FORKED: Created by split(). Captures on a private stream forked from the 

216 # primary. Starts in CAPTURING state and never transitions; the user joins 

217 # it back to the primary via join(), which closes the builder. Must NOT 

218 # call cuStreamEndCapture (the driver requires all forked streams to be 

219 # joined first). 

220 FORKED = 1 

221 # CONDITIONAL_BODY: Created by if_then/if_else/switch/while_loop. Captures 

222 # into a non-owned body graph via cuStreamBeginCaptureToGraph. The body 

223 # graph's lifetime is tied to a parent graph. Progresses through all three 

224 # capture states like PRIMARY. 

225 CONDITIONAL_BODY = 2 

226  

227  

228# Tracks the capture lifecycle of a GraphBuilder. 

229cdef enum _CaptureState: 

230 CAPTURE_NOT_STARTED = 0 

231 CAPTURING = 1 

232 CAPTURE_ENDED = 2 # Finished, valid handle 

233 CLOSED = 3 # No valid handle 

234  

235  

236cdef class GraphBuilder: 

237 """A graph under construction by stream capture. 

238  

239 A graph groups a set of CUDA kernels and other CUDA operations together and executes 

240 them with a specified dependency tree. It speeds up the workflow by combining the 

241 driver activities associated with CUDA kernel launches and CUDA API calls. 

242  

243 Directly creating a :obj:`~graph.GraphBuilder` is not supported due 

244 to ambiguity. New graph builders should instead be created through a 

245 :obj:`~_device.Device`, or a :obj:`~_stream.stream` object. 

246  

247 .. note:: 

248  

249 Operations recorded during capture reference your memory but do not 

250 take ownership of it. As with ordinary stream work, you must keep the 

251 operands alive for as long as the completed graph may execute -- for 

252 example, the :obj:`~_memory.Buffer` objects passed to :func:`~launch` 

253 or :meth:`~_memory.Buffer.copy_to`. Host callbacks added with 

254 :meth:`callback` are the exception: the callable (and any copied 

255 ``user_data``) are retained for the graph's lifetime. This differs from 

256 building a graph explicitly with :class:`~graph.GraphDefinition`, which 

257 retains the operands it is given. 

258  

259 """ 

260  

261 def __init__(self): 

262 raise NotImplementedError( 

263 "directly creating a GraphBuilder object can be ambiguous. Please either " 

264 "call Device.create_graph_builder() or stream.create_graph_builder()" 

265 ) 

266  

267 def __dealloc__(self): 

268 GB_end_capture_if_needed(self, False) 2[ I J K L ) M 1 T x G y ; Qb? H = U : ^ E _ ] F 6 WbW 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D . * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P R $ % @ S a '

269  

270 @staticmethod 

271 def _init(Stream stream): 

272 cdef GraphBuilder self = GraphBuilder.__new__(GraphBuilder) 2[ I J K L ) M 1 T x G y ; Qb? H = U : ^ E _ ] F 6 WbW 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D . * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P R $ % @ S a '

273 # _h_graph set by begin_building 

274 self._h_stream = stream._h_stream 2[ I J K L ) M 1 T x G y ; Qb? H = U : ^ E _ ] F 6 WbW 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D . * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P R $ % @ S a '

275 self._kind = PRIMARY 2[ I J K L ) M 1 T x G y ; Qb? H = U : ^ E _ ] F 6 WbW 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D . * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P R $ % @ S a '

276 self._state = CAPTURE_NOT_STARTED 2[ I J K L ) M 1 T x G y ; Qb? H = U : ^ E _ ] F 6 WbW 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D . * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P R $ % @ S a '

277 self._stream = stream 2[ I J K L ) M 1 T x G y ; Qb? H = U : ^ E _ ] F 6 WbW 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D . * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P R $ % @ S a '

278 return self 2[ I J K L ) M 1 T x G y ; Qb? H = U : ^ E _ ] F 6 WbW 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D . * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P R $ % @ S a '

279  

280 def close(self): 

281 """Destroy the graph builder.""" 

282 GB_end_capture_if_needed(self, True) 11T;H:EF7Vbcdefghijklmnopqrstuva

283 self._h_graph.reset() 11T;H:EF7Vbcdefghijklmnopqrstuva

284 self._h_stream.reset() 11T;H:EF7Vbcdefghijklmnopqrstuva

285 self._state = CLOSED 11T;H:EF7Vbcdefghijklmnopqrstuva

286 self._stream = None 11T;H:EF7Vbcdefghijklmnopqrstuva

287  

288 @property 

289 def stream(self) -> Stream: 

290 """Returns the stream associated with the graph builder.""" 

291 return self._stream 1M1Txy;H=U:E_F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

292  

293 @property 

294 def is_join_required(self) -> bool: 

295 """Returns True if this graph builder must be joined before building is ended.""" 

296 return self._kind == FORKED 1THEFbcdefghijklmnopqrstuva

297  

298 @property 

299 def graph_definition(self) -> GraphDefinition: 

300 """The captured graph as an explicit :class:`~graph.GraphDefinition`. 

301  

302 .. versionadded:: 1.1.0 

303  

304 The returned :class:`~graph.GraphDefinition` is a view of the same 

305 graph this builder is producing: nodes added through it appear in 

306 subsequent :meth:`complete` and :meth:`debug_dot_print` calls, and 

307 the view stays valid even after the builder is closed. 

308  

309 This lets you mix the capture and explicit APIs on a single graph, 

310 for example to inspect what was captured, augment it with extra 

311 nodes, or build a conditional body entirely with the explicit API. 

312  

313 Availability: 

314  

315 - **Primary builders** (created by :meth:`Device.create_graph_builder` 

316 or :meth:`Stream.create_graph_builder`): only after 

317 :meth:`end_building`. 

318  

319 - **Conditional-body builders** (returned by :meth:`if_then`, 

320 :meth:`if_else`, :meth:`while_loop`, :meth:`switch`): both before 

321 :meth:`begin_building` and after :meth:`end_building`. The body 

322 graph already exists when the conditional is created, so you may 

323 populate it through this view without ever calling 

324 :meth:`begin_building` on the body builder. 

325  

326 - **Forked builders** (returned by :meth:`split`): never. Forked 

327 builders share the primary builder's graph; access it through the 

328 primary instead. 

329  

330 Returns 

331 ------- 

332 GraphDefinition 

333 A view of the graph being built. 

334  

335 Raises 

336 ------ 

337 RuntimeError 

338 If the builder is closed, forked, currently building, or (for 

339 primary builders) has not started building yet. A 

340 :class:`~graph.GraphDefinition` obtained before :meth:`close` 

341 keeps working; only fresh access through this property is 

342 rejected once the builder is closed. 

343 """ 

344 GB_check_open(self) 2x G y ; Qb? H = U :

345 if self._kind == FORKED: 2x G y Qb? H = U :

346 raise RuntimeError( 1H

347 "graph_definition is unavailable on forked graph builders; " 

348 "access it through the primary builder instead." 

349 ) 

350 elif self._state == CAPTURING: 2x G y Qb? = U :

351 raise RuntimeError( 1G?

352 "graph_definition is unavailable while capture is in " 

353 "progress; call end_building() first." 

354 ) 

355 elif self._kind == PRIMARY: 2x y Qb= U :

356 if self._state == CAPTURE_NOT_STARTED: 2Qb= U :

357 raise RuntimeError( 2Qb

358 "graph_definition is unavailable before begin_building() on " 

359 "a primary builder; no graph has been created yet." 

360 ) 

361 return GraphDefinition._from_handle(self._h_graph) 1xy=U:

362  

363 def begin_building(self, mode: str | None = "relaxed") -> GraphBuilder: 

364 """Begins the building process. 

365  

366 Build `mode` for controlling interaction with other API calls must be one of the following: 

367  

368 - `global` : Prohibit potentially unsafe operations across all streams in the process. 

369 - `thread_local` : Prohibit potentially unsafe operations in streams created by the current thread. 

370 - `relaxed` : The local thread is not prohibited from potentially unsafe operations. 

371  

372 Parameters 

373 ---------- 

374 mode : str, optional 

375 Build mode to control the interaction with other API calls that are porentially unsafe. 

376 Default set to use relaxed. 

377  

378 """ 

379 GB_check_open(self) 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

380 if self._state != CAPTURE_NOT_STARTED: 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

381 if self._state == CAPTURING: 1[6

382 raise RuntimeError("Graph builder is already building.") 1[

383 else: 

384 raise RuntimeError("Cannot resume building after building has ended.") 16

385 cdef cydriver.CUstreamCaptureMode c_mode 

386 if mode == "global": 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

387 c_mode = cydriver.CU_STREAM_CAPTURE_MODE_GLOBAL 1./89Q!#@

388 elif mode == "thread_local": 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD*,+-X2Y3NOZ405wPR$%@Sa'

389 c_mode = cydriver.CU_STREAM_CAPTURE_MODE_THREAD_LOCAL 1,-23O45@

390 elif mode == "relaxed": 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD*+XYNZ0wPR$%@Sa'

391 c_mode = cydriver.CU_STREAM_CAPTURE_MODE_RELAXED 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD*+XYNZ0wPR$%@Sa'

392 else: 

393 raise ValueError(f"Unsupported build mode: {mode}") 1@

394  

395 cdef cydriver.CUstream c_stream = as_cu(self._h_stream) 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

396 cdef cydriver.CUgraph c_graph 

397 cdef cydriver.CUstreamCaptureStatus c_status 

398 if self._kind == CONDITIONAL_BODY: 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

399 c_graph = as_cu(self._h_graph) 1xGbcdefghijklmnopqzrstuvABCDwa

400 with nogil: 1xGbcdefghijklmnopqzrstuvABCDwa

401 HANDLE_RETURN(cydriver.cuStreamBeginCaptureToGraph( 1xGbcdefghijklmnopqzrstuvABCDwa

402 c_stream, c_graph, NULL, NULL, 0, c_mode)) 

403 self._state = CAPTURING 1xGbcdefghijklmnopqzrstuvABCDwa

404 else: 

405 with nogil: 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

406 HANDLE_RETURN(cydriver.cuStreamBeginCapture(c_stream, c_mode)) 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

407 # Capture is active now; set CAPTURING before the calls below so a 

408 # failure in _get_capture_info/create_graph_handle still lets 

409 # cleanup end the capture rather than leaving the stream poisoned. 

410 self._state = CAPTURING 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

411 with nogil: 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

412 # The driver rejects a NULL captureStatus_out, so pass a 

413 # stack-local even though we only want the graph handle. 

414 _get_capture_info(c_stream, &c_status, &c_graph) 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

415 self._h_graph = create_graph_handle(c_graph) 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

416 return self 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

417  

418 @property 

419 def is_building(self) -> bool: 

420 """Returns True if the graph builder is currently building.""" 

421 GB_check_open(self) 1[IJKL)M1xGy;?H=U:^E]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

422 cdef cydriver.CUstream c_stream = as_cu(self._h_stream) 1[IJKL)M1xGy;?H=U:^E]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

423 cdef cydriver.CUstreamCaptureStatus status 

424 with nogil: 1[IJKL)M1xGy;?H=U:^E]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

425 _get_capture_info(c_stream, &status, NULL) 1[IJKL)M1xGy;?H=U:^E]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

426 if status == cydriver.CU_STREAM_CAPTURE_STATUS_NONE: 1[IJKL)M1xGy;?H=U:^E]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

427 return False 1]

428 elif status == cydriver.CU_STREAM_CAPTURE_STATUS_ACTIVE: 

429 return True 1[IJKL)M1xGy;?H=U:^E]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

430 elif status == cydriver.CU_STREAM_CAPTURE_STATUS_INVALIDATED: 

431 raise RuntimeError( 

432 "Build process encountered an error and has been invalidated. Build process must now be ended." 

433 ) 

434 else: 

435 raise NotImplementedError(f"Unsupported capture status type received: {status}") 

436  

437 def end_building(self) -> GraphBuilder: 

438 """Ends the building process.""" 

439 GB_check_open(self) 1[IJKL)M1xGy;?H=U:^E]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

440 if not self.is_building: 1[IJKL)M1xGy;?H=U:^E]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

441 raise RuntimeError("Graph builder is not building.") 

442 cdef cydriver.CUstream c_stream = as_cu(self._h_stream) 1[IJKL)M1xGy;?H=U:^E]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

443 cdef cydriver.CUgraph c_graph 

444 with nogil: 1[IJKL)M1xGy;?H=U:^E]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

445 HANDLE_RETURN(cydriver.cuStreamEndCapture(c_stream, &c_graph)) 1[IJKL)M1xGy;?H=U:^E]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

446  

447 # TODO: Resolving https://github.com/NVIDIA/cuda-python/issues/617 would allow us to 

448 # resume the build process after the first call to end_building() 

449 self._state = CAPTURE_ENDED 1[IJKL)M1xGy;?H=U:^E]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

450 return self 1[IJKL)M1xGy;?H=U:^E]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

451  

452 def complete(self, options: GraphCompleteOptions | None = None) -> Graph: 

453 """Completes the graph builder and returns the built :obj:`~graph.Graph` object. 

454  

455 Parameters 

456 ---------- 

457 options : :obj:`~graph.GraphCompleteOptions`, optional 

458 Customizable dataclass for the graph builder completion options. 

459  

460 Returns 

461 ------- 

462 graph : :obj:`~graph.Graph` 

463 The newly built graph. 

464  

465 """ 

466 GB_check_open(self) 1IJKL)M1TxyUEF6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%S'

467 if self._state != CAPTURE_ENDED: 1IJKL)M1xyUEF6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%S'

468 raise RuntimeError("Graph has not finished building.") 1)

469  

470 return _instantiate_graph(as_py(self._h_graph), options) 1IJKL)M1xyUEF6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%S'

471  

472 def debug_dot_print(self, path: str, options: GraphDebugPrintOptions | None = None) -> None: 

473 """Generates a DOT debug file for the graph builder. 

474  

475 Parameters 

476 ---------- 

477 path : str 

478 File path to use for writting debug DOT output 

479 options : :obj:`~graph.GraphDebugPrintOptions`, optional 

480 Customizable dataclass for the debug print options. 

481  

482 """ 

483 GB_check_open(self) 1a

484 if self._state != CAPTURE_ENDED: 1a

485 raise RuntimeError("Graph has not finished building.") 

486 cdef unsigned int c_flags = options._to_flags() if options else 0 1a

487 cdef cydriver.CUgraph c_graph = as_cu(self._h_graph) 1a

488 cdef bytes b_path = path.encode('utf-8') 1a

489 cdef const char* c_path = b_path 1a

490 with nogil: 1a

491 HANDLE_RETURN(cydriver.cuGraphDebugDotPrint(c_graph, c_path, c_flags)) 1a

492  

493 def split(self, count: int) -> tuple[GraphBuilder, ...]: 

494 """Splits the original graph builder into multiple graph builders. 

495  

496 The new builders inherit work dependencies from the original builder. 

497 The original builder is reused for the split and is returned first in the tuple. 

498  

499 Parameters 

500 ---------- 

501 count : int 

502 The number of graph builders to split the graph builder into. 

503  

504 Returns 

505 ------- 

506 graph_builders : tuple[:obj:`~graph.GraphBuilder`, ...] 

507 A tuple of split graph builders. The first graph builder in the tuple 

508 is always the original graph builder. 

509  

510 """ 

511 if count < 2: 2T H E F Wbb c d e f g h i j k l m n o p q r s t u v a

512 raise ValueError(f"Invalid split count: expecting >= 2, got {count}") 1E

513 GB_check_open(self) 2T H E F Wbb c d e f g h i j k l m n o p q r s t u v a

514 if self._state != CAPTURING: 2T H E F Wbb c d e f g h i j k l m n o p q r s t u v a

515 raise RuntimeError("Graph builder must be building before it can be split.") 2Wb

516  

517 event = self._stream.record() 1THEFbcdefghijklmnopqrstuva

518 result = [self] 1THEFbcdefghijklmnopqrstuva

519 for i in range(count - 1): 1THEFbcdefghijklmnopqrstuva

520 stream = self._stream.device.create_stream() 1THEFbcdefghijklmnopqrstuva

521 stream.wait(event) 1THEFbcdefghijklmnopqrstuva

522 result.append(GB_init_forked(stream, self._h_graph)) 1THEFbcdefghijklmnopqrstuva

523 event.close() 1THEFbcdefghijklmnopqrstuva

524 return tuple(result) 1THEFbcdefghijklmnopqrstuva

525  

526 @staticmethod 

527 def join(*graph_builders: GraphBuilder) -> GraphBuilder: 

528 """Joins multiple graph builders into a single graph builder. 

529  

530 The returned builder inherits work dependencies from the provided builders. 

531  

532 Parameters 

533 ---------- 

534 *graph_builders : :obj:`~graph.GraphBuilder` 

535 The graph builders to join. 

536  

537 Returns 

538 ------- 

539 graph_builder : :obj:`~graph.GraphBuilder` 

540 The newly joined graph builder. 

541  

542 """ 

543 if any(not isinstance(builder, GraphBuilder) for builder in graph_builders): 1THEFbcdefghijklmnopqrstuva

544 raise TypeError("All arguments must be GraphBuilder instances") 

545 if len(graph_builders) < 2: 1THEFbcdefghijklmnopqrstuva

546 raise ValueError("Must join with at least two graph builders") 1E

547  

548 # Discover the root builder others should join 

549 root_idx = 0 1THEFbcdefghijklmnopqrstuva

550 for i, builder in enumerate(graph_builders): 1THEFbcdefghijklmnopqrstuva

551 if not builder.is_join_required: 1THEFbcdefghijklmnopqrstuva

552 root_idx = i 1THEFbcdefghijklmnopqrstuva

553 break 1THEFbcdefghijklmnopqrstuva

554  

555 # Join all onto the root builder 

556 root_bdr = graph_builders[root_idx] 1THEFbcdefghijklmnopqrstuva

557 for idx, builder in enumerate(graph_builders): 1THEFbcdefghijklmnopqrstuva

558 if idx == root_idx: 1THEFbcdefghijklmnopqrstuva

559 continue 1THEFbcdefghijklmnopqrstuva

560 root_bdr.stream.wait(builder.stream) 1THEFbcdefghijklmnopqrstuva

561 builder.close() 1THEFbcdefghijklmnopqrstuva

562  

563 return root_bdr 1THEFbcdefghijklmnopqrstuva

564  

565 def __cuda_stream__(self) -> tuple[int, int]: 

566 """Return an instance of a __cuda_stream__ protocol.""" 

567 GB_check_open(self) 

568 return self.stream.__cuda_stream__() 

569  

570 def _get_conditional_context(self) -> driver.CUcontext: 

571 return self._stream.context.handle 1xGybcdefghijklmnopqzrstuvABCDwa

572  

573 def create_condition(self, default_value: int | None = None) -> GraphCondition: 

574 """Create a condition variable for use with conditional nodes. 

575  

576 The returned :class:`GraphCondition` object is passed to conditional-node 

577 builder methods (:meth:`if_then`, :meth:`if_else`, :meth:`while_loop`, 

578 :meth:`switch`). Its value is controlled at runtime by device code via 

579 ``cudaGraphSetConditional``. 

580  

581 Parameters 

582 ---------- 

583 default_value : int, optional 

584 The default value to assign to the condition. If None, no 

585 default is assigned. 

586  

587 Returns 

588 ------- 

589 GraphCondition 

590 A condition variable for controlling conditional execution. 

591 """ 

592 GB_check_open(self) 1xGybcdefghijklmnopqzrstuvABCDwa

593 if cy_driver_version() < (12, 3, 0): 1xGybcdefghijklmnopqzrstuvABCDwa

594 raise RuntimeError(f"Driver version {'.'.join(map(str, cy_driver_version()))} does not support conditional handles") 

595 if cy_binding_version() < (12, 3, 0): 1xGybcdefghijklmnopqzrstuvABCDwa

596 raise RuntimeError(f"Binding version {'.'.join(map(str, cy_binding_version()))} does not support conditional handles") 

597 if default_value is not None: 1xGybcdefghijklmnopqzrstuvABCDwa

598 flags = driver.CU_GRAPH_COND_ASSIGN_DEFAULT 1xGyABCDw

599 else: 

600 default_value = 0 1bcdefghijklmnopqzrstuva

601 flags = 0 1bcdefghijklmnopqzrstuva

602  

603 status, _, graph, *_, _ = handle_return(driver.cuStreamGetCaptureInfo(self._stream.handle)) 1xGybcdefghijklmnopqzrstuvABCDwa

604 if status != driver.CUstreamCaptureStatus.CU_STREAM_CAPTURE_STATUS_ACTIVE: 1xGybcdefghijklmnopqzrstuvABCDwa

605 raise RuntimeError("Cannot create a condition when graph is not being built") 

606  

607 raw_handle = handle_return( 1xGybcdefghijklmnopqzrstuvABCDwa

608 driver.cuGraphConditionalHandleCreate(graph, self._get_conditional_context(), default_value, flags) 1xGybcdefghijklmnopqzrstuvABCDwa

609 ) 

610 return GraphCondition._from_handle(<cydriver.CUgraphConditionalHandle><intptr_t>int(raw_handle)) 1xGybcdefghijklmnopqzrstuvABCDwa

611  

612 def if_then(self, condition: GraphCondition) -> GraphBuilder: 

613 """Adds an if condition branch and returns a new graph builder for it. 

614  

615 The resulting if graph will only execute the branch if the 

616 condition evaluates to true at runtime. 

617  

618 The new builder inherits work dependencies from the original builder. 

619  

620 Parameters 

621 ---------- 

622 condition : :class:`~graph.GraphCondition` 

623 The condition variable from :meth:`create_condition` controlling 

624 whether the branch executes. 

625  

626 Returns 

627 ------- 

628 graph_builder : :obj:`~graph.GraphBuilder` 

629 The newly created conditional graph builder. 

630  

631 """ 

632 GB_check_open(self) 1xGybcdefghizra

633 if cy_driver_version() < (12, 3, 0): 1xGybcdefghizra

634 raise RuntimeError(f"Driver version {'.'.join(map(str, cy_driver_version()))} does not support conditional if") 

635 if cy_binding_version() < (12, 3, 0): 1xGybcdefghizra

636 raise RuntimeError(f"Binding version {'.'.join(map(str, cy_binding_version()))} does not support conditional if") 

637 if not isinstance(condition, GraphCondition): 1xGybcdefghizra

638 raise TypeError( 

639 f"condition must be a GraphCondition object (from " 

640 f"GraphBuilder.create_condition()), got {type(condition).__name__}") 

641 node_params = driver.CUgraphNodeParams() 1xGybcdefghizra

642 node_params.type = driver.CUgraphNodeType.CU_GRAPH_NODE_TYPE_CONDITIONAL 1xGybcdefghizra

643 node_params.conditional.handle = condition.handle 1xGybcdefghizra

644 node_params.conditional.type = driver.CUgraphConditionalNodeType.CU_GRAPH_COND_TYPE_IF 1xGybcdefghizra

645 node_params.conditional.size = 1 1xGybcdefghizra

646 node_params.conditional.ctx = self._get_conditional_context() 1xGybcdefghizra

647 return GB_cond_with_params(self, node_params)[0] 1xGybcdefghizra

648  

649 def if_else(self, condition: GraphCondition) -> tuple[GraphBuilder, GraphBuilder]: 

650 """Adds an if-else condition branch and returns new graph builders for both branches. 

651  

652 The resulting if graph will execute the branch if the condition 

653 evaluates to true at runtime, otherwise the else branch will execute. 

654  

655 The new builders inherit work dependencies from the original builder. 

656  

657 Parameters 

658 ---------- 

659 condition : :class:`~graph.GraphCondition` 

660 The condition variable from :meth:`create_condition` controlling 

661 which branch executes. 

662  

663 Returns 

664 ------- 

665 graph_builders : tuple[:obj:`~graph.GraphBuilder`, :obj:`~graph.GraphBuilder`] 

666 A tuple of two new graph builders, one for the if branch and one for the else branch. 

667  

668 """ 

669 GB_check_open(self) 1jklmnopq

670 if cy_driver_version() < (12, 8, 0): 1jklmnopq

671 raise RuntimeError(f"Driver version {'.'.join(map(str, cy_driver_version()))} does not support conditional if-else") 

672 if cy_binding_version() < (12, 8, 0): 1jklmnopq

673 raise RuntimeError(f"Binding version {'.'.join(map(str, cy_binding_version()))} does not support conditional if-else") 

674 if not isinstance(condition, GraphCondition): 1jklmnopq

675 raise TypeError( 

676 f"condition must be a GraphCondition object (from " 

677 f"GraphBuilder.create_condition()), got {type(condition).__name__}") 

678 node_params = driver.CUgraphNodeParams() 1jklmnopq

679 node_params.type = driver.CUgraphNodeType.CU_GRAPH_NODE_TYPE_CONDITIONAL 1jklmnopq

680 node_params.conditional.handle = condition.handle 1jklmnopq

681 node_params.conditional.type = driver.CUgraphConditionalNodeType.CU_GRAPH_COND_TYPE_IF 1jklmnopq

682 node_params.conditional.size = 2 1jklmnopq

683 node_params.conditional.ctx = self._get_conditional_context() 1jklmnopq

684 return GB_cond_with_params(self, node_params) 1jklmnopq

685  

686 def switch(self, condition: GraphCondition, count: int) -> tuple[GraphBuilder, ...]: 

687 """Adds a switch condition branch and returns new graph builders for all cases. 

688  

689 The resulting switch graph will execute the branch whose case index 

690 matches the value of the condition at runtime. If no match is found, no 

691 branch will be executed. 

692  

693 The new builders inherit work dependencies from the original builder. 

694  

695 Parameters 

696 ---------- 

697 condition : :class:`~graph.GraphCondition` 

698 The condition variable from :meth:`create_condition` selecting 

699 which case executes. 

700 count : int 

701 The number of cases to add to the switch conditional. 

702  

703 Returns 

704 ------- 

705 graph_builders : tuple[:obj:`~graph.GraphBuilder`, ...] 

706 A tuple of new graph builders, one for each branch. 

707  

708 """ 

709 GB_check_open(self) 1stuvw

710 if cy_driver_version() < (12, 8, 0): 1stuvw

711 raise RuntimeError(f"Driver version {'.'.join(map(str, cy_driver_version()))} does not support conditional switch") 

712 if cy_binding_version() < (12, 8, 0): 1stuvw

713 raise RuntimeError(f"Binding version {'.'.join(map(str, cy_binding_version()))} does not support conditional switch") 

714 if not isinstance(condition, GraphCondition): 1stuvw

715 raise TypeError( 

716 f"condition must be a GraphCondition object (from " 

717 f"GraphBuilder.create_condition()), got {type(condition).__name__}") 

718 node_params = driver.CUgraphNodeParams() 1stuvw

719 node_params.type = driver.CUgraphNodeType.CU_GRAPH_NODE_TYPE_CONDITIONAL 1stuvw

720 node_params.conditional.handle = condition.handle 1stuvw

721 node_params.conditional.type = driver.CUgraphConditionalNodeType.CU_GRAPH_COND_TYPE_SWITCH 1stuvw

722 node_params.conditional.size = count 1stuvw

723 node_params.conditional.ctx = self._get_conditional_context() 1stuvw

724 return GB_cond_with_params(self, node_params) 1stuvw

725  

726 def while_loop(self, condition: GraphCondition) -> GraphBuilder: 

727 """Adds a while loop and returns a new graph builder for it. 

728  

729 The resulting while loop graph will execute the branch repeatedly at runtime 

730 until the condition evaluates to false. 

731  

732 The new builder inherits work dependencies from the original builder. 

733  

734 Parameters 

735 ---------- 

736 condition : :class:`~graph.GraphCondition` 

737 The condition variable from :meth:`create_condition` controlling 

738 loop continuation. 

739  

740 Returns 

741 ------- 

742 graph_builder : :obj:`~graph.GraphBuilder` 

743 The newly created while loop graph builder. 

744  

745 """ 

746 GB_check_open(self) 1ABCD

747 if cy_driver_version() < (12, 3, 0): 1ABCD

748 raise RuntimeError(f"Driver version {'.'.join(map(str, cy_driver_version()))} does not support conditional while loop") 

749 if cy_binding_version() < (12, 3, 0): 1ABCD

750 raise RuntimeError(f"Binding version {'.'.join(map(str, cy_binding_version()))} does not support conditional while loop") 

751 if not isinstance(condition, GraphCondition): 1ABCD

752 raise TypeError( 

753 f"condition must be a GraphCondition object (from " 

754 f"GraphBuilder.create_condition()), got {type(condition).__name__}") 

755 node_params = driver.CUgraphNodeParams() 1ABCD

756 node_params.type = driver.CUgraphNodeType.CU_GRAPH_NODE_TYPE_CONDITIONAL 1ABCD

757 node_params.conditional.handle = condition.handle 1ABCD

758 node_params.conditional.type = driver.CUgraphConditionalNodeType.CU_GRAPH_COND_TYPE_WHILE 1ABCD

759 node_params.conditional.size = 1 1ABCD

760 node_params.conditional.ctx = self._get_conditional_context() 1ABCD

761 return GB_cond_with_params(self, node_params)[0] 1ABCD

762  

763 def embed(self, GraphBuilder child): 

764 """Embed a previously-built :obj:`~graph.GraphBuilder` as a child node. 

765  

766 Parameters 

767 ---------- 

768 child : :obj:`~graph.GraphBuilder` 

769 The child graph builder. Must have finished building. 

770 """ 

771 GB_check_open(self) 1M

772 if child._state != CAPTURE_ENDED: 1M

773 raise ValueError("Child graph has not finished building.") 

774  

775 if not self.is_building: 1M

776 raise ValueError("Parent graph is not being built.") 

777  

778 stream_handle = self._stream.handle 1M

779 _, _, graph_out, *deps_info_out, num_dependencies_out = handle_return( 1M

780 driver.cuStreamGetCaptureInfo(stream_handle) 1M

781 ) 

782  

783 # See https://github.com/NVIDIA/cuda-python/pull/879#issuecomment-3211054159 

784 # for rationale 

785 deps_info_trimmed = deps_info_out[:num_dependencies_out] 1M

786 deps_info_update = [ 1M

787 [ 1M

788 handle_return( 1M

789 driver.cuGraphAddChildGraphNode( 1M

790 graph_out, *deps_info_trimmed, num_dependencies_out, as_py(child._h_graph) 1M

791 ) 

792 ) 

793 ] 

794 ] + [None] * (len(deps_info_out) - 1) 1M

795 handle_return( 1M

796 driver.cuStreamUpdateCaptureDependencies( 1M

797 stream_handle, 1M

798 *deps_info_update, # dependencies, edgeData 

799 1, 

800 driver.CUstreamUpdateCaptureDependencies_flags.CU_STREAM_SET_CAPTURE_DEPENDENCIES, 1M

801 ) 

802 ) 

803  

804 def callback(self, fn, *, user_data=None) -> None: 

805 """Add a host callback to the graph during stream capture. 

806  

807 The callback runs on the host CPU when the graph reaches this point 

808 in execution. Two modes are supported: 

809  

810 - **Python callable**: Pass any callable. The GIL is acquired 

811 automatically. The callable must take no arguments; use closures 

812 or ``functools.partial`` to bind state. 

813 - **ctypes function pointer**: Pass a ``ctypes.CFUNCTYPE`` instance. 

814 The function receives a single ``void*`` argument (the 

815 ``user_data``). The caller must keep the ctypes wrapper alive 

816 for the lifetime of the graph. 

817  

818 .. warning:: 

819  

820 Callbacks must not call CUDA API functions. Doing so may 

821 deadlock or corrupt driver state. 

822  

823 Parameters 

824 ---------- 

825 fn : callable or ctypes function pointer 

826 The callback function. 

827 user_data : int or bytes-like, optional 

828 Only for ctypes function pointers. If ``int``, passed as a raw 

829 pointer (caller manages lifetime). If bytes-like, the data is 

830 copied and its lifetime is tied to the graph. 

831 """ 

832 GB_check_open(self) 1IJKL

833 cdef Stream stream = self._stream 1IJKL

834 cdef cydriver.CUstream c_stream = as_cu(stream._h_stream) 1IJKL

835 cdef cydriver.CUstreamCaptureStatus capture_status 

836  

837 with nogil: 1IJKL

838 _get_capture_info(c_stream, &capture_status, NULL) 1IJKL

839  

840 if capture_status != cydriver.CU_STREAM_CAPTURE_STATUS_ACTIVE: 1IJKL

841 raise RuntimeError("Cannot add callback when graph is not being built") 

842  

843 cdef cydriver.CUhostFn c_fn 

844 cdef void* c_user_data = NULL 1IJKL

845 cdef OpaqueHandle fn_owner, data_owner 

846 _resolve_host_callback(fn, user_data, &c_fn, &c_user_data, &fn_owner, &data_owner) 1IJKL

847  

848 with nogil: 1IJKL

849 HANDLE_RETURN(cydriver.cuLaunchHostFunc(c_stream, c_fn, c_user_data)) 1IJKL

850  

851 # Capturing the host function added a node to the graph; it is now the 

852 # stream's sole capture dependency. Key the callback's owners to it so 

853 # they live in the graph's slot table like any explicitly-added node. 

854 cdef cydriver.CUgraphNode host_node = _capture_tail_node(c_stream) 1IJKL

855 _attach_host_callback_owners(self._h_graph, host_node, fn_owner, data_owner) 1IJKL

856  

857  

858cdef inline int GB_check_open(GraphBuilder gb) except -1: 

859 """Reject operations on a builder that has been closed. 

860  

861 A CLOSED builder has reset its stream and graph handles, so any method 

862 that dereferences them would read a null handle (or, for the cached 

863 Stream, a None typed as cdef Stream). Guarding here yields a clear error 

864 instead. 

865 """ 

866 if gb._state == CLOSED: 2[ I J K L ) M 1 T x G y ; Qb? H = U : ^ E _ ] F 6 WbW 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D . * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P R $ % @ S a '

867 raise RuntimeError("Graph builder has been closed.") 1T;

868 return 0 2[ I J K L ) M 1 T x G y ; Qb? H = U : ^ E _ ] F 6 WbW 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D . * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P R $ % @ S a '

869  

870  

871cdef inline int GB_end_capture_if_needed(GraphBuilder gb, bint check_status) except -1 nogil: 

872 """End an in-progress capture if this builder owns it. 

873  

874 Only a CAPTURING PRIMARY or CONDITIONAL_BODY builder owns the live 

875 capture. A FORKED builder must not call cuStreamEndCapture: the driver 

876 requires forked streams to be joined first. 

877  

878 check_status=True checks the driver return (close()); False ignores it 

879 (__dealloc__). 

880 """ 

881 cdef cydriver.CUgraph c_graph 

882 cdef cydriver.CUresult err 

883 cdef cydriver.CUstream c_stream 

884 if gb._h_stream and gb._state == CAPTURING and gb._kind != FORKED: 2[ I J K L ) M 1 T x G y ; Qb? H = U : ^ E _ ] F 6 WbW 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D . * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P R $ % @ S a '

885 c_stream = as_cu(gb._h_stream) 1T_

886 with nogil: 1T_

887 err = cydriver.cuStreamEndCapture(c_stream, &c_graph) 1T_

888 if check_status: 1T_

889 HANDLE_RETURN(err) 

890 return 0 2[ I J K L ) M 1 T x G y ; Qb? H = U : ^ E _ ] F 6 WbW 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D . * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P R $ % @ S a '

891  

892  

893cdef inline GraphBuilder GB_init_forked(Stream stream, GraphHandle h_primary_graph): 

894 cdef GraphBuilder gb = GraphBuilder.__new__(GraphBuilder) 1THEFbcdefghijklmnopqrstuva

895 # A FORKED builder captures into the primary's CUgraph. It holds the 

896 # primary's GraphHandle so conditional bodies created on it (via 

897 # GB_init_conditional -> create_graph_handle_ref(cond_graph, parent._h_graph)) 

898 # have a valid parent handle to pin. 

899 gb._h_graph = h_primary_graph 1THEFbcdefghijklmnopqrstuva

900 gb._h_stream = stream._h_stream 1THEFbcdefghijklmnopqrstuva

901 gb._kind = FORKED 1THEFbcdefghijklmnopqrstuva

902 gb._state = CAPTURING 1THEFbcdefghijklmnopqrstuva

903 gb._stream = stream 1THEFbcdefghijklmnopqrstuva

904 return gb 1THEFbcdefghijklmnopqrstuva

905  

906  

907cdef inline GraphBuilder GB_init_conditional(Stream stream, cydriver.CUgraph cond_graph, GraphBuilder parent): 

908 cdef GraphBuilder gb = GraphBuilder.__new__(GraphBuilder) 1xGybcdefghijklmnopqzrstuvABCDwa

909 gb._h_graph = create_graph_handle_ref(cond_graph, parent._h_graph) 1xGybcdefghijklmnopqzrstuvABCDwa

910 gb._h_stream = stream._h_stream 1xGybcdefghijklmnopqzrstuvABCDwa

911 gb._kind = CONDITIONAL_BODY 1xGybcdefghijklmnopqzrstuvABCDwa

912 gb._state = CAPTURE_NOT_STARTED 1xGybcdefghijklmnopqzrstuvABCDwa

913 gb._stream = stream 1xGybcdefghijklmnopqzrstuvABCDwa

914 return gb 1xGybcdefghijklmnopqzrstuvABCDwa

915  

916  

917cdef inline int _get_capture_info( 

918 cydriver.CUstream stream, 

919 cydriver.CUstreamCaptureStatus* status, 

920 cydriver.CUgraph* graph) except?-1 nogil: 

921 """Thin wrapper around ``cuStreamGetCaptureInfo`` that papers over the 

922 CUDA 12 vs 13 signature change. 

923  

924 ``status`` must be non-NULL: the driver rejects ``captureStatus_out=NULL`` 

925 with ``CUDA_ERROR_INVALID_VALUE``. ``graph`` may be NULL when the caller 

926 does not need the graph handle. 

927 """ 

928 IF CUDA_CORE_BUILD_MAJOR >= 13: 

929 return HANDLE_RETURN(cydriver.cuStreamGetCaptureInfo( 1[IJKL)M1TxGy;?H=U:^E_]F6W7VbcdefghijklmnopqzrstuvABCD.*,/+-8X29Y3QNO!Z4#05wPR$%@Sa'

930 stream, status, NULL, graph, NULL, NULL, NULL)) 

931 ELSE: 

932 return HANDLE_RETURN(cydriver.cuStreamGetCaptureInfo( 

933 stream, status, NULL, graph, NULL, NULL)) 

934  

935  

936cdef inline cydriver.CUgraphNode _capture_tail_node(cydriver.CUstream stream) except *: 

937 """Return the node a freshly-captured single-node operation left as the 

938 stream's sole capture dependency (e.g. the host node added by 

939 ``cuLaunchHostFunc``). The driver advances the stream's dependency set to 

940 the new node, so the next captured op would depend on it. 

941 """ 

942 cdef cydriver.CUstreamCaptureStatus status 

943 cdef const cydriver.CUgraphNode* deps = NULL 1IJKL

944 cdef size_t num_deps = 0 1IJKL

945 with nogil: 1IJKL

946 IF CUDA_CORE_BUILD_MAJOR >= 13: 

947 HANDLE_RETURN(cydriver.cuStreamGetCaptureInfo( 1IJKL

948 stream, &status, NULL, NULL, &deps, NULL, &num_deps)) 

949 ELSE: 

950 HANDLE_RETURN(cydriver.cuStreamGetCaptureInfo( 

951 stream, &status, NULL, NULL, &deps, &num_deps)) 

952 if num_deps != 1: 1IJKL

953 raise RuntimeError( 

954 f"expected exactly one capture dependency after a host callback, got {num_deps}") 

955 return <cydriver.CUgraphNode>deps[0] 1IJKL

956  

957  

958cdef inline tuple GB_cond_with_params(GraphBuilder gb, node_params): 

959 status, _, graph, *deps_info, num_dependencies = handle_return( 1xGybcdefghijklmnopqzrstuvABCDwa

960 driver.cuStreamGetCaptureInfo(gb._stream.handle) 1xGybcdefghijklmnopqzrstuvABCDwa

961 ) 

962 if status != driver.CUstreamCaptureStatus.CU_STREAM_CAPTURE_STATUS_ACTIVE: 1xGybcdefghijklmnopqzrstuvABCDwa

963 raise RuntimeError("Cannot add conditional node when not actively capturing") 

964  

965 deps_info_update = [ 1xGybcdefghijklmnopqzrstuvABCDwa

966 [handle_return(driver.cuGraphAddNode(graph, *deps_info, num_dependencies, node_params))] 1xGybcdefghijklmnopqzrstuvABCDwa

967 ] + [None] * (len(deps_info) - 1) 1xGybcdefghijklmnopqzrstuvABCDwa

968  

969 handle_return( 1xGybcdefghijklmnopqzrstuvABCDwa

970 driver.cuStreamUpdateCaptureDependencies( 1xGybcdefghijklmnopqzrstuvABCDwa

971 gb._stream.handle, 1xGybcdefghijklmnopqzrstuvABCDwa

972 *deps_info_update, # dependencies, edgeData 1xGybcdefghijklmnopqzrstuvABCDwa

973 1, # numDependencies 

974 driver.CUstreamUpdateCaptureDependencies_flags.CU_STREAM_SET_CAPTURE_DEPENDENCIES, 1xGybcdefghijklmnopqzrstuvABCDwa

975 ) 

976 ) 

977  

978 return tuple( 1xGybcdefghijklmnopqzrstuvABCDwa

979 GB_init_conditional( 1xGybcdefghijklmnopqzrstuvABCDwa

980 gb._stream.device.create_stream(), 1xGybcdefghijklmnopqzrstuvABCDwa

981 <cydriver.CUgraph><intptr_t>int(node_params.conditional.phGraph_out[i]), 1xGybcdefghijklmnopqzrstuvABCDwa

982 gb, 1xGybcdefghijklmnopqzrstuvABCDwa

983 ) 

984 for i in range(node_params.conditional.size) 1xGybcdefghijklmnopqzrstuvABCDwa

985 ) 

986  

987  

988cdef class Graph: 

989 """An executable graph. 

990  

991 A graph groups a set of CUDA kernels and other CUDA operations together and executes 

992 them with a specified dependency tree. It speeds up the workflow by combining the 

993 driver activities associated with CUDA kernel launches and CUDA API calls. 

994  

995 Graphs must be built using a :obj:`~graph.GraphBuilder` object. 

996  

997 """ 

998  

999 def __init__(self): 

1000 raise RuntimeError("directly constructing a Graph instance is not supported") 

1001  

1002 @staticmethod 

1003 cdef Graph _init(cydriver.CUgraphExec graph_exec): 

1004 cdef Graph self = Graph.__new__(Graph) 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

1005 self._h_graph_exec = create_graph_exec_handle(graph_exec) 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

1006 return self 2I J K L ) M 1 x y U E F 6 W 7 V b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibsbRbSbtbTbUbVbybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb. * , / + - 8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P ebR $ % S '

1007  

1008 def close(self) -> None: 

1009 """Destroy the graph.""" 

1010 self._h_graph_exec.reset() 117S

1011  

1012 @property 

1013 def handle(self) -> driver.CUgraphExec: 

1014 """Return the underlying ``CUgraphExec`` object. 

1015  

1016 .. caution:: 

1017  

1018 This handle is a Python object. To get the memory address of the underlying C 

1019 handle, call ``int()`` on the returned object. 

1020  

1021 """ 

1022 return as_py(self._h_graph_exec) 11

1023  

1024 def update(self, source: "GraphBuilder | GraphDefinition") -> None: 

1025 """Update the graph using a new graph definition. 

1026  

1027 The topology of the provided source must be identical to this graph. 

1028  

1029 Parameters 

1030 ---------- 

1031 source : :obj:`~graph.GraphBuilder` or :obj:`~graph.GraphDefinition` 

1032 The graph definition to update from. A GraphBuilder must have 

1033 finished building. 

1034  

1035 """ 

1036 from cuda.core.graph import GraphDefinition 2V w P ebR $ %

1037  

1038 cdef cydriver.CUgraph cu_graph 

1039 cdef cydriver.CUgraphExec cu_exec = as_cu(self._h_graph_exec) 2V w P ebR $ %

1040  

1041 if isinstance(source, GraphBuilder): 2V w P ebR $ %

1042 if (<GraphBuilder>source)._state == CLOSED: 1VwPR$

1043 raise ValueError("Source graph builder has been closed.") 1V

1044 if (<GraphBuilder>source)._state != CAPTURE_ENDED: 1wPR$

1045 raise ValueError("Graph has not finished building.") 1$

1046 cu_graph = as_cu((<GraphBuilder>source)._h_graph) 1wPR

1047 elif isinstance(source, GraphDefinition): 2eb%

1048 cu_graph = <cydriver.CUgraph><intptr_t>int(source.handle) 2eb

1049 else: 

1050 raise TypeError( 1%

1051 f"expected GraphBuilder or GraphDefinition, got {type(source).__name__}") 1%

1052  

1053 cdef cydriver.CUgraphExecUpdateResultInfo result_info 

1054 cdef cydriver.CUresult err 

1055 with nogil: 2w P ebR

1056 err = cydriver.cuGraphExecUpdate(cu_exec, cu_graph, &result_info) 2w P ebR

1057 if err == cydriver.CUresult.CUDA_ERROR_GRAPH_EXEC_UPDATE_FAILURE: 2w P ebR

1058 reason = driver.CUgraphExecUpdateResult(result_info.result) 1R

1059 msg = f"Graph update failed: {reason.__doc__.strip()} ({reason.name})" 1R

1060 raise CUDAError(msg) 1R

1061 HANDLE_RETURN(err) 2w P eb

1062  

1063 def upload(self, stream: Stream) -> None: 

1064 """Uploads the graph in a stream. 

1065  

1066 Parameters 

1067 ---------- 

1068 stream : :obj:`~_stream.Stream` 

1069 The stream in which to upload the graph 

1070  

1071 """ 

1072 cdef cydriver.CUgraphExec c_exec = as_cu(self._h_graph_exec) 2E W jb` kb{ lbmbnbob| pb} ~ abqbbbcbrbdbubvbwb8 X 2 9 Y 3 Q N O ! Z 4 # 0 5

1073 cdef cydriver.CUstream c_stream = <cydriver.CUstream><intptr_t>int(stream.handle) 2E W jb` kb{ lbmbnbob| pb} ~ abqbbbcbrbdbubvbwb8 X 2 9 Y 3 Q N O ! Z 4 # 0 5

1074 with nogil: 2E W jb` kb{ lbmbnbob| pb} ~ abqbbbcbrbdbubvbwb8 X 2 9 Y 3 Q N O ! Z 4 # 0 5

1075 HANDLE_RETURN(cydriver.cuGraphUpload(c_exec, c_stream)) 2E W jb` kb{ lbmbnbob| pb} ~ abqbbbcbrbdbubvbwb8 X 2 9 Y 3 Q N O ! Z 4 # 0 5

1076  

1077 def launch(self, stream: Stream) -> None: 

1078 """Launches the graph in a stream. 

1079  

1080 Parameters 

1081 ---------- 

1082 stream : :obj:`~_stream.Stream` 

1083 The stream in which to launch the graph. 

1084  

1085 """ 

1086 cdef cydriver.CUgraphExec c_exec = as_cu(self._h_graph_exec) 2I J K L M x y U E 6 W b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P eb'

1087 cdef cydriver.CUstream c_stream = <cydriver.CUstream><intptr_t>int(stream.handle) 2I J K L M x y U E 6 W b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P eb'

1088 with nogil: 2I J K L M x y U E 6 W b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P eb'

1089 HANDLE_RETURN(cydriver.cuGraphLaunch(c_exec, c_stream)) 2I J K L M x y U E 6 W b c d e f g h i j k l m n o p q z r s t u v A B C D jb` kb{ fblbmbnbob| pb} gb~ abqbbbhbcbrbdbibybubvbzbAbBbCbDbEbFbGbHbIbJbwbKbLbMbNbObPb8 X 2 9 Y 3 Q N O ! Z 4 # 0 5 w P eb'