k3-node 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (459) hide show
  1. k3_node/__init__.py +122 -0
  2. k3_node/applications/__init__.py +17 -0
  3. k3_node/applications/bio/__init__.py +21 -0
  4. k3_node/applications/chemistry/__init__.py +155 -0
  5. k3_node/applications/materials/__init__.py +127 -0
  6. k3_node/applications/materials/basis.py +449 -0
  7. k3_node/applications/materials/chgnet.py +360 -0
  8. k3_node/applications/materials/core.py +351 -0
  9. k3_node/applications/materials/grace.py +246 -0
  10. k3_node/applications/materials/io.py +230 -0
  11. k3_node/applications/materials/m3gnet.py +462 -0
  12. k3_node/applications/materials/megnet.py +395 -0
  13. k3_node/applications/materials/qet.py +220 -0
  14. k3_node/applications/materials/readout.py +235 -0
  15. k3_node/applications/materials/so3net.py +234 -0
  16. k3_node/applications/materials/tensornet.py +381 -0
  17. k3_node/applications/materials/test_materials.py +167 -0
  18. k3_node/applications/materials/wrappers.py +95 -0
  19. k3_node/data/__init__.py +47 -0
  20. k3_node/data/batch.py +102 -0
  21. k3_node/data/collate.py +282 -0
  22. k3_node/data/data.py +532 -0
  23. k3_node/data/database.py +154 -0
  24. k3_node/data/dataset.py +182 -0
  25. k3_node/data/download.py +49 -0
  26. k3_node/data/extract.py +45 -0
  27. k3_node/data/feature_store.py +70 -0
  28. k3_node/data/graph_store.py +92 -0
  29. k3_node/data/hetero_data.py +374 -0
  30. k3_node/data/hypergraph_data.py +59 -0
  31. k3_node/data/in_memory_dataset.py +177 -0
  32. k3_node/data/makedirs.py +7 -0
  33. k3_node/data/on_disk_dataset.py +77 -0
  34. k3_node/data/separate.py +115 -0
  35. k3_node/data/storage.py +593 -0
  36. k3_node/data/temporal.py +154 -0
  37. k3_node/data/test_batch.py +67 -0
  38. k3_node/data/test_data.py +68 -0
  39. k3_node/data/test_dataset_and_stores.py +111 -0
  40. k3_node/data/test_hetero_data.py +33 -0
  41. k3_node/data/test_temporal_and_hyper.py +32 -0
  42. k3_node/data/view.py +43 -0
  43. k3_node/datasets/__init__.py +88 -0
  44. k3_node/datasets/actor.py +101 -0
  45. k3_node/datasets/airports.py +84 -0
  46. k3_node/datasets/amazon.py +66 -0
  47. k3_node/datasets/ba2motif_dataset.py +73 -0
  48. k3_node/datasets/ba_shapes.py +81 -0
  49. k3_node/datasets/bitcoin_otc.py +77 -0
  50. k3_node/datasets/citation_full.py +81 -0
  51. k3_node/datasets/coauthor.py +66 -0
  52. k3_node/datasets/dblp.py +106 -0
  53. k3_node/datasets/digits.py +63 -0
  54. k3_node/datasets/email_eu_core.py +60 -0
  55. k3_node/datasets/entities.py +158 -0
  56. k3_node/datasets/explainer_dataset.py +101 -0
  57. k3_node/datasets/facebook.py +51 -0
  58. k3_node/datasets/fake.py +256 -0
  59. k3_node/datasets/freebase.py +90 -0
  60. k3_node/datasets/geometric_shapes.py +69 -0
  61. k3_node/datasets/github.py +51 -0
  62. k3_node/datasets/graph_generator/__init__.py +6 -0
  63. k3_node/datasets/graph_generator/ba_graph.py +20 -0
  64. k3_node/datasets/graph_generator/base.py +29 -0
  65. k3_node/datasets/graph_generator/er_graph.py +21 -0
  66. k3_node/datasets/icews.py +58 -0
  67. k3_node/datasets/imdb.py +96 -0
  68. k3_node/datasets/jodie.py +56 -0
  69. k3_node/datasets/karate.py +56 -0
  70. k3_node/datasets/lastfm_asia.py +51 -0
  71. k3_node/datasets/mesh_correspondence.py +50 -0
  72. k3_node/datasets/molecule_net.py +148 -0
  73. k3_node/datasets/motif_generator/__init__.py +7 -0
  74. k3_node/datasets/motif_generator/base.py +29 -0
  75. k3_node/datasets/motif_generator/custom.py +17 -0
  76. k3_node/datasets/motif_generator/cycle.py +25 -0
  77. k3_node/datasets/motif_generator/house.py +27 -0
  78. k3_node/datasets/movielens.py +55 -0
  79. k3_node/datasets/planetoid.py +137 -0
  80. k3_node/datasets/polblogs.py +63 -0
  81. k3_node/datasets/ppi.py +189 -0
  82. k3_node/datasets/qm7.py +65 -0
  83. k3_node/datasets/qm9.py +132 -0
  84. k3_node/datasets/reddit.py +121 -0
  85. k3_node/datasets/sbm_dataset.py +165 -0
  86. k3_node/datasets/seal.py +74 -0
  87. k3_node/datasets/shape_scenes.py +92 -0
  88. k3_node/datasets/test_datasets.py +322 -0
  89. k3_node/datasets/tu_dataset.py +131 -0
  90. k3_node/datasets/twitch.py +66 -0
  91. k3_node/datasets/webkb.py +102 -0
  92. k3_node/datasets/wikics.py +85 -0
  93. k3_node/datasets/word_net.py +184 -0
  94. k3_node/etl/__init__.py +37 -0
  95. k3_node/etl/encoders.py +248 -0
  96. k3_node/etl/graph_builders.py +270 -0
  97. k3_node/etl/relational_to_graph.py +201 -0
  98. k3_node/etl/table_to_graph.py +244 -0
  99. k3_node/etl/test_etl.py +318 -0
  100. k3_node/export/__init__.py +15 -0
  101. k3_node/export/cross_backend.py +172 -0
  102. k3_node/export/onnx_exporter.py +190 -0
  103. k3_node/export/runtime.py +254 -0
  104. k3_node/export/tensorrt_exporter.py +201 -0
  105. k3_node/export/test_export.py +337 -0
  106. k3_node/export/tflite_exporter.py +112 -0
  107. k3_node/hub/__init__.py +29 -0
  108. k3_node/hub/dataset_hub.py +242 -0
  109. k3_node/hub/hub_mixin.py +599 -0
  110. k3_node/hub/model_card.py +133 -0
  111. k3_node/hub/test_hub.py +419 -0
  112. k3_node/io/__init__.py +22 -0
  113. k3_node/io/fs.py +117 -0
  114. k3_node/io/npz.py +45 -0
  115. k3_node/io/off.py +29 -0
  116. k3_node/io/planetoid.py +98 -0
  117. k3_node/io/tu.py +137 -0
  118. k3_node/io/txt_array.py +58 -0
  119. k3_node/layers/__init__.py +14 -0
  120. k3_node/layers/aggr/__init__.py +70 -0
  121. k3_node/layers/aggr/attention.py +77 -0
  122. k3_node/layers/aggr/base.py +403 -0
  123. k3_node/layers/aggr/basic.py +412 -0
  124. k3_node/layers/aggr/deep_sets.py +65 -0
  125. k3_node/layers/aggr/deepsets.py +29 -0
  126. k3_node/layers/aggr/equilibrium.py +107 -0
  127. k3_node/layers/aggr/fused.py +43 -0
  128. k3_node/layers/aggr/gmt.py +89 -0
  129. k3_node/layers/aggr/gru.py +58 -0
  130. k3_node/layers/aggr/lcm.py +143 -0
  131. k3_node/layers/aggr/lstm.py +58 -0
  132. k3_node/layers/aggr/mlp.py +75 -0
  133. k3_node/layers/aggr/multi.py +154 -0
  134. k3_node/layers/aggr/patch_transformer.py +137 -0
  135. k3_node/layers/aggr/quantile.py +125 -0
  136. k3_node/layers/aggr/resolver.py +68 -0
  137. k3_node/layers/aggr/scaler.py +133 -0
  138. k3_node/layers/aggr/set2set.py +87 -0
  139. k3_node/layers/aggr/set_transformer.py +107 -0
  140. k3_node/layers/aggr/sort.py +68 -0
  141. k3_node/layers/aggr/test_aggr.py +337 -0
  142. k3_node/layers/aggr/utils.py +210 -0
  143. k3_node/layers/aggr/variance_preserving.py +54 -0
  144. k3_node/layers/attention/__init__.py +5 -0
  145. k3_node/layers/attention/pair_attention.py +448 -0
  146. k3_node/layers/attention/performer.py +187 -0
  147. k3_node/layers/attention/polynormer.py +160 -0
  148. k3_node/layers/attention/qformer.py +143 -0
  149. k3_node/layers/attention/sgformer.py +106 -0
  150. k3_node/layers/attention/test_attention.py +68 -0
  151. k3_node/layers/attention/test_pair_attention.py +91 -0
  152. k3_node/layers/conv/__init__.py +149 -0
  153. k3_node/layers/conv/agnn_conv.py +120 -0
  154. k3_node/layers/conv/antisymmetric_conv.py +94 -0
  155. k3_node/layers/conv/appnp.py +105 -0
  156. k3_node/layers/conv/appnp_conv.py +157 -0
  157. k3_node/layers/conv/arma_conv.py +231 -0
  158. k3_node/layers/conv/cg_conv.py +92 -0
  159. k3_node/layers/conv/cheb_conv.py +137 -0
  160. k3_node/layers/conv/cluster_gcn_conv.py +102 -0
  161. k3_node/layers/conv/conv.py +100 -0
  162. k3_node/layers/conv/crystal_conv.py +140 -0
  163. k3_node/layers/conv/cugraph.py +84 -0
  164. k3_node/layers/conv/diffusion_conv.py +144 -0
  165. k3_node/layers/conv/dir_gnn_conv.py +93 -0
  166. k3_node/layers/conv/dna_conv.py +192 -0
  167. k3_node/layers/conv/edge_conv.py +107 -0
  168. k3_node/layers/conv/eg_conv.py +155 -0
  169. k3_node/layers/conv/fa_conv.py +107 -0
  170. k3_node/layers/conv/feast_conv.py +126 -0
  171. k3_node/layers/conv/film_conv.py +143 -0
  172. k3_node/layers/conv/gat_conv.py +244 -0
  173. k3_node/layers/conv/gated_graph_conv.py +136 -0
  174. k3_node/layers/conv/gatv2_conv.py +205 -0
  175. k3_node/layers/conv/gcn.py +144 -0
  176. k3_node/layers/conv/gcn2_conv.py +126 -0
  177. k3_node/layers/conv/gcn_conv.py +135 -0
  178. k3_node/layers/conv/gen_conv.py +163 -0
  179. k3_node/layers/conv/general_conv.py +218 -0
  180. k3_node/layers/conv/gin_conv.py +218 -0
  181. k3_node/layers/conv/gmm_conv.py +172 -0
  182. k3_node/layers/conv/gps_conv.py +153 -0
  183. k3_node/layers/conv/graph_attention.py +262 -0
  184. k3_node/layers/conv/graph_conv.py +84 -0
  185. k3_node/layers/conv/gravnet_conv.py +93 -0
  186. k3_node/layers/conv/han_conv.py +175 -0
  187. k3_node/layers/conv/heat_conv.py +131 -0
  188. k3_node/layers/conv/hetero_conv.py +128 -0
  189. k3_node/layers/conv/hgt_conv.py +218 -0
  190. k3_node/layers/conv/hypergraph_conv.py +182 -0
  191. k3_node/layers/conv/le_conv.py +81 -0
  192. k3_node/layers/conv/lg_conv.py +58 -0
  193. k3_node/layers/conv/meshcnn_conv.py +84 -0
  194. k3_node/layers/conv/message_passing.py +451 -0
  195. k3_node/layers/conv/mf_conv.py +95 -0
  196. k3_node/layers/conv/mixhop_conv.py +108 -0
  197. k3_node/layers/conv/nn_conv.py +110 -0
  198. k3_node/layers/conv/pan_conv.py +100 -0
  199. k3_node/layers/conv/pdn_conv.py +109 -0
  200. k3_node/layers/conv/pna_conv.py +177 -0
  201. k3_node/layers/conv/point_conv.py +101 -0
  202. k3_node/layers/conv/point_gnn_conv.py +90 -0
  203. k3_node/layers/conv/point_transformer_conv.py +132 -0
  204. k3_node/layers/conv/ppf_conv.py +135 -0
  205. k3_node/layers/conv/ppnp.py +89 -0
  206. k3_node/layers/conv/res_gated_graph_conv.py +126 -0
  207. k3_node/layers/conv/rgat_conv.py +251 -0
  208. k3_node/layers/conv/rgcn_conv.py +321 -0
  209. k3_node/layers/conv/sage_conv.py +154 -0
  210. k3_node/layers/conv/sg_conv.py +96 -0
  211. k3_node/layers/conv/signed_conv.py +100 -0
  212. k3_node/layers/conv/simple_conv.py +75 -0
  213. k3_node/layers/conv/spline_conv.py +182 -0
  214. k3_node/layers/conv/ssg_conv.py +101 -0
  215. k3_node/layers/conv/supergat_conv.py +195 -0
  216. k3_node/layers/conv/tag_conv.py +98 -0
  217. k3_node/layers/conv/test_backend_consistency.py +164 -0
  218. k3_node/layers/conv/test_conv.py +176 -0
  219. k3_node/layers/conv/test_conv_pyg.py +566 -0
  220. k3_node/layers/conv/transformer_conv.py +168 -0
  221. k3_node/layers/conv/utils.py +403 -0
  222. k3_node/layers/conv/wl_conv.py +151 -0
  223. k3_node/layers/conv/x_conv.py +187 -0
  224. k3_node/layers/dense/__init__.py +40 -0
  225. k3_node/layers/dense/dense_gat_conv.py +149 -0
  226. k3_node/layers/dense/dense_gcn_conv.py +117 -0
  227. k3_node/layers/dense/dense_gin_conv.py +88 -0
  228. k3_node/layers/dense/dense_graph_conv.py +95 -0
  229. k3_node/layers/dense/dense_sage_conv.py +85 -0
  230. k3_node/layers/dense/diff_pool.py +76 -0
  231. k3_node/layers/dense/dmon_pool.py +223 -0
  232. k3_node/layers/dense/linear.py +327 -0
  233. k3_node/layers/dense/mincut_pool.py +92 -0
  234. k3_node/layers/dense/test_dense.py +377 -0
  235. k3_node/layers/functional/__init__.py +13 -0
  236. k3_node/layers/functional/bro.py +49 -0
  237. k3_node/layers/functional/edge_dropout.py +55 -0
  238. k3_node/layers/functional/gini.py +44 -0
  239. k3_node/layers/functional/test_functional.py +34 -0
  240. k3_node/layers/kge/__init__.py +17 -0
  241. k3_node/layers/kge/base.py +255 -0
  242. k3_node/layers/kge/complex.py +98 -0
  243. k3_node/layers/kge/distmult.py +79 -0
  244. k3_node/layers/kge/loader.py +50 -0
  245. k3_node/layers/kge/rotate.py +103 -0
  246. k3_node/layers/kge/test_kge.py +76 -0
  247. k3_node/layers/kge/transe.py +96 -0
  248. k3_node/layers/norm/__init__.py +23 -0
  249. k3_node/layers/norm/batch_norm.py +328 -0
  250. k3_node/layers/norm/diff_group_norm.py +141 -0
  251. k3_node/layers/norm/graph_norm.py +105 -0
  252. k3_node/layers/norm/graph_size_norm.py +57 -0
  253. k3_node/layers/norm/instance_norm.py +163 -0
  254. k3_node/layers/norm/layer_norm.py +245 -0
  255. k3_node/layers/norm/mean_subtraction_norm.py +57 -0
  256. k3_node/layers/norm/msg_norm.py +58 -0
  257. k3_node/layers/norm/pair_norm.py +94 -0
  258. k3_node/layers/norm/test_norm.py +275 -0
  259. k3_node/layers/pool/__init__.py +83 -0
  260. k3_node/layers/pool/approx_knn.py +101 -0
  261. k3_node/layers/pool/asap.py +173 -0
  262. k3_node/layers/pool/avg_pool.py +165 -0
  263. k3_node/layers/pool/cluster_pool.py +168 -0
  264. k3_node/layers/pool/connect/__init__.py +10 -0
  265. k3_node/layers/pool/connect/base.py +103 -0
  266. k3_node/layers/pool/connect/filter_edges.py +113 -0
  267. k3_node/layers/pool/consecutive.py +30 -0
  268. k3_node/layers/pool/decimation.py +48 -0
  269. k3_node/layers/pool/edge_pool.py +189 -0
  270. k3_node/layers/pool/glob.py +139 -0
  271. k3_node/layers/pool/graclus.py +66 -0
  272. k3_node/layers/pool/knn.py +253 -0
  273. k3_node/layers/pool/max_pool.py +159 -0
  274. k3_node/layers/pool/mem_pool.py +145 -0
  275. k3_node/layers/pool/pan_pool.py +144 -0
  276. k3_node/layers/pool/point_cloud.py +212 -0
  277. k3_node/layers/pool/pool.py +119 -0
  278. k3_node/layers/pool/sag_pool.py +174 -0
  279. k3_node/layers/pool/select/__init__.py +10 -0
  280. k3_node/layers/pool/select/base.py +112 -0
  281. k3_node/layers/pool/select/topk.py +206 -0
  282. k3_node/layers/pool/test_pool.py +456 -0
  283. k3_node/layers/pool/topk_pool.py +103 -0
  284. k3_node/layers/pool/voxel_grid.py +70 -0
  285. k3_node/layers/unpool/__init__.py +9 -0
  286. k3_node/layers/unpool/knn_interpolate.py +57 -0
  287. k3_node/layers/unpool/test_unpool.py +31 -0
  288. k3_node/loader/__init__.py +62 -0
  289. k3_node/loader/base.py +69 -0
  290. k3_node/loader/cache.py +68 -0
  291. k3_node/loader/cluster.py +127 -0
  292. k3_node/loader/data_list_loader.py +45 -0
  293. k3_node/loader/dataloader.py +117 -0
  294. k3_node/loader/dense_data_loader.py +62 -0
  295. k3_node/loader/dynamic_batch_sampler.py +93 -0
  296. k3_node/loader/graph_saint.py +188 -0
  297. k3_node/loader/hgt_loader.py +90 -0
  298. k3_node/loader/imbalanced_sampler.py +87 -0
  299. k3_node/loader/keras_dataset.py +334 -0
  300. k3_node/loader/link_loader.py +179 -0
  301. k3_node/loader/link_neighbor_loader.py +202 -0
  302. k3_node/loader/mixin.py +190 -0
  303. k3_node/loader/neighbor_loader.py +159 -0
  304. k3_node/loader/neighbor_sampler.py +167 -0
  305. k3_node/loader/node_loader.py +185 -0
  306. k3_node/loader/prefetch.py +115 -0
  307. k3_node/loader/random_node_loader.py +89 -0
  308. k3_node/loader/sampler_utils.py +499 -0
  309. k3_node/loader/shadow.py +115 -0
  310. k3_node/loader/temporal_dataloader.py +98 -0
  311. k3_node/loader/test_dataloader.py +113 -0
  312. k3_node/loader/test_keras_dataset.py +221 -0
  313. k3_node/loader/test_neighbor_loader.py +122 -0
  314. k3_node/loader/test_sampler_utils.py +82 -0
  315. k3_node/loader/test_samplers.py +96 -0
  316. k3_node/loader/test_subgraph_loaders.py +89 -0
  317. k3_node/loader/utils.py +232 -0
  318. k3_node/loader/zip_loader.py +88 -0
  319. k3_node/metrics.py +94 -0
  320. k3_node/models/__init__.py +424 -0
  321. k3_node/models/attentive_fp.py +232 -0
  322. k3_node/models/attract_repel.py +108 -0
  323. k3_node/models/autoencoder.py +318 -0
  324. k3_node/models/basic_gnn.py +443 -0
  325. k3_node/models/bio/__init__.py +4 -0
  326. k3_node/models/captum.py +52 -0
  327. k3_node/models/chemistry/__init__.py +4 -0
  328. k3_node/models/correct_and_smooth.py +146 -0
  329. k3_node/models/deep_graph_infomax.py +113 -0
  330. k3_node/models/deepgcn.py +121 -0
  331. k3_node/models/dimenet.py +737 -0
  332. k3_node/models/dimenet_utils.py +153 -0
  333. k3_node/models/gnnff.py +263 -0
  334. k3_node/models/gps_model.py +1122 -0
  335. k3_node/models/gpse.py +638 -0
  336. k3_node/models/graph_unet.py +199 -0
  337. k3_node/models/graphmae2.py +954 -0
  338. k3_node/models/graphormer.py +1258 -0
  339. k3_node/models/graphormer_3d.py +868 -0
  340. k3_node/models/grover.py +1066 -0
  341. k3_node/models/jumping_knowledge.py +200 -0
  342. k3_node/models/label_prop.py +110 -0
  343. k3_node/models/lightgcn.py +171 -0
  344. k3_node/models/linkx.py +181 -0
  345. k3_node/models/lpformer.py +404 -0
  346. k3_node/models/mask_label.py +114 -0
  347. k3_node/models/materials/__init__.py +33 -0
  348. k3_node/models/meta.py +133 -0
  349. k3_node/models/metapath2vec.py +234 -0
  350. k3_node/models/mlp.py +264 -0
  351. k3_node/models/mole_bert.py +379 -0
  352. k3_node/models/neural_fingerprint.py +95 -0
  353. k3_node/models/node2vec.py +213 -0
  354. k3_node/models/pmlp.py +157 -0
  355. k3_node/models/polynormer.py +229 -0
  356. k3_node/models/rect.py +93 -0
  357. k3_node/models/renet.py +221 -0
  358. k3_node/models/rev_gnn.py +128 -0
  359. k3_node/models/schnet.py +484 -0
  360. k3_node/models/sgformer.py +195 -0
  361. k3_node/models/signed_gcn.py +185 -0
  362. k3_node/models/test_attentive_fp.py +32 -0
  363. k3_node/models/test_attract_repel.py +33 -0
  364. k3_node/models/test_autoencoder.py +119 -0
  365. k3_node/models/test_basic_gnn.py +102 -0
  366. k3_node/models/test_correct_and_smooth.py +40 -0
  367. k3_node/models/test_deep_graph_infomax.py +68 -0
  368. k3_node/models/test_deepgcn.py +21 -0
  369. k3_node/models/test_dimenet.py +86 -0
  370. k3_node/models/test_domain_apis.py +138 -0
  371. k3_node/models/test_gnnff.py +24 -0
  372. k3_node/models/test_gps_model.py +271 -0
  373. k3_node/models/test_gpse.py +34 -0
  374. k3_node/models/test_graph_unet.py +26 -0
  375. k3_node/models/test_graphmae2.py +226 -0
  376. k3_node/models/test_graphormer.py +233 -0
  377. k3_node/models/test_graphormer3d.py +163 -0
  378. k3_node/models/test_grover.py +287 -0
  379. k3_node/models/test_jumping_knowledge.py +129 -0
  380. k3_node/models/test_label_prop.py +37 -0
  381. k3_node/models/test_lightgcn.py +38 -0
  382. k3_node/models/test_linkx.py +31 -0
  383. k3_node/models/test_lpformer.py +22 -0
  384. k3_node/models/test_mask_label.py +90 -0
  385. k3_node/models/test_meta.py +159 -0
  386. k3_node/models/test_metapath2vec.py +45 -0
  387. k3_node/models/test_mlp.py +62 -0
  388. k3_node/models/test_mole_bert.py +164 -0
  389. k3_node/models/test_neural_fingerprint.py +13 -0
  390. k3_node/models/test_node2vec.py +57 -0
  391. k3_node/models/test_pmlp.py +81 -0
  392. k3_node/models/test_polynormer.py +104 -0
  393. k3_node/models/test_rect.py +23 -0
  394. k3_node/models/test_renet.py +32 -0
  395. k3_node/models/test_rev_gnn.py +24 -0
  396. k3_node/models/test_schnet.py +43 -0
  397. k3_node/models/test_sgformer.py +48 -0
  398. k3_node/models/test_signed_gcn.py +28 -0
  399. k3_node/models/test_tgn.py +77 -0
  400. k3_node/models/test_unimol.py +179 -0
  401. k3_node/models/test_unimol2.py +114 -0
  402. k3_node/models/test_unimol_plus.py +131 -0
  403. k3_node/models/test_visnet.py +44 -0
  404. k3_node/models/tgn.py +382 -0
  405. k3_node/models/unimol.py +1156 -0
  406. k3_node/models/unimol2.py +616 -0
  407. k3_node/models/unimol_docking_v2.py +301 -0
  408. k3_node/models/unimol_plus.py +456 -0
  409. k3_node/models/utils.py +97 -0
  410. k3_node/models/visnet.py +759 -0
  411. k3_node/ops/__init__.py +4 -0
  412. k3_node/ops/conv.py +56 -0
  413. k3_node/ops/creation.py +43 -0
  414. k3_node/ops/graph.py +27 -0
  415. k3_node/ops/host.py +41 -0
  416. k3_node/ops/matmul.py +49 -0
  417. k3_node/ops/numpy.py +24 -0
  418. k3_node/ops/segment.py +54 -0
  419. k3_node/ops/sparse.py +51 -0
  420. k3_node/rag/__init__.py +49 -0
  421. k3_node/rag/encoders.py +312 -0
  422. k3_node/rag/pipeline.py +192 -0
  423. k3_node/rag/projector.py +184 -0
  424. k3_node/rag/subgraph.py +270 -0
  425. k3_node/rag/test_rag.py +347 -0
  426. k3_node/rag/verbalizer.py +162 -0
  427. k3_node/tasks/__init__.py +19 -0
  428. k3_node/tasks/backbone_resolver.py +125 -0
  429. k3_node/tasks/base.py +67 -0
  430. k3_node/tasks/graph_classification.py +270 -0
  431. k3_node/tasks/graph_regression.py +228 -0
  432. k3_node/tasks/link_prediction.py +306 -0
  433. k3_node/tasks/node_classification.py +194 -0
  434. k3_node/tasks/node_regression.py +138 -0
  435. k3_node/tasks/test_tasks.py +319 -0
  436. k3_node/test_docstring_examples.py +106 -0
  437. k3_node/test_training_forwarding.py +116 -0
  438. k3_node/training.py +115 -0
  439. k3_node/transforms/__init__.py +166 -0
  440. k3_node/transforms/base_transform.py +32 -0
  441. k3_node/transforms/compose.py +58 -0
  442. k3_node/transforms/general.py +676 -0
  443. k3_node/transforms/graph.py +1070 -0
  444. k3_node/transforms/spatial.py +797 -0
  445. k3_node/transforms/test_random_link_split.py +45 -0
  446. k3_node/transforms/test_spatial_transforms.py +65 -0
  447. k3_node/transforms/test_transforms.py +253 -0
  448. k3_node/transforms/utils.py +102 -0
  449. k3_node/utils/__init__.py +5 -0
  450. k3_node/utils/backend_import.py +12 -0
  451. k3_node/utils/graph.py +286 -0
  452. k3_node/utils/keras.py +94 -0
  453. k3_node/utils/random.py +103 -0
  454. k3_node/utils/smiles.py +235 -0
  455. k3_node-1.0.0.dist-info/METADATA +284 -0
  456. k3_node-1.0.0.dist-info/RECORD +459 -0
  457. k3_node-1.0.0.dist-info/WHEEL +5 -0
  458. k3_node-1.0.0.dist-info/licenses/LICENSE +21 -0
  459. k3_node-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,201 @@
1
+ """Turn-key TensorRT engine exporter and Triton config generator for K3-Node."""
2
+
3
+ import os
4
+ import shutil
5
+ import subprocess
6
+ from pathlib import Path
7
+ from typing import Any, Dict, List, Optional, Tuple, Union
8
+
9
+ from k3_node.export.onnx_exporter import export_onnx
10
+
11
+
12
+ def export_tensorrt(
13
+ model_or_task_or_onnx: Any,
14
+ output_path: Union[str, Path],
15
+ dummy_inputs: Optional[Any] = None,
16
+ precision: str = "fp16",
17
+ workspace_gb: int = 1,
18
+ min_shapes: Optional[Dict[str, Tuple[int, ...]]] = None,
19
+ opt_shapes: Optional[Dict[str, Tuple[int, ...]]] = None,
20
+ max_shapes: Optional[Dict[str, Tuple[int, ...]]] = None,
21
+ verbose: bool = False,
22
+ ) -> Path:
23
+ r"""Compiles a K3-Node model or ONNX file into an ultra-low latency NVIDIA TensorRT engine.
24
+
25
+ Uses the Python `tensorrt` API if available on the GPU host, or invokes NVIDIA's
26
+ `trtexec` binary directly.
27
+
28
+ Args:
29
+ model_or_task_or_onnx: K3-Node model/task, or an existing `.onnx` file path.
30
+ output_path: Target path for the compiled `.engine` or `.plan` binary.
31
+ dummy_inputs: Optional input sample to determine shapes and topology.
32
+ precision: Precision mode: `"fp32"`, `"fp16"`, or `"int8"`. (default: `"fp16"`)
33
+ workspace_gb: Max GPU memory in GB allocated for TensorRT engine building. (default: `1`)
34
+ min_shapes: Optional minimum dynamic shapes dictionary (e.g. `{'x': (1, 16)}`).
35
+ opt_shapes: Optional optimal dynamic shapes dictionary (e.g. `{'x': (100, 16)}`).
36
+ max_shapes: Optional maximum dynamic shapes dictionary (e.g. `{'x': (10000, 16)}`).
37
+ verbose: Whether to log engine building progress. (default: `False`)
38
+
39
+ Returns:
40
+ Path object pointing to the compiled TensorRT `.engine` file.
41
+ """
42
+ engine_file = Path(output_path)
43
+ engine_file.parent.mkdir(parents=True, exist_ok=True)
44
+
45
+ # 1. Resolve ONNX file
46
+ if isinstance(model_or_task_or_onnx, (str, Path)) and str(model_or_task_or_onnx).endswith(".onnx"):
47
+ onnx_path = Path(model_or_task_or_onnx)
48
+ else:
49
+ temp_onnx = engine_file.with_suffix(".onnx")
50
+ export_onnx(model_or_task_or_onnx, temp_onnx, dummy_inputs=dummy_inputs, verbose=verbose)
51
+ onnx_path = temp_onnx
52
+
53
+ # 2. Check for TensorRT Python API
54
+ try:
55
+ import tensorrt as trt
56
+
57
+ logger = trt.Logger(trt.Logger.VERBOSE if verbose else trt.Logger.WARNING)
58
+ builder = trt.Builder(logger)
59
+ network_flags = 1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)
60
+ network = builder.create_network(network_flags)
61
+ parser = trt.OnnxParser(network, logger)
62
+
63
+ with open(onnx_path, "rb") as f:
64
+ if not parser.parse(f.read()):
65
+ error_msgs = "\n".join(str(parser.get_error(i)) for i in range(parser.num_errors))
66
+ raise RuntimeError(f"TensorRT failed to parse ONNX graph:\n{error_msgs}")
67
+
68
+ config = builder.create_builder_config()
69
+ config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, workspace_gb * (1 << 30))
70
+
71
+ if precision in ("fp16", "float16") and builder.platform_has_fast_fp16:
72
+ config.set_flag(trt.BuilderFlag.FP16)
73
+ elif precision in ("int8",) and builder.platform_has_fast_int8:
74
+ config.set_flag(trt.BuilderFlag.INT8)
75
+
76
+ # Optimization profile for dynamic shapes if provided
77
+ if min_shapes and opt_shapes and max_shapes:
78
+ profile = builder.create_optimization_profile()
79
+ for name in min_shapes:
80
+ profile.set_shape(name, min_shapes[name], opt_shapes[name], max_shapes[name])
81
+ config.add_optimization_profile(profile)
82
+
83
+ plan = builder.build_serialized_network(network, config)
84
+ if plan is None:
85
+ raise RuntimeError("Failed to build TensorRT serialized network.")
86
+
87
+ with open(engine_file, "wb") as f:
88
+ f.write(plan)
89
+
90
+ if verbose:
91
+ print(f"TensorRT engine compiled successfully via Python API: {engine_file}")
92
+ return engine_file
93
+
94
+ except ImportError:
95
+ # Fall back to trtexec binary if available
96
+ trtexec_bin = shutil.which("trtexec")
97
+ if trtexec_bin:
98
+ cmd = [
99
+ trtexec_bin,
100
+ f"--onnx={onnx_path}",
101
+ f"--saveEngine={engine_file}",
102
+ ]
103
+ if precision in ("fp16", "float16"):
104
+ cmd.append("--fp16")
105
+ elif precision in ("int8",):
106
+ cmd.append("--int8")
107
+
108
+ if min_shapes and opt_shapes and max_shapes:
109
+ min_str = ",".join(f"{k}:{'x'.join(map(str, v))}" for k, v in min_shapes.items())
110
+ opt_str = ",".join(f"{k}:{'x'.join(map(str, v))}" for k, v in opt_shapes.items())
111
+ max_str = ",".join(f"{k}:{'x'.join(map(str, v))}" for k, v in max_shapes.items())
112
+ cmd.extend([
113
+ f"--minShapes={min_str}",
114
+ f"--optShapes={opt_str}",
115
+ f"--maxShapes={max_str}",
116
+ ])
117
+
118
+ if verbose:
119
+ print(f"Compiling TensorRT engine via trtexec: {' '.join(cmd)}")
120
+ subprocess.run(cmd, check=True)
121
+ return engine_file
122
+ else:
123
+ raise ImportError(
124
+ "Neither NVIDIA `tensorrt` Python package nor `trtexec` binary was found on this system.\n"
125
+ "To compile TensorRT engines, please ensure you are running on an NVIDIA GPU system with:\n"
126
+ " pip install tensorrt\n"
127
+ "or install the NVIDIA TensorRT SDK (providing `trtexec`).\n"
128
+ f"Your ONNX model was successfully exported to: {onnx_path}\n"
129
+ f"You can compile it later on any GPU server with:\n"
130
+ f" trtexec --onnx={onnx_path} --saveEngine={engine_file} --fp16"
131
+ )
132
+
133
+
134
+ def generate_triton_config(
135
+ model_name: str,
136
+ output_dir: Union[str, Path],
137
+ in_channels: int,
138
+ out_channels: int,
139
+ backend: str = "onnxruntime",
140
+ max_batch_size: int = 0,
141
+ ) -> Path:
142
+ r"""Generates a complete Triton Inference Server model repository directory structure and `config.pbtxt`.
143
+
144
+ Args:
145
+ model_name: Name of the model in Triton (e.g., `'cora_gcn'`).
146
+ output_dir: Root path for the model repository folder.
147
+ in_channels: Number of node input features.
148
+ out_channels: Number of output features or classes.
149
+ backend: Backend engine (`'onnxruntime'` or `'tensorrt_plan'`).
150
+ max_batch_size: Maximum batch size (default: `0` for dynamic graph sizing).
151
+
152
+ Returns:
153
+ Path to the generated `config.pbtxt`.
154
+ """
155
+ model_dir = Path(output_dir) / model_name
156
+ version_dir = model_dir / "1"
157
+ version_dir.mkdir(parents=True, exist_ok=True)
158
+
159
+ platform = "onnxruntime_onnx" if backend == "onnxruntime" else "tensorrt_plan"
160
+
161
+ config_text = f"""name: "{model_name}"
162
+ platform: "{platform}"
163
+ max_batch_size: {max_batch_size}
164
+
165
+ input [
166
+ {{
167
+ name: "x"
168
+ data_type: TYPE_FP32
169
+ dims: [ -1, {in_channels} ]
170
+ }},
171
+ {{
172
+ name: "edge_index"
173
+ data_type: TYPE_INT64
174
+ dims: [ 2, -1 ]
175
+ }}
176
+ ]
177
+
178
+ output [
179
+ {{
180
+ name: "output"
181
+ data_type: TYPE_FP32
182
+ dims: [ -1, {out_channels} ]
183
+ }}
184
+ ]
185
+
186
+ instance_group [
187
+ {{
188
+ count: 1
189
+ kind: KIND_GPU
190
+ }}
191
+ ]
192
+
193
+ dynamic_batching {{
194
+ max_queue_delay_microseconds: 1000
195
+ }}
196
+ """
197
+ config_path = model_dir / "config.pbtxt"
198
+ with open(config_path, "w", encoding="utf-8") as f:
199
+ f.write(config_text)
200
+
201
+ return config_path
@@ -0,0 +1,337 @@
1
+ """Unit and integration tests for turn-key ONNX, TFLite, and TensorRT export."""
2
+
3
+ import os
4
+ import subprocess
5
+ import tempfile
6
+ from pathlib import Path
7
+ from unittest.mock import MagicMock, patch
8
+
9
+ import pytest
10
+ import numpy as np
11
+ import keras
12
+ from keras import ops
13
+
14
+ import k3_node
15
+ from k3_node.data import Data
16
+ from k3_node.models import GCN
17
+ from k3_node.tasks import NodeClassifier
18
+ from k3_node.export import (
19
+ export_onnx,
20
+ export_tflite,
21
+ export_tensorrt,
22
+ generate_triton_config,
23
+ ONNXModel,
24
+ TFLiteModel,
25
+ )
26
+
27
+
28
+ def _create_sample_graph(num_nodes=6, in_channels=8):
29
+ x = ops.zeros((num_nodes, in_channels), dtype="float32")
30
+ src = np.arange(num_nodes, dtype="int64")
31
+ dst = (src + 1) % num_nodes
32
+ edge_index = ops.convert_to_tensor(np.stack([src, dst], axis=0), dtype="int64")
33
+ y = ops.convert_to_tensor(np.array([i % 2 for i in range(num_nodes)], dtype="int64"), dtype="int64")
34
+ return Data(x=x, edge_index=edge_index, y=y)
35
+
36
+
37
+ def test_export_module_exports():
38
+ """Verify all export utilities are exported from top-level and export modules."""
39
+ assert hasattr(k3_node, "export_onnx")
40
+ assert hasattr(k3_node, "export_tflite")
41
+ assert hasattr(k3_node, "export_tensorrt")
42
+ assert hasattr(k3_node, "ONNXModel")
43
+ assert hasattr(k3_node, "TFLiteModel")
44
+
45
+
46
+ def test_export_onnx_model_and_runtime_inference():
47
+ """Test exporting GCN to ONNX and serving with ONNXModel."""
48
+ gcn = GCN(in_channels=8, hidden_channels=16, num_layers=2, out_channels=3)
49
+ graph = _create_sample_graph(num_nodes=6, in_channels=8)
50
+
51
+ # Initial Keras prediction
52
+ keras_pred = ops.convert_to_numpy(gcn.predict(graph))
53
+
54
+ with tempfile.TemporaryDirectory() as tmpdir:
55
+ onnx_file = Path(tmpdir) / "gcn.onnx"
56
+ out_path = export_onnx(gcn, onnx_file, dummy_inputs=graph)
57
+ assert out_path.exists()
58
+ assert out_path.stat().st_size > 0
59
+
60
+ # Load into lightweight serving runtime
61
+ serving_model = ONNXModel(onnx_file)
62
+ onnx_pred = serving_model.predict(graph)
63
+ assert onnx_pred.shape == (6, 3)
64
+
65
+ # Numerical parity check
66
+ np.testing.assert_allclose(keras_pred, onnx_pred, atol=1e-4)
67
+
68
+ # Dynamic graph sizing check: serve a graph with different number of nodes & edges!
69
+ large_graph = _create_sample_graph(num_nodes=14, in_channels=8)
70
+ large_pred = serving_model.predict(large_graph)
71
+ assert large_pred.shape == (14, 3)
72
+
73
+
74
+ def test_export_onnx_task_estimator():
75
+ """Test exporting NodeClassifier task estimator to ONNX."""
76
+ data = _create_sample_graph(num_nodes=8, in_channels=4)
77
+ clf = NodeClassifier(backbone="gcn", in_channels=4, out_channels=2, hidden_channels=8)
78
+ clf.fit(data, epochs=1, verbose=0)
79
+
80
+ with tempfile.TemporaryDirectory() as tmpdir:
81
+ onnx_file = Path(tmpdir) / "classifier.onnx"
82
+ out_path = clf.export_onnx(onnx_file)
83
+ assert out_path.exists()
84
+
85
+ runtime = ONNXModel(onnx_file)
86
+ preds = runtime.predict(data)
87
+ assert preds.shape == (8, 2)
88
+
89
+
90
+ def test_export_tflite_fp32_and_runtime_inference():
91
+ """Test exporting GCN to TFLite (FP32) and serving with TFLiteModel."""
92
+ gcn = GCN(in_channels=8, hidden_channels=16, num_layers=2, out_channels=3)
93
+ graph = _create_sample_graph(num_nodes=6, in_channels=8)
94
+ keras_pred = ops.convert_to_numpy(gcn.predict(graph))
95
+
96
+ with tempfile.TemporaryDirectory() as tmpdir:
97
+ tflite_file = Path(tmpdir) / "gcn.tflite"
98
+ out_path = export_tflite(gcn, tflite_file, dummy_inputs=graph)
99
+ assert out_path.exists()
100
+ assert out_path.stat().st_size > 0
101
+
102
+ # Serve with TFLiteModel runtime
103
+ tflite_runner = TFLiteModel(tflite_file)
104
+ tflite_pred = tflite_runner.predict(graph)
105
+ assert tflite_pred.shape == (6, 3)
106
+ np.testing.assert_allclose(keras_pred, tflite_pred, atol=1e-4)
107
+
108
+
109
+ def test_export_tflite_fp16_quantization():
110
+ """Test TFLite FP16 quantization."""
111
+ gcn = GCN(in_channels=8, hidden_channels=16, num_layers=2, out_channels=3)
112
+ graph = _create_sample_graph(num_nodes=6, in_channels=8)
113
+
114
+ with tempfile.TemporaryDirectory() as tmpdir:
115
+ tflite_file = Path(tmpdir) / "gcn_fp16.tflite"
116
+ out_path = gcn.export_tflite(tflite_file, dummy_inputs=graph, quantization="fp16")
117
+ assert out_path.exists()
118
+ assert out_path.stat().st_size > 0
119
+
120
+
121
+ def test_export_tflite_int8_dynamic_quantization():
122
+ """Test TFLite dynamic range INT8 quantization."""
123
+ gcn = GCN(in_channels=8, hidden_channels=16, num_layers=2, out_channels=3)
124
+ graph = _create_sample_graph(num_nodes=6, in_channels=8)
125
+
126
+ with tempfile.TemporaryDirectory() as tmpdir:
127
+ tflite_file = Path(tmpdir) / "gcn_int8.tflite"
128
+ out_path = gcn.export_tflite(tflite_file, dummy_inputs=graph, quantization="int8_dynamic")
129
+ assert out_path.exists()
130
+ assert out_path.stat().st_size > 0
131
+
132
+
133
+ def test_generate_triton_config():
134
+ """Test generating Triton Inference Server model repository and config.pbtxt."""
135
+ with tempfile.TemporaryDirectory() as tmpdir:
136
+ cfg_path = generate_triton_config(
137
+ model_name="cora_gcn",
138
+ output_dir=tmpdir,
139
+ in_channels=1433,
140
+ out_channels=7,
141
+ backend="onnxruntime",
142
+ )
143
+ assert cfg_path.exists()
144
+ with open(cfg_path, "r", encoding="utf-8") as f:
145
+ content = f.read()
146
+ assert 'name: "cora_gcn"' in content
147
+ assert "onnxruntime_onnx" in content
148
+ assert "dims: [ -1, 1433 ]" in content
149
+ assert "dims: [ -1, 7 ]" in content
150
+ assert (Path(tmpdir) / "cora_gcn" / "1").exists()
151
+
152
+
153
+ def test_export_tensorrt_mocked():
154
+ """Test TensorRT exporter fallback and compilation logic with mock."""
155
+ gcn = GCN(in_channels=8, hidden_channels=16, num_layers=2, out_channels=3)
156
+ graph = _create_sample_graph(num_nodes=6, in_channels=8)
157
+
158
+ with tempfile.TemporaryDirectory() as tmpdir:
159
+ engine_file = Path(tmpdir) / "model.engine"
160
+
161
+ real_run = subprocess.run
162
+ with patch("shutil.which", return_value="/usr/local/bin/trtexec"), patch("subprocess.run") as mock_sub:
163
+ # Create a mock engine file to simulate trtexec output
164
+ def fake_run(cmd, *args, **kwargs):
165
+ cmd_str = " ".join(cmd) if isinstance(cmd, list) else str(cmd)
166
+ if "trtexec" in cmd_str:
167
+ with open(engine_file, "wb") as f:
168
+ f.write(b"TRT_ENGINE_BYTES")
169
+ return MagicMock(returncode=0)
170
+ return real_run(cmd, *args, **kwargs)
171
+
172
+ mock_sub.side_effect = fake_run
173
+ out = gcn.export_tensorrt(engine_file, dummy_inputs=graph)
174
+ assert out.exists()
175
+ assert mock_sub.called
176
+ args, _ = mock_sub.call_args
177
+ assert "--saveEngine=" in " ".join(args[0])
178
+
179
+
180
+ def test_export_tensorrt_python_api_mocked():
181
+ """Test TensorRT Python API compilation path with optimization profile."""
182
+ gcn = GCN(in_channels=8, hidden_channels=16, num_layers=2, out_channels=3)
183
+ graph = _create_sample_graph(num_nodes=6, in_channels=8)
184
+
185
+ with tempfile.TemporaryDirectory() as tmpdir:
186
+ engine_file = Path(tmpdir) / "model.engine"
187
+
188
+ # Mock tensorrt module
189
+ mock_trt = MagicMock()
190
+ mock_builder = MagicMock()
191
+ mock_trt.Builder.return_value = mock_builder
192
+ mock_builder.platform_has_fast_fp16 = True
193
+
194
+ mock_parser = MagicMock()
195
+ mock_parser.parse.return_value = True
196
+ mock_parser.num_errors = 0
197
+ mock_trt.OnnxParser.return_value = mock_parser
198
+
199
+ mock_config = MagicMock()
200
+ mock_builder.create_builder_config.return_value = mock_config
201
+ mock_profile = MagicMock()
202
+ mock_builder.create_optimization_profile.return_value = mock_profile
203
+
204
+ # Return serialized engine bytes
205
+ mock_builder.build_serialized_network.return_value = b"TENSORRT_SERIALIZED_PLAN"
206
+
207
+ with patch.dict("sys.modules", {"tensorrt": mock_trt}):
208
+ out = export_tensorrt(
209
+ gcn,
210
+ engine_file,
211
+ dummy_inputs=graph,
212
+ precision="fp16",
213
+ min_shapes={"x": (1, 8), "edge_index": (2, 1)},
214
+ opt_shapes={"x": (6, 8), "edge_index": (2, 6)},
215
+ max_shapes={"x": (50, 8), "edge_index": (2, 100)},
216
+ )
217
+ assert out.exists()
218
+ assert out.read_bytes() == b"TENSORRT_SERIALIZED_PLAN"
219
+ assert mock_builder.create_builder_config.called
220
+ assert mock_builder.create_optimization_profile.called
221
+ assert mock_config.add_optimization_profile.called
222
+ assert mock_config.set_flag.called
223
+
224
+
225
+ def test_export_tensorrt_direct_onnx_input():
226
+ """Test export_tensorrt when supplied directly with an existing .onnx file."""
227
+ gcn = GCN(in_channels=8, hidden_channels=16, num_layers=2, out_channels=3)
228
+ graph = _create_sample_graph(num_nodes=6, in_channels=8)
229
+
230
+ with tempfile.TemporaryDirectory() as tmpdir:
231
+ onnx_file = Path(tmpdir) / "pre_exported.onnx"
232
+ export_onnx(gcn, onnx_file, dummy_inputs=graph)
233
+ assert onnx_file.exists()
234
+
235
+ engine_file = Path(tmpdir) / "pre_exported.engine"
236
+
237
+ real_run = subprocess.run
238
+ with patch("shutil.which", return_value="/usr/local/bin/trtexec"), patch("subprocess.run") as mock_sub:
239
+ def fake_run(cmd, *args, **kwargs):
240
+ cmd_str = " ".join(cmd) if isinstance(cmd, list) else str(cmd)
241
+ if "trtexec" in cmd_str:
242
+ with open(engine_file, "wb") as f:
243
+ f.write(b"DIRECT_ONNX_TRT_BYTES")
244
+ return MagicMock(returncode=0)
245
+ return real_run(cmd, *args, **kwargs)
246
+
247
+ mock_sub.side_effect = fake_run
248
+ out = export_tensorrt(str(onnx_file), engine_file)
249
+ assert out.exists()
250
+ assert out.read_bytes() == b"DIRECT_ONNX_TRT_BYTES"
251
+ args, _ = mock_sub.call_args
252
+ cmd_line = " ".join(args[0])
253
+ assert f"--onnx={onnx_file}" in cmd_line
254
+ assert f"--saveEngine={engine_file}" in cmd_line
255
+
256
+
257
+ def test_export_tensorrt_precision_and_dynamic_shapes_flags():
258
+ """Test trtexec dynamic shape and INT8 precision flag generation."""
259
+ gcn = GCN(in_channels=8, hidden_channels=16, num_layers=2, out_channels=3)
260
+ graph = _create_sample_graph(num_nodes=6, in_channels=8)
261
+
262
+ with tempfile.TemporaryDirectory() as tmpdir:
263
+ engine_file = Path(tmpdir) / "int8_model.engine"
264
+
265
+ real_run = subprocess.run
266
+ with patch("shutil.which", return_value="/usr/bin/trtexec"), patch("subprocess.run") as mock_sub:
267
+ captured_cmd = []
268
+
269
+ def fake_run(cmd, *args, **kwargs):
270
+ cmd_str = " ".join(cmd) if isinstance(cmd, list) else str(cmd)
271
+ if "trtexec" in cmd_str:
272
+ captured_cmd.extend(cmd)
273
+ with open(engine_file, "wb") as f:
274
+ f.write(b"INT8_ENGINE")
275
+ return MagicMock(returncode=0)
276
+ return real_run(cmd, *args, **kwargs)
277
+
278
+ mock_sub.side_effect = fake_run
279
+ out = gcn.export_tensorrt(
280
+ engine_file,
281
+ dummy_inputs=graph,
282
+ precision="int8",
283
+ min_shapes={"x": (1, 8), "edge_index": (2, 2)},
284
+ opt_shapes={"x": (10, 8), "edge_index": (2, 20)},
285
+ max_shapes={"x": (100, 8), "edge_index": (2, 200)},
286
+ )
287
+ assert out.exists()
288
+ cmd_line = " ".join(captured_cmd)
289
+ assert "--int8" in cmd_line
290
+ assert "--minShapes=x:1x8,edge_index:2x2" in cmd_line
291
+ assert "--optShapes=x:10x8,edge_index:2x20" in cmd_line
292
+ assert "--maxShapes=x:100x8,edge_index:2x200" in cmd_line
293
+
294
+
295
+ def test_export_tensorrt_missing_tools_raises_import_error():
296
+ """Test that informative ImportError is raised when neither tensorrt nor trtexec is present."""
297
+ gcn = GCN(in_channels=8, hidden_channels=16, num_layers=2, out_channels=3)
298
+ graph = _create_sample_graph(num_nodes=6, in_channels=8)
299
+
300
+ with tempfile.TemporaryDirectory() as tmpdir:
301
+ engine_file = Path(tmpdir) / "model.engine"
302
+
303
+ # Mock absence of trtexec and ensure tensorrt import fails
304
+ with patch("shutil.which", return_value=None):
305
+ with pytest.raises(ImportError, match="Neither NVIDIA `tensorrt` Python package nor `trtexec` binary"):
306
+ export_tensorrt(gcn, engine_file, dummy_inputs=graph)
307
+
308
+ # Confirm intermediate ONNX file was preserved
309
+ intermediate_onnx = engine_file.with_suffix(".onnx")
310
+ assert intermediate_onnx.exists()
311
+ assert intermediate_onnx.stat().st_size > 0
312
+
313
+
314
+ def test_export_tensorrt_task_estimator():
315
+ """Test task estimator .export_tensorrt() method."""
316
+ data = _create_sample_graph(num_nodes=8, in_channels=4)
317
+ clf = NodeClassifier(backbone="gcn", in_channels=4, out_channels=2, hidden_channels=8)
318
+ clf.fit(data, epochs=1, verbose=0)
319
+
320
+ with tempfile.TemporaryDirectory() as tmpdir:
321
+ engine_file = Path(tmpdir) / "task_model.engine"
322
+
323
+ real_run = subprocess.run
324
+ with patch("shutil.which", return_value="/usr/local/bin/trtexec"), patch("subprocess.run") as mock_sub:
325
+ def fake_run(cmd, *args, **kwargs):
326
+ cmd_str = " ".join(cmd) if isinstance(cmd, list) else str(cmd)
327
+ if "trtexec" in cmd_str:
328
+ with open(engine_file, "wb") as f:
329
+ f.write(b"TASK_TRT_BYTES")
330
+ return MagicMock(returncode=0)
331
+ return real_run(cmd, *args, **kwargs)
332
+
333
+ mock_sub.side_effect = fake_run
334
+ out = clf.export_tensorrt(engine_file, dummy_inputs=data)
335
+ assert out.exists()
336
+ assert out.read_bytes() == b"TASK_TRT_BYTES"
337
+
@@ -0,0 +1,112 @@
1
+ """Turn-key TensorFlow Lite / LiteRT exporter for K3-Node models and tasks."""
2
+
3
+ from pathlib import Path
4
+ from typing import Any, Callable, Generator, List, Optional, Tuple, Union
5
+ import numpy as np
6
+
7
+ import keras
8
+ from keras import ops
9
+
10
+ from k3_node.export.onnx_exporter import _extract_model_and_inputs
11
+
12
+
13
+ def export_tflite(
14
+ model_or_task: Any,
15
+ output_path: Union[str, Path],
16
+ dummy_inputs: Optional[Any] = None,
17
+ quantization: Optional[str] = None,
18
+ representative_dataset: Optional[Callable[[], Generator[List[np.ndarray], None, None]]] = None,
19
+ verbose: bool = False,
20
+ ) -> Path:
21
+ r"""Exports a K3-Node GNN model or task to an optimized TensorFlow Lite flatbuffer.
22
+
23
+ Supports float32, float16 (FP16), and dynamic range INT8 quantization for
24
+ low-latency deployment on edge, mobile, and embedded hardware.
25
+
26
+ Args:
27
+ model_or_task: A K3-Node task instance or model instance.
28
+ output_path: Target path for the `.tflite` file.
29
+ dummy_inputs: Optional sample input data (e.g., PyG `Data` object, tuple of tensors).
30
+ quantization: Quantization strategy:
31
+ - `None` or `"fp32"`: Full 32-bit floating point precision.
32
+ - `"fp16"`: 16-bit floating point quantization (~2x smaller & faster on GPU/NPU).
33
+ - `"int8_dynamic"`: Dynamic range integer quantization (~4x smaller with 8-bit weights).
34
+ - `"int8_full"`: Full 8-bit integer quantization (requires `representative_dataset`).
35
+ representative_dataset: Generator of representative calibration inputs for `"int8_full"`.
36
+ verbose: Whether to print verbose progress. (default: `False`)
37
+
38
+ Returns:
39
+ Path object pointing to the generated `.tflite` file.
40
+ """
41
+ out_file = Path(output_path)
42
+ out_file.parent.mkdir(parents=True, exist_ok=True)
43
+
44
+ from k3_node.export.cross_backend import is_tensorflow_backend, export_via_tf_subprocess
45
+
46
+ if not is_tensorflow_backend():
47
+ return export_via_tf_subprocess(
48
+ exporter_type="tflite",
49
+ model_or_task=model_or_task,
50
+ output_path=output_path,
51
+ dummy_inputs=dummy_inputs,
52
+ quantization=quantization,
53
+ verbose=verbose,
54
+ )
55
+
56
+ try:
57
+ import tensorflow as tf
58
+ except ImportError:
59
+ raise ImportError(
60
+ "TensorFlow is required for TFLite export. Install it via `pip install tensorflow`."
61
+ )
62
+
63
+ model, inputs, inferred_names = _extract_model_and_inputs(model_or_task, dummy_inputs)
64
+
65
+ # Build concrete function with exact input shapes
66
+ specs = []
67
+ for inp in inputs:
68
+ inp_np = ops.convert_to_numpy(inp)
69
+ specs.append(tf.TensorSpec(shape=inp_np.shape, dtype=tf.as_dtype(inp_np.dtype)))
70
+
71
+ concrete_func = tf.function(lambda *args: model(*args)).get_concrete_function(*specs)
72
+
73
+ converter = tf.lite.TFLiteConverter.from_concrete_functions([concrete_func])
74
+ converter.target_spec.supported_ops = [
75
+ tf.lite.OpsSet.TFLITE_BUILTINS,
76
+ tf.lite.OpsSet.SELECT_TF_OPS,
77
+ ]
78
+
79
+ # Apply quantization options
80
+ if quantization in ("fp16", "float16"):
81
+ converter.optimizations = [tf.lite.Optimize.DEFAULT]
82
+ converter.target_spec.supported_types = [tf.float16]
83
+ if verbose:
84
+ print("Applying FP16 quantization to TFLite model.")
85
+ elif quantization in ("int8_dynamic", "int8"):
86
+ converter.optimizations = [tf.lite.Optimize.DEFAULT]
87
+ if verbose:
88
+ print("Applying dynamic range INT8 quantization to TFLite model.")
89
+ elif quantization == "int8_full":
90
+ if representative_dataset is None:
91
+ raise ValueError(
92
+ "Full INT8 quantization (`quantization='int8_full'`) requires a "
93
+ "`representative_dataset` generator to calibrate activation ranges."
94
+ )
95
+ converter.optimizations = [tf.lite.Optimize.DEFAULT]
96
+ converter.representative_dataset = representative_dataset
97
+ converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
98
+ converter.inference_input_type = tf.int8
99
+ converter.inference_output_type = tf.int8
100
+ if verbose:
101
+ print("Applying full integer INT8 quantization with calibration dataset.")
102
+
103
+ # Convert model to TFLite flatbuffer
104
+ tflite_model_bytes = converter.convert()
105
+
106
+ with open(out_file, "wb") as f:
107
+ f.write(tflite_model_bytes)
108
+
109
+ if verbose:
110
+ print(f"Exported TFLite model ({len(tflite_model_bytes)} bytes) to {out_file}")
111
+
112
+ return out_file
@@ -0,0 +1,29 @@
1
+ """Hugging Face Hub integration for K3-Node GNN models and graph datasets."""
2
+
3
+ from k3_node.hub.hub_mixin import (
4
+ K3NodeHubMixin,
5
+ from_pretrained,
6
+ push_to_hub,
7
+ save_pretrained,
8
+ )
9
+ from k3_node.hub.model_card import generate_model_card
10
+ from k3_node.hub.dataset_hub import (
11
+ generate_dataset_card,
12
+ load_dataset_from_hub,
13
+ load_graph_dataset,
14
+ push_dataset_to_hub,
15
+ save_graph_dataset,
16
+ )
17
+
18
+ __all__ = [
19
+ "K3NodeHubMixin",
20
+ "save_pretrained",
21
+ "from_pretrained",
22
+ "push_to_hub",
23
+ "generate_model_card",
24
+ "save_graph_dataset",
25
+ "load_graph_dataset",
26
+ "generate_dataset_card",
27
+ "push_dataset_to_hub",
28
+ "load_dataset_from_hub",
29
+ ]