tgraphx 0.4.3__tar.gz → 0.4.4__tar.gz

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 (230) hide show
  1. {tgraphx-0.4.3 → tgraphx-0.4.4}/PKG-INFO +1 -1
  2. {tgraphx-0.4.3 → tgraphx-0.4.4}/pyproject.toml +1 -1
  3. tgraphx-0.4.4/tests/test_matching_coloring_flow.py +178 -0
  4. tgraphx-0.4.4/tests/test_node2vec_kg_hypergraph.py +285 -0
  5. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/__init__.py +1 -1
  6. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/__init__.py +53 -0
  7. tgraphx-0.4.4/tgraphx/mining/graph_io.py +309 -0
  8. tgraphx-0.4.4/tgraphx/mining/hypergraph.py +198 -0
  9. tgraphx-0.4.4/tgraphx/mining/knowledge_graph.py +393 -0
  10. tgraphx-0.4.4/tgraphx/mining/matching_coloring.py +509 -0
  11. tgraphx-0.4.4/tgraphx/mining/node2vec.py +331 -0
  12. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx.egg-info/PKG-INFO +1 -1
  13. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx.egg-info/SOURCES.txt +7 -0
  14. {tgraphx-0.4.3 → tgraphx-0.4.4}/LICENSE +0 -0
  15. {tgraphx-0.4.3 → tgraphx-0.4.4}/README.md +0 -0
  16. {tgraphx-0.4.3 → tgraphx-0.4.4}/setup.cfg +0 -0
  17. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_3d_support.py +0 -0
  18. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_algorithms.py +0 -0
  19. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_amp_compile.py +0 -0
  20. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_backward_compatibility.py +0 -0
  21. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_benchmark_smoke.py +0 -0
  22. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_chunking.py +0 -0
  23. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_dashboard.py +0 -0
  24. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_dashboard_metadata.py +0 -0
  25. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_dataset_cache.py +0 -0
  26. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_dataset_docs_claims.py +0 -0
  27. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_dataset_download_mocked.py +0 -0
  28. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_dataset_registry.py +0 -0
  29. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_datasets_base.py +0 -0
  30. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_devices.py +0 -0
  31. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_distributed_compat.py +0 -0
  32. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_documentation_claims.py +0 -0
  33. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_edge_features.py +0 -0
  34. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_edge_weight.py +0 -0
  35. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_experiments.py +0 -0
  36. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_explainability.py +0 -0
  37. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_factories.py +0 -0
  38. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_folder_datasets.py +0 -0
  39. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_gat_chunking.py +0 -0
  40. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_gnn_families.py +0 -0
  41. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_gradients.py +0 -0
  42. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_graph.py +0 -0
  43. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_graph_api.py +0 -0
  44. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_graph_builders.py +0 -0
  45. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_graph_transformer_v027.py +0 -0
  46. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_graph_utils.py +0 -0
  47. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_hard_negative_sampling.py +0 -0
  48. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_hetero_batch.py +0 -0
  49. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_hetero_layers.py +0 -0
  50. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_hetero_sampling.py +0 -0
  51. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_imports.py +0 -0
  52. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_layers.py +0 -0
  53. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_math.py +0 -0
  54. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_math_invariants_v030.py +0 -0
  55. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_metrics.py +0 -0
  56. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_mining_centrality.py +0 -0
  57. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_mining_core.py +0 -0
  58. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_mining_generators_spectral.py +0 -0
  59. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_mining_graph_learning.py +0 -0
  60. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_mining_paths_algorithms.py +0 -0
  61. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_mining_structural.py +0 -0
  62. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_model_zoo.py +0 -0
  63. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_models.py +0 -0
  64. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_negative_sampling.py +0 -0
  65. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_neural_mining.py +0 -0
  66. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_neural_mining_batched.py +0 -0
  67. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_packaging.py +0 -0
  68. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_performance_smoke.py +0 -0
  69. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_plotting.py +0 -0
  70. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_public_benchmarks.py +0 -0
  71. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_pyg_dgl_ogb_wrappers.py +0 -0
  72. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_random_walk_sample.py +0 -0
  73. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_release_validation_v030.py +0 -0
  74. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_reproducibility.py +0 -0
  75. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_sampling.py +0 -0
  76. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_sampling_loaders.py +0 -0
  77. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_synthetic_datasets.py +0 -0
  78. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_temporal_sampling.py +0 -0
  79. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_temporal_v025.py +0 -0
  80. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_time_encoding.py +0 -0
  81. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_tiny_overfit_v030.py +0 -0
  82. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_torchvision_wrappers.py +0 -0
  83. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_tracking.py +0 -0
  84. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_training.py +0 -0
  85. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_transforms.py +0 -0
  86. {tgraphx-0.4.3 → tgraphx-0.4.4}/tests/test_v024_features.py +0 -0
  87. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/algorithms/__init__.py +0 -0
  88. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/algorithms/connectivity.py +0 -0
  89. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/algorithms/structural.py +0 -0
  90. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/algorithms/traversal.py +0 -0
  91. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/core/__init__.py +0 -0
  92. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/core/dataloader.py +0 -0
  93. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/core/graph.py +0 -0
  94. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/core/graph_utils.py +0 -0
  95. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/core/hetero_batch.py +0 -0
  96. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/core/hetero_graph.py +0 -0
  97. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/core/temporal.py +0 -0
  98. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/core/temporal_batch.py +0 -0
  99. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/core/utils.py +0 -0
  100. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/dashboard/__init__.py +0 -0
  101. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/dashboard/__main__.py +0 -0
  102. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/dashboard/app.py +0 -0
  103. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/dashboard/static/dashboard.css +0 -0
  104. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/dashboard/static/dashboard.js +0 -0
  105. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/__init__.py +0 -0
  106. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/base.py +0 -0
  107. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/cache.py +0 -0
  108. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/converters.py +0 -0
  109. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/dgl_wrappers.py +0 -0
  110. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/download.py +0 -0
  111. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/errors.py +0 -0
  112. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/folder.py +0 -0
  113. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/metadata.py +0 -0
  114. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/ogb_wrappers.py +0 -0
  115. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/pyg_wrappers.py +0 -0
  116. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/registry.py +0 -0
  117. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/synthetic.py +0 -0
  118. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/datasets/torchvision_wrappers.py +0 -0
  119. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/distributed.py +0 -0
  120. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/experiments/__init__.py +0 -0
  121. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/experiments/callbacks.py +0 -0
  122. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/experiments/checkpoints.py +0 -0
  123. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/experiments/cli.py +0 -0
  124. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/experiments/config.py +0 -0
  125. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/experiments/grid.py +0 -0
  126. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/experiments/runner.py +0 -0
  127. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/experiments/summarize.py +0 -0
  128. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/explain/__init__.py +0 -0
  129. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/explain/attention.py +0 -0
  130. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/explain/edge_attribution.py +0 -0
  131. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/explain/export.py +0 -0
  132. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/explain/integrated_gradients.py +0 -0
  133. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/explain/patch_heatmap.py +0 -0
  134. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/explain/saliency.py +0 -0
  135. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/explain/utils.py +0 -0
  136. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/graph_builders.py +0 -0
  137. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/hetero_sampling.py +0 -0
  138. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/interop.py +0 -0
  139. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/__init__.py +0 -0
  140. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/_dim.py +0 -0
  141. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/_scatter.py +0 -0
  142. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/aggregator.py +0 -0
  143. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/appnp.py +0 -0
  144. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/attention_message.py +0 -0
  145. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/base.py +0 -0
  146. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/conv_message.py +0 -0
  147. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/factory.py +0 -0
  148. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/gat.py +0 -0
  149. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/gatv2.py +0 -0
  150. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/gin.py +0 -0
  151. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/graph_transformer.py +0 -0
  152. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/hetero.py +0 -0
  153. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/hetero_readout.py +0 -0
  154. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/pooling.py +0 -0
  155. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/safe_pool.py +0 -0
  156. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/sage.py +0 -0
  157. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/temporal_readout.py +0 -0
  158. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/transformer_encodings.py +0 -0
  159. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/layers/vector_gcn.py +0 -0
  160. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/learned_graph.py +0 -0
  161. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/metrics/__init__.py +0 -0
  162. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/metrics/classification.py +0 -0
  163. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/metrics/link_prediction.py +0 -0
  164. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/metrics/ogb.py +0 -0
  165. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/metrics/ranking.py +0 -0
  166. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/metrics/regression.py +0 -0
  167. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/metrics/reports.py +0 -0
  168. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/anomaly.py +0 -0
  169. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/api.py +0 -0
  170. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/centrality.py +0 -0
  171. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/communities.py +0 -0
  172. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/embeddings.py +0 -0
  173. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/frequent.py +0 -0
  174. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/generators.py +0 -0
  175. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/graph_learning.py +0 -0
  176. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/hetero.py +0 -0
  177. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/kernels.py +0 -0
  178. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/label_prop.py +0 -0
  179. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/link_prediction.py +0 -0
  180. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/motifs.py +0 -0
  181. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/neural.py +0 -0
  182. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/paths.py +0 -0
  183. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/patterns.py +0 -0
  184. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/prototype.py +0 -0
  185. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/random_walk.py +0 -0
  186. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/reports.py +0 -0
  187. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/sequence_models.py +0 -0
  188. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/similarity.py +0 -0
  189. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/spectral.py +0 -0
  190. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/structural.py +0 -0
  191. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/structural_encodings.py +0 -0
  192. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/mining/temporal.py +0 -0
  193. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/models/__init__.py +0 -0
  194. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/models/cnn_encoder.py +0 -0
  195. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/models/cnn_gnn_model.py +0 -0
  196. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/models/edge_predictor.py +0 -0
  197. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/models/factory.py +0 -0
  198. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/models/graph_classifier.py +0 -0
  199. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/models/hetero_models.py +0 -0
  200. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/models/model_zoo.py +0 -0
  201. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/models/node_classifier.py +0 -0
  202. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/models/pre_encoder.py +0 -0
  203. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/models/regressors.py +0 -0
  204. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/models/temporal_models.py +0 -0
  205. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/performance.py +0 -0
  206. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/plotting/__init__.py +0 -0
  207. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/plotting/graph.py +0 -0
  208. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/plotting/layouts.py +0 -0
  209. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/plotting/mining.py +0 -0
  210. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/plotting/utils.py +0 -0
  211. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/reproducibility.py +0 -0
  212. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/sampling.py +0 -0
  213. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/sampling_loaders.py +0 -0
  214. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/sampling_negative.py +0 -0
  215. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/temporal/__init__.py +0 -0
  216. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/temporal/time_encoding.py +0 -0
  217. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/temporal_sampling.py +0 -0
  218. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/tracking.py +0 -0
  219. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/training.py +0 -0
  220. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/transforms/__init__.py +0 -0
  221. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/transforms/compose.py +0 -0
  222. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/transforms/features.py +0 -0
  223. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/transforms/graph.py +0 -0
  224. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/transforms/patch.py +0 -0
  225. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/transforms/positional.py +0 -0
  226. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx/transforms/splits.py +0 -0
  227. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx.egg-info/dependency_links.txt +0 -0
  228. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx.egg-info/entry_points.txt +0 -0
  229. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx.egg-info/requires.txt +0 -0
  230. {tgraphx-0.4.3 → tgraphx-0.4.4}/tgraphx.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tgraphx
3
- Version: 0.4.3
3
+ Version: 0.4.4
4
4
  Summary: Tensor-aware graph neural networks preserving spatial node feature layouts
5
5
  Author-email: Arash Sajjadi <arash.sajjadi@usask.ca>
6
6
  Maintainer-email: Arash Sajjadi <arash.sajjadi@usask.ca>
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "tgraphx"
7
7
  # Keep this in sync with tgraphx/__init__.py::__version__
8
- version = "0.4.3"
8
+ version = "0.4.4"
9
9
  description = "Tensor-aware graph neural networks preserving spatial node feature layouts"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.10"
@@ -0,0 +1,178 @@
1
+ """Tests for matching, coloring, clique, and max-flow algorithms."""
2
+ import pytest
3
+ import torch
4
+ from tgraphx.mining import (
5
+ greedy_maximal_matching,
6
+ bipartite_greedy_matching,
7
+ greedy_coloring,
8
+ welsh_powell_coloring,
9
+ greedy_maximal_independent_set,
10
+ enumerate_maximal_cliques,
11
+ edmonds_karp_max_flow,
12
+ min_cut_from_max_flow,
13
+ wl_isomorphism_test,
14
+ write_algorithm_report,
15
+ )
16
+
17
+
18
+ def _chain(N=4):
19
+ src = list(range(N-1)) + list(range(1, N))
20
+ dst = list(range(1, N)) + list(range(N-1))
21
+ return torch.tensor([src, dst], dtype=torch.long), N
22
+
23
+
24
+ def _triangle():
25
+ return torch.tensor([[0,1,2,1,2,0],[1,2,0,0,1,2]], dtype=torch.long), 3
26
+
27
+
28
+ class TestMatching:
29
+ def test_maximal_matching_chain(self):
30
+ ei, N = _chain()
31
+ m = greedy_maximal_matching(ei, N)
32
+ assert m.size(0) == 2
33
+ # Each node appears at most once.
34
+ all_nodes = m.flatten().tolist()
35
+ assert len(set(all_nodes)) == len(all_nodes)
36
+
37
+ def test_maximal_matching_empty(self):
38
+ ei = torch.zeros((2, 0), dtype=torch.long)
39
+ m = greedy_maximal_matching(ei, 4)
40
+ assert m.shape == (2, 0)
41
+
42
+ def test_bipartite_matching(self):
43
+ # Left: 0,1; Right: 2,3; edges: 0-2, 0-3, 1-2
44
+ ei = torch.tensor([[0, 0, 1], [2, 3, 2]], dtype=torch.long)
45
+ m = bipartite_greedy_matching(ei, num_nodes_left=2, num_nodes_right=2)
46
+ assert m.size(0) == 2
47
+ # At most 2 pairs.
48
+ assert m.size(1) <= 2
49
+
50
+
51
+ class TestColoring:
52
+ def test_greedy_chain_valid(self):
53
+ ei, N = _chain()
54
+ colors, nc = greedy_coloring(ei, N)
55
+ # Valid coloring: no adjacent nodes have same color.
56
+ src, dst = ei[0].tolist(), ei[1].tolist()
57
+ for u, v in zip(src, dst):
58
+ assert colors[u].item() != colors[v].item(), f"Conflict at ({u},{v})"
59
+
60
+ def test_greedy_triangle_three_colors_max(self):
61
+ ei, N = _triangle()
62
+ colors, nc = greedy_coloring(ei, N)
63
+ # Triangle needs exactly 3 colors.
64
+ assert nc <= 3
65
+ src, dst = ei[0].tolist(), ei[1].tolist()
66
+ for u, v in zip(src, dst):
67
+ assert colors[u].item() != colors[v].item()
68
+
69
+ def test_welsh_powell_valid(self):
70
+ ei, N = _chain()
71
+ colors, nc = welsh_powell_coloring(ei, N)
72
+ src, dst = ei[0].tolist(), ei[1].tolist()
73
+ for u, v in zip(src, dst):
74
+ assert colors[u].item() != colors[v].item()
75
+
76
+ def test_empty_graph_one_color(self):
77
+ ei = torch.zeros((2, 0), dtype=torch.long)
78
+ colors, nc = greedy_coloring(ei, 5)
79
+ assert nc == 1 # all nodes get color 0
80
+ assert colors.tolist() == [0, 0, 0, 0, 0]
81
+
82
+
83
+ class TestClique:
84
+ def test_independent_set_chain(self):
85
+ ei, N = _chain()
86
+ ind_set = greedy_maximal_independent_set(ei, N, seed=0)
87
+ # Valid independent set: no two adjacent members.
88
+ adj = set(zip(ei[0].tolist(), ei[1].tolist()))
89
+ members = set(ind_set.tolist())
90
+ for u in members:
91
+ for v in members:
92
+ assert (u, v) not in adj or u == v
93
+
94
+ def test_independent_set_triangle_size_one(self):
95
+ ei, N = _triangle()
96
+ ind_set = greedy_maximal_independent_set(ei, N, seed=0)
97
+ assert ind_set.size(0) == 1 # can't pick more than 1 from triangle
98
+
99
+ def test_enumerate_maximal_cliques_triangle(self):
100
+ ei, N = _triangle()
101
+ cliques = enumerate_maximal_cliques(ei, N)
102
+ # K3 has exactly one maximal clique: {0,1,2}.
103
+ assert len(cliques) == 1
104
+ assert cliques[0] == frozenset({0, 1, 2})
105
+
106
+ def test_enumerate_maximal_cliques_chain(self):
107
+ ei, N = _chain()
108
+ cliques = enumerate_maximal_cliques(ei, N)
109
+ # Chain: 0-1-2-3 → maximal cliques are edges: {0,1},{1,2},{2,3}.
110
+ assert len(cliques) == 3
111
+
112
+ def test_clique_enumeration_size_guard(self):
113
+ ei = torch.zeros((2, 0), dtype=torch.long)
114
+ with pytest.raises(ValueError, match="max_nodes"):
115
+ enumerate_maximal_cliques(ei, 51, max_nodes=50)
116
+
117
+
118
+ class TestMaxFlow:
119
+ def test_simple_flow(self):
120
+ # 0 → 1 (cap 3), 0 → 2 (cap 2), 1 → 3 (cap 2), 2 → 3 (cap 3)
121
+ ei = torch.tensor([[0, 0, 1, 2], [1, 2, 3, 3]], dtype=torch.long)
122
+ cap = torch.tensor([3.0, 2.0, 2.0, 3.0])
123
+ flow, _ = edmonds_karp_max_flow(ei, 4, cap, 0, 3)
124
+ # Max flow = 4 (3+2 paths capped by sink capacities 2+3).
125
+ # Actually: path 0→1→3 (cap=2), path 0→2→3 (cap=2) → flow=4.
126
+ assert abs(flow - 4.0) < 1e-6
127
+
128
+ def test_max_flow_min_cut(self):
129
+ ei = torch.tensor([[0, 0, 1, 2], [1, 2, 3, 3]], dtype=torch.long)
130
+ cap = torch.tensor([3.0, 2.0, 2.0, 3.0])
131
+ flow, S, T = min_cut_from_max_flow(ei, 4, cap, 0, 3)
132
+ assert 0 in S and 3 in T
133
+ assert abs(flow - 4.0) < 1e-6
134
+
135
+ def test_negative_capacity_raises(self):
136
+ ei = torch.tensor([[0], [1]], dtype=torch.long)
137
+ cap = torch.tensor([-1.0])
138
+ with pytest.raises(ValueError, match="non-negative"):
139
+ edmonds_karp_max_flow(ei, 2, cap, 0, 1)
140
+
141
+ def test_disconnected_source_sink(self):
142
+ ei = torch.zeros((2, 0), dtype=torch.long)
143
+ cap = torch.zeros(0)
144
+ flow, _ = edmonds_karp_max_flow(ei, 3, cap, 0, 2)
145
+ assert flow == 0.0
146
+
147
+ def test_size_guard(self):
148
+ ei = torch.zeros((2, 0), dtype=torch.long)
149
+ cap = torch.zeros(0)
150
+ with pytest.raises(ValueError, match="num_nodes"):
151
+ edmonds_karp_max_flow(ei, 501, cap, 0, 499)
152
+
153
+
154
+ class TestWLIsomorphism:
155
+ def test_identical_graphs(self):
156
+ ei, N = _triangle()
157
+ assert wl_isomorphism_test(ei, N, ei, N) is True
158
+
159
+ def test_different_sizes(self):
160
+ ei3, N3 = _triangle()
161
+ ei4, N4 = _chain(5)
162
+ assert wl_isomorphism_test(ei3, N3, ei4, N4) is False
163
+
164
+ def test_non_isomorphic_same_size(self):
165
+ # Path 4 vs Cycle 4.
166
+ ei_path, _ = _chain(4)
167
+ ei_cycle = torch.tensor([[0,1,2,3,1,2,3,0],[1,2,3,0,0,1,2,3]], dtype=torch.long)
168
+ # WL should distinguish these.
169
+ result = wl_isomorphism_test(ei_path, 4, ei_cycle, 4, num_iterations=3)
170
+ # Note: WL may or may not distinguish some graphs. Accept any bool.
171
+ assert isinstance(result, bool)
172
+
173
+ def test_write_algorithm_report(self, tmp_path):
174
+ p = write_algorithm_report(str(tmp_path / "algo.json"),
175
+ "greedy_coloring", num_colors=3)
176
+ import json; d = json.loads(open(p).read())
177
+ assert d["algorithm"] == "greedy_coloring"
178
+ assert d["num_colors"] == 3
@@ -0,0 +1,285 @@
1
+ """Tests for Node2Vec, Knowledge Graph, Hypergraph, and Graph IO."""
2
+ import json
3
+ import os
4
+ import tempfile
5
+
6
+ import pytest
7
+ import torch
8
+
9
+ from tgraphx.mining import (
10
+ # Node2Vec
11
+ node2vec_walks, deepwalk_walks, generate_skipgram_pairs,
12
+ Node2VecEmbedding, train_node2vec_step, extract_node2vec_embeddings,
13
+ # KG
14
+ KnowledgeGraph, negative_triple_sampling, filtered_ranking_metrics,
15
+ TransE, DistMult, train_kg_step,
16
+ # Hypergraph
17
+ Hypergraph, incidence_to_bipartite_graph, clique_expansion,
18
+ star_expansion, hypergraph_density,
19
+ # IO
20
+ read_graph_json, write_graph_json, write_edge_list_csv, read_edge_list_csv,
21
+ save_graph_npz, load_graph_npz,
22
+ )
23
+
24
+
25
+ def _chain_ei(N=4):
26
+ src = list(range(N-1)) + list(range(1, N))
27
+ dst = list(range(1, N)) + list(range(N-1))
28
+ return torch.tensor([src, dst], dtype=torch.long), N
29
+
30
+
31
+ class TestNode2Vec:
32
+ def test_walks_shape(self):
33
+ ei, N = _chain_ei()
34
+ walks = node2vec_walks(ei, N, walk_length=5, walks_per_node=3, seed=0)
35
+ assert walks.shape == (N * 3, 6) # walk_length + 1
36
+
37
+ def test_walks_deterministic(self):
38
+ ei, N = _chain_ei()
39
+ w1 = node2vec_walks(ei, N, walk_length=5, walks_per_node=2, seed=7)
40
+ w2 = node2vec_walks(ei, N, walk_length=5, walks_per_node=2, seed=7)
41
+ assert torch.equal(w1, w2)
42
+
43
+ def test_walks_valid_nodes(self):
44
+ ei, N = _chain_ei()
45
+ walks = node2vec_walks(ei, N, walk_length=10, walks_per_node=5, seed=0)
46
+ assert ((walks >= 0) & (walks < N)).all()
47
+
48
+ def test_deepwalk_is_p1_q1(self):
49
+ ei, N = _chain_ei()
50
+ dw = deepwalk_walks(ei, N, walk_length=5, walks_per_node=2, seed=0)
51
+ n2v = node2vec_walks(ei, N, walk_length=5, walks_per_node=2, p=1.0, q=1.0, seed=0)
52
+ assert torch.equal(dw, n2v)
53
+
54
+ def test_biased_walks_different_from_uniform(self):
55
+ ei = torch.tensor([[0,0,0,0,0],[1,2,3,4,5]], dtype=torch.long)
56
+ N = 6
57
+ uniform = node2vec_walks(ei, N, walk_length=10, walks_per_node=5, p=1.0, q=1.0, seed=0)
58
+ biased = node2vec_walks(ei, N, walk_length=10, walks_per_node=5, p=0.5, q=2.0, seed=0)
59
+ # They should differ at some point (not guaranteed, but very likely for this graph).
60
+ # Just verify both are valid.
61
+ assert ((uniform >= 0) & (uniform < N)).all()
62
+ assert ((biased >= 0) & (biased < N)).all()
63
+
64
+ def test_skipgram_pairs(self):
65
+ ei, N = _chain_ei()
66
+ walks = node2vec_walks(ei, N, walk_length=5, walks_per_node=2, seed=0)
67
+ centers, contexts, negatives = generate_skipgram_pairs(walks, window_size=2, negative_ratio=3, num_nodes=N, seed=0)
68
+ assert centers.size(0) == contexts.size(0)
69
+ assert negatives.size(0) == centers.size(0) * 3
70
+
71
+ def test_node2vec_embedding_forward(self):
72
+ model = Node2VecEmbedding(num_nodes=10, embedding_dim=8)
73
+ centers = torch.randint(10, (4,))
74
+ contexts = torch.randint(10, (4,))
75
+ negatives = torch.randint(10, (8,))
76
+ loss = model(centers, contexts, negatives)
77
+ assert torch.isfinite(loss)
78
+
79
+ def test_node2vec_embedding_backward(self):
80
+ model = Node2VecEmbedding(num_nodes=10, embedding_dim=8)
81
+ opt = torch.optim.Adam(model.parameters(), lr=1e-2)
82
+ centers = torch.randint(10, (4,))
83
+ contexts = torch.randint(10, (4,))
84
+ negatives = torch.randint(10, (8,))
85
+ loss_val = train_node2vec_step(model, opt, centers, contexts, negatives)
86
+ assert isinstance(loss_val, float) and loss_val >= 0
87
+
88
+ def test_node2vec_loss_decreases(self):
89
+ torch.manual_seed(0)
90
+ ei, N = _chain_ei(8)
91
+ # Generate many walks to create reliable pairs.
92
+ walks = node2vec_walks(ei, N, walk_length=10, walks_per_node=20, seed=0)
93
+ centers, contexts, negatives = generate_skipgram_pairs(
94
+ walks, window_size=3, negative_ratio=5, num_nodes=N, seed=0,
95
+ )
96
+ model = Node2VecEmbedding(num_nodes=N, embedding_dim=16)
97
+ opt = torch.optim.Adam(model.parameters(), lr=0.02)
98
+ losses = []
99
+ for _ in range(10):
100
+ losses.append(train_node2vec_step(model, opt, centers, contexts, negatives))
101
+ assert losses[-1] < losses[0], f"Loss did not decrease: {losses[0]:.4f}→{losses[-1]:.4f}"
102
+
103
+ def test_extract_embeddings_shape(self):
104
+ model = Node2VecEmbedding(num_nodes=5, embedding_dim=8)
105
+ emb = extract_node2vec_embeddings(model)
106
+ assert emb.shape == (5, 8)
107
+ assert not emb.requires_grad
108
+
109
+
110
+ class TestKnowledgeGraph:
111
+ def _kg(self):
112
+ triples = torch.tensor([[0,0,1],[1,0,2],[0,1,2],[2,0,0]], dtype=torch.long)
113
+ return KnowledgeGraph(triples)
114
+
115
+ def test_kg_creation(self):
116
+ kg = self._kg()
117
+ assert kg.num_entities == 3
118
+ assert kg.num_relations == 2
119
+ assert len(kg) == 4
120
+
121
+ def test_kg_positive_lookup(self):
122
+ kg = self._kg()
123
+ assert kg.is_positive(0, 0, 1)
124
+ assert not kg.is_positive(0, 0, 2)
125
+
126
+ def test_kg_split(self):
127
+ triples = torch.tensor([[i, 0, (i+1) % 10] for i in range(20)], dtype=torch.long)
128
+ kg = KnowledgeGraph(triples)
129
+ train, val, test = kg.train_val_test_split(ratios=(0.7, 0.15, 0.15), seed=0)
130
+ assert len(train) + len(val) + len(test) == 20
131
+
132
+ def test_negative_sampling_valid(self):
133
+ triples = torch.tensor([[0,0,1],[1,0,2]], dtype=torch.long)
134
+ neg = negative_triple_sampling(triples, num_entities=3, num_neg=2, seed=0)
135
+ assert neg.shape == (4, 3)
136
+ # Relations must match.
137
+ assert (neg[:, 1] == triples.repeat_interleave(2, dim=0)[:, 1]).all()
138
+
139
+ def test_transe_forward_backward(self):
140
+ kg = self._kg()
141
+ model = TransE(kg.num_entities, kg.num_relations, embedding_dim=8)
142
+ triples = kg.triples
143
+ neg = negative_triple_sampling(triples, kg.num_entities, seed=0)
144
+ opt = torch.optim.Adam(model.parameters(), lr=1e-2)
145
+ loss = train_kg_step(model, opt, triples, neg)
146
+ assert isinstance(loss, float) and loss >= 0
147
+
148
+ def test_transe_loss_decreases(self):
149
+ torch.manual_seed(0)
150
+ triples = torch.tensor([[i, 0, (i+1) % 8] for i in range(8)], dtype=torch.long)
151
+ kg = KnowledgeGraph(triples)
152
+ model = TransE(kg.num_entities, kg.num_relations, embedding_dim=16, margin=1.0)
153
+ opt = torch.optim.Adam(model.parameters(), lr=5e-3)
154
+ losses = []
155
+ for _ in range(20):
156
+ neg = negative_triple_sampling(triples, kg.num_entities, num_neg=2, seed=0)
157
+ losses.append(train_kg_step(model, opt, triples, neg[:8]))
158
+ assert losses[-1] < losses[0], f"Loss did not decrease: {losses[0]:.4f}→{losses[-1]:.4f}"
159
+
160
+ def test_distmult_forward_backward(self):
161
+ kg = self._kg()
162
+ model = DistMult(kg.num_entities, kg.num_relations, embedding_dim=8)
163
+ neg = negative_triple_sampling(kg.triples, kg.num_entities, seed=1)
164
+ opt = torch.optim.Adam(model.parameters(), lr=1e-2)
165
+ loss = train_kg_step(model, opt, kg.triples, neg)
166
+ assert isinstance(loss, float) and loss >= 0
167
+
168
+ def test_transe_score_shape(self):
169
+ model = TransE(5, 3, embedding_dim=8)
170
+ h = torch.tensor([0, 1, 2])
171
+ r = torch.tensor([0, 0, 1])
172
+ t = torch.tensor([1, 2, 3])
173
+ scores = model.score(h, r, t)
174
+ assert scores.shape == (3,)
175
+ assert torch.isfinite(scores).all()
176
+
177
+ def test_filtered_ranking_metrics(self):
178
+ torch.manual_seed(0)
179
+ triples = torch.tensor([[0,0,1],[1,0,2],[2,0,0]], dtype=torch.long)
180
+ kg = KnowledgeGraph(triples)
181
+ model = TransE(kg.num_entities, kg.num_relations, embedding_dim=8)
182
+ # Just run and check return format.
183
+ all_set = kg._positive_set
184
+ metrics = filtered_ranking_metrics(model, triples[:2], all_set, kg.num_entities)
185
+ assert "mrr" in metrics and "hits@1" in metrics and "hits@10" in metrics
186
+ assert 0 <= metrics["mrr"] <= 1
187
+
188
+
189
+ class TestHypergraph:
190
+ def test_creation(self):
191
+ hg = Hypergraph(5, [[0,1,2],[2,3,4],[0,4]])
192
+ assert hg.num_nodes == 5
193
+ assert hg.num_hyperedges == 3
194
+
195
+ def test_node_hyperdegree(self):
196
+ hg = Hypergraph(5, [[0,1],[1,2],[0,2]])
197
+ deg = hg.node_hyperdegree()
198
+ assert int(deg[0]) == 2 # node 0 appears in 2 hyperedges
199
+ assert int(deg[1]) == 2
200
+ assert int(deg[3]) == 0 # isolated node
201
+
202
+ def test_hyperedge_degree(self):
203
+ hg = Hypergraph(4, [[0,1],[1,2,3]])
204
+ edge_deg = hg.hyperedge_degree()
205
+ assert edge_deg.tolist() == [2, 3]
206
+
207
+ def test_density(self):
208
+ hg = Hypergraph(4, [[0,1,2,3]]) # all nodes in one hyperedge → density 1
209
+ assert abs(hg.density() - 1.0) < 1e-6
210
+
211
+ def test_density_empty(self):
212
+ hg = Hypergraph(4, [[]])
213
+ assert hg.density() == 0.0
214
+
215
+ def test_incidence_to_bipartite(self):
216
+ hg = Hypergraph(3, [[0,1],[1,2]])
217
+ bi_ei, total = incidence_to_bipartite_graph(hg)
218
+ assert total == 5 # 3 original + 2 hyperedge nodes
219
+ assert bi_ei.shape[0] == 2
220
+
221
+ def test_clique_expansion(self):
222
+ hg = Hypergraph(3, [[0,1,2]])
223
+ ei, N = clique_expansion(hg)
224
+ assert N == 3
225
+ # K3 = 6 directed edges
226
+ assert ei.size(1) == 6
227
+
228
+ def test_star_expansion(self):
229
+ hg = Hypergraph(3, [[0,1,2]])
230
+ ei, N = star_expansion(hg)
231
+ assert N == 4 # 3 + 1 hyperedge node
232
+ assert ei.size(1) == 6 # 3 undirected connections × 2 directions
233
+
234
+ def test_invalid_node(self):
235
+ with pytest.raises(ValueError):
236
+ Hypergraph(3, [[0, 5]]) # node 5 >= 3
237
+
238
+ def test_summary(self):
239
+ hg = Hypergraph(5, [[0,1,2],[2,3,4]])
240
+ s = hg.summary()
241
+ assert s["num_nodes"] == 5
242
+ assert s["num_hyperedges"] == 2
243
+ import json; json.dumps(s) # must be JSON-serializable
244
+
245
+
246
+ class TestGraphIO:
247
+ def test_write_read_json_roundtrip(self, tmp_path):
248
+ ei = torch.tensor([[0,1,2],[1,2,3]], dtype=torch.long)
249
+ ew = torch.tensor([1.0, 2.0, 3.0])
250
+ p = write_graph_json(str(tmp_path / "g.json"), ei, 4, edge_weight=ew, metadata={"test": True})
251
+ result = read_graph_json(p)
252
+ assert torch.equal(result["edge_index"], ei)
253
+ assert result["num_nodes"] == 4
254
+ assert result["metadata"]["test"] is True
255
+
256
+ def test_write_read_csv_roundtrip(self, tmp_path):
257
+ ei = torch.tensor([[0,1,2],[1,2,3]], dtype=torch.long)
258
+ ew = torch.tensor([0.5, 1.5, 2.5])
259
+ p = write_edge_list_csv(str(tmp_path / "edges.csv"), ei, edge_weight=ew, header=["src","dst","w"])
260
+ new_ei, N, new_ew = read_edge_list_csv(p, has_header=True, weight_col=2)
261
+ assert new_ei.size(1) == 3
262
+ assert new_ew is not None
263
+
264
+ def test_csv_missing_file(self):
265
+ with pytest.raises(FileNotFoundError):
266
+ read_edge_list_csv("/nonexistent/path.csv")
267
+
268
+ def test_json_missing_file(self):
269
+ with pytest.raises(FileNotFoundError):
270
+ read_graph_json("/nonexistent/path.json")
271
+
272
+ def test_write_read_npz_roundtrip(self, tmp_path):
273
+ pytest.importorskip("numpy")
274
+ ei = torch.tensor([[0,1],[1,2]], dtype=torch.long)
275
+ x = torch.randn(3, 4)
276
+ p = save_graph_npz(str(tmp_path / "g.npz"), ei, 3, node_features=x)
277
+ result = load_graph_npz(p)
278
+ assert torch.equal(result["edge_index"], ei)
279
+ assert result["num_nodes"] == 3
280
+ assert result["node_features"].shape == (3, 4)
281
+
282
+ def test_npz_missing_file(self):
283
+ pytest.importorskip("numpy")
284
+ with pytest.raises(FileNotFoundError):
285
+ load_graph_npz("/nonexistent/path.npz")
@@ -10,7 +10,7 @@ Common one-liner imports::
10
10
  """
11
11
 
12
12
  # Keep this in sync with [project].version in pyproject.toml.
13
- __version__ = "0.4.3"
13
+ __version__ = "0.4.4"
14
14
 
15
15
  # ── Core data structures ──────────────────────────────────────────────────────
16
16
  from .core.graph import Graph, GraphBatch
@@ -280,6 +280,59 @@ from .sequence_models import (
280
280
  pad_sequences,
281
281
  )
282
282
 
283
+ # ── Matching, coloring, clique, max-flow (Beta/Experimental) ─────────────────
284
+ from .matching_coloring import (
285
+ greedy_maximal_matching,
286
+ bipartite_greedy_matching,
287
+ greedy_coloring,
288
+ welsh_powell_coloring,
289
+ greedy_maximal_independent_set,
290
+ enumerate_maximal_cliques,
291
+ edmonds_karp_max_flow,
292
+ min_cut_from_max_flow,
293
+ wl_isomorphism_test,
294
+ write_algorithm_report,
295
+ )
296
+
297
+ # ── Node2Vec / DeepWalk (Experimental) ───────────────────────────────────────
298
+ from .node2vec import (
299
+ node2vec_walks,
300
+ deepwalk_walks,
301
+ generate_skipgram_pairs,
302
+ Node2VecEmbedding,
303
+ train_node2vec_step,
304
+ extract_node2vec_embeddings,
305
+ )
306
+
307
+ # ── Knowledge graphs (Experimental) ─────────────────────────────────────────
308
+ from .knowledge_graph import (
309
+ KnowledgeGraph,
310
+ negative_triple_sampling,
311
+ filtered_ranking_metrics,
312
+ TransE,
313
+ DistMult,
314
+ train_kg_step,
315
+ )
316
+
317
+ # ── Hypergraphs (Experimental) ───────────────────────────────────────────────
318
+ from .hypergraph import (
319
+ Hypergraph,
320
+ incidence_to_bipartite_graph,
321
+ clique_expansion,
322
+ star_expansion,
323
+ hypergraph_density,
324
+ )
325
+
326
+ # ── Graph IO (Beta) ───────────────────────────────────────────────────────────
327
+ from .graph_io import (
328
+ read_edge_list_csv,
329
+ write_edge_list_csv,
330
+ read_graph_json,
331
+ write_graph_json,
332
+ save_graph_npz,
333
+ load_graph_npz,
334
+ )
335
+
283
336
  # ── Neural mining (Experimental) ─────────────────────────────────────────────
284
337
  from .neural import (
285
338
  PrototypeMembershipScorer,