tgraphx 0.4.2__tar.gz → 0.4.3__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 (223) hide show
  1. {tgraphx-0.4.2 → tgraphx-0.4.3}/PKG-INFO +1 -1
  2. {tgraphx-0.4.2 → tgraphx-0.4.3}/pyproject.toml +1 -1
  3. tgraphx-0.4.3/tests/test_mining_graph_learning.py +301 -0
  4. tgraphx-0.4.3/tests/test_mining_paths_algorithms.py +251 -0
  5. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/__init__.py +1 -1
  6. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/__init__.py +59 -0
  7. tgraphx-0.4.3/tgraphx/mining/graph_learning.py +500 -0
  8. tgraphx-0.4.3/tgraphx/mining/paths.py +674 -0
  9. tgraphx-0.4.3/tgraphx/mining/sequence_models.py +340 -0
  10. tgraphx-0.4.3/tgraphx/mining/structural_encodings.py +295 -0
  11. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx.egg-info/PKG-INFO +1 -1
  12. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx.egg-info/SOURCES.txt +6 -0
  13. {tgraphx-0.4.2 → tgraphx-0.4.3}/LICENSE +0 -0
  14. {tgraphx-0.4.2 → tgraphx-0.4.3}/README.md +0 -0
  15. {tgraphx-0.4.2 → tgraphx-0.4.3}/setup.cfg +0 -0
  16. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_3d_support.py +0 -0
  17. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_algorithms.py +0 -0
  18. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_amp_compile.py +0 -0
  19. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_backward_compatibility.py +0 -0
  20. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_benchmark_smoke.py +0 -0
  21. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_chunking.py +0 -0
  22. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_dashboard.py +0 -0
  23. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_dashboard_metadata.py +0 -0
  24. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_dataset_cache.py +0 -0
  25. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_dataset_docs_claims.py +0 -0
  26. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_dataset_download_mocked.py +0 -0
  27. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_dataset_registry.py +0 -0
  28. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_datasets_base.py +0 -0
  29. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_devices.py +0 -0
  30. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_distributed_compat.py +0 -0
  31. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_documentation_claims.py +0 -0
  32. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_edge_features.py +0 -0
  33. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_edge_weight.py +0 -0
  34. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_experiments.py +0 -0
  35. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_explainability.py +0 -0
  36. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_factories.py +0 -0
  37. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_folder_datasets.py +0 -0
  38. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_gat_chunking.py +0 -0
  39. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_gnn_families.py +0 -0
  40. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_gradients.py +0 -0
  41. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_graph.py +0 -0
  42. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_graph_api.py +0 -0
  43. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_graph_builders.py +0 -0
  44. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_graph_transformer_v027.py +0 -0
  45. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_graph_utils.py +0 -0
  46. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_hard_negative_sampling.py +0 -0
  47. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_hetero_batch.py +0 -0
  48. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_hetero_layers.py +0 -0
  49. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_hetero_sampling.py +0 -0
  50. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_imports.py +0 -0
  51. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_layers.py +0 -0
  52. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_math.py +0 -0
  53. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_math_invariants_v030.py +0 -0
  54. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_metrics.py +0 -0
  55. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_mining_centrality.py +0 -0
  56. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_mining_core.py +0 -0
  57. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_mining_generators_spectral.py +0 -0
  58. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_mining_structural.py +0 -0
  59. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_model_zoo.py +0 -0
  60. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_models.py +0 -0
  61. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_negative_sampling.py +0 -0
  62. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_neural_mining.py +0 -0
  63. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_neural_mining_batched.py +0 -0
  64. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_packaging.py +0 -0
  65. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_performance_smoke.py +0 -0
  66. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_plotting.py +0 -0
  67. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_public_benchmarks.py +0 -0
  68. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_pyg_dgl_ogb_wrappers.py +0 -0
  69. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_random_walk_sample.py +0 -0
  70. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_release_validation_v030.py +0 -0
  71. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_reproducibility.py +0 -0
  72. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_sampling.py +0 -0
  73. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_sampling_loaders.py +0 -0
  74. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_synthetic_datasets.py +0 -0
  75. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_temporal_sampling.py +0 -0
  76. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_temporal_v025.py +0 -0
  77. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_time_encoding.py +0 -0
  78. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_tiny_overfit_v030.py +0 -0
  79. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_torchvision_wrappers.py +0 -0
  80. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_tracking.py +0 -0
  81. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_training.py +0 -0
  82. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_transforms.py +0 -0
  83. {tgraphx-0.4.2 → tgraphx-0.4.3}/tests/test_v024_features.py +0 -0
  84. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/algorithms/__init__.py +0 -0
  85. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/algorithms/connectivity.py +0 -0
  86. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/algorithms/structural.py +0 -0
  87. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/algorithms/traversal.py +0 -0
  88. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/core/__init__.py +0 -0
  89. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/core/dataloader.py +0 -0
  90. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/core/graph.py +0 -0
  91. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/core/graph_utils.py +0 -0
  92. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/core/hetero_batch.py +0 -0
  93. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/core/hetero_graph.py +0 -0
  94. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/core/temporal.py +0 -0
  95. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/core/temporal_batch.py +0 -0
  96. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/core/utils.py +0 -0
  97. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/dashboard/__init__.py +0 -0
  98. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/dashboard/__main__.py +0 -0
  99. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/dashboard/app.py +0 -0
  100. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/dashboard/static/dashboard.css +0 -0
  101. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/dashboard/static/dashboard.js +0 -0
  102. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/__init__.py +0 -0
  103. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/base.py +0 -0
  104. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/cache.py +0 -0
  105. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/converters.py +0 -0
  106. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/dgl_wrappers.py +0 -0
  107. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/download.py +0 -0
  108. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/errors.py +0 -0
  109. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/folder.py +0 -0
  110. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/metadata.py +0 -0
  111. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/ogb_wrappers.py +0 -0
  112. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/pyg_wrappers.py +0 -0
  113. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/registry.py +0 -0
  114. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/synthetic.py +0 -0
  115. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/datasets/torchvision_wrappers.py +0 -0
  116. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/distributed.py +0 -0
  117. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/experiments/__init__.py +0 -0
  118. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/experiments/callbacks.py +0 -0
  119. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/experiments/checkpoints.py +0 -0
  120. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/experiments/cli.py +0 -0
  121. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/experiments/config.py +0 -0
  122. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/experiments/grid.py +0 -0
  123. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/experiments/runner.py +0 -0
  124. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/experiments/summarize.py +0 -0
  125. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/explain/__init__.py +0 -0
  126. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/explain/attention.py +0 -0
  127. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/explain/edge_attribution.py +0 -0
  128. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/explain/export.py +0 -0
  129. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/explain/integrated_gradients.py +0 -0
  130. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/explain/patch_heatmap.py +0 -0
  131. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/explain/saliency.py +0 -0
  132. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/explain/utils.py +0 -0
  133. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/graph_builders.py +0 -0
  134. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/hetero_sampling.py +0 -0
  135. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/interop.py +0 -0
  136. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/__init__.py +0 -0
  137. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/_dim.py +0 -0
  138. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/_scatter.py +0 -0
  139. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/aggregator.py +0 -0
  140. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/appnp.py +0 -0
  141. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/attention_message.py +0 -0
  142. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/base.py +0 -0
  143. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/conv_message.py +0 -0
  144. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/factory.py +0 -0
  145. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/gat.py +0 -0
  146. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/gatv2.py +0 -0
  147. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/gin.py +0 -0
  148. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/graph_transformer.py +0 -0
  149. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/hetero.py +0 -0
  150. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/hetero_readout.py +0 -0
  151. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/pooling.py +0 -0
  152. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/safe_pool.py +0 -0
  153. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/sage.py +0 -0
  154. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/temporal_readout.py +0 -0
  155. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/transformer_encodings.py +0 -0
  156. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/layers/vector_gcn.py +0 -0
  157. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/learned_graph.py +0 -0
  158. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/metrics/__init__.py +0 -0
  159. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/metrics/classification.py +0 -0
  160. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/metrics/link_prediction.py +0 -0
  161. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/metrics/ogb.py +0 -0
  162. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/metrics/ranking.py +0 -0
  163. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/metrics/regression.py +0 -0
  164. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/metrics/reports.py +0 -0
  165. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/anomaly.py +0 -0
  166. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/api.py +0 -0
  167. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/centrality.py +0 -0
  168. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/communities.py +0 -0
  169. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/embeddings.py +0 -0
  170. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/frequent.py +0 -0
  171. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/generators.py +0 -0
  172. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/hetero.py +0 -0
  173. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/kernels.py +0 -0
  174. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/label_prop.py +0 -0
  175. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/link_prediction.py +0 -0
  176. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/motifs.py +0 -0
  177. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/neural.py +0 -0
  178. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/patterns.py +0 -0
  179. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/prototype.py +0 -0
  180. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/random_walk.py +0 -0
  181. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/reports.py +0 -0
  182. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/similarity.py +0 -0
  183. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/spectral.py +0 -0
  184. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/structural.py +0 -0
  185. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/mining/temporal.py +0 -0
  186. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/models/__init__.py +0 -0
  187. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/models/cnn_encoder.py +0 -0
  188. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/models/cnn_gnn_model.py +0 -0
  189. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/models/edge_predictor.py +0 -0
  190. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/models/factory.py +0 -0
  191. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/models/graph_classifier.py +0 -0
  192. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/models/hetero_models.py +0 -0
  193. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/models/model_zoo.py +0 -0
  194. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/models/node_classifier.py +0 -0
  195. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/models/pre_encoder.py +0 -0
  196. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/models/regressors.py +0 -0
  197. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/models/temporal_models.py +0 -0
  198. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/performance.py +0 -0
  199. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/plotting/__init__.py +0 -0
  200. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/plotting/graph.py +0 -0
  201. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/plotting/layouts.py +0 -0
  202. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/plotting/mining.py +0 -0
  203. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/plotting/utils.py +0 -0
  204. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/reproducibility.py +0 -0
  205. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/sampling.py +0 -0
  206. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/sampling_loaders.py +0 -0
  207. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/sampling_negative.py +0 -0
  208. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/temporal/__init__.py +0 -0
  209. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/temporal/time_encoding.py +0 -0
  210. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/temporal_sampling.py +0 -0
  211. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/tracking.py +0 -0
  212. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/training.py +0 -0
  213. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/transforms/__init__.py +0 -0
  214. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/transforms/compose.py +0 -0
  215. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/transforms/features.py +0 -0
  216. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/transforms/graph.py +0 -0
  217. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/transforms/patch.py +0 -0
  218. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/transforms/positional.py +0 -0
  219. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx/transforms/splits.py +0 -0
  220. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx.egg-info/dependency_links.txt +0 -0
  221. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx.egg-info/entry_points.txt +0 -0
  222. {tgraphx-0.4.2 → tgraphx-0.4.3}/tgraphx.egg-info/requires.txt +0 -0
  223. {tgraphx-0.4.2 → tgraphx-0.4.3}/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.2
3
+ Version: 0.4.3
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.2"
8
+ version = "0.4.3"
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,301 @@
1
+ """Tests for graph learning, self-supervised, and augmentation utilities."""
2
+ import pytest
3
+ import torch
4
+ from tgraphx.mining import (
5
+ contrastive_loss, supervised_contrastive_loss, triplet_loss,
6
+ bpr_loss, reconstruction_loss,
7
+ drop_edges, drop_nodes, mask_node_features, add_random_edges,
8
+ subgraph_sampling, DGIObjective, GraphCLObjective,
9
+ create_negative_pairs, create_positive_pairs_from_batch,
10
+ degree_encoding, random_walk_structural_encoding,
11
+ shortest_path_anchor_encoding, centrality_encoding,
12
+ StructuralEncodingModule, attach_structural_encodings,
13
+ GraphSequenceEncoder, GraphSequenceClassifier, GraphRNNEdgeGenerator,
14
+ bfs_sequence_encode, random_walk_sequence_encode, pad_sequences,
15
+ )
16
+
17
+
18
+ def _chain_ei(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
+ class TestContrastiveLosses:
25
+ def test_contrastive_loss_forward(self):
26
+ z1 = torch.randn(4, 8, requires_grad=True)
27
+ z2 = torch.randn(4, 8, requires_grad=True)
28
+ loss = contrastive_loss(z1, z2)
29
+ assert loss.dim() == 0
30
+ assert torch.isfinite(loss)
31
+
32
+ def test_contrastive_loss_backward(self):
33
+ z1 = torch.randn(4, 8, requires_grad=True)
34
+ z2 = torch.randn(4, 8, requires_grad=True)
35
+ loss = contrastive_loss(z1, z2)
36
+ loss.backward()
37
+ assert z1.grad is not None and torch.isfinite(z1.grad).all()
38
+
39
+ def test_contrastive_loss_batch_too_small(self):
40
+ z1 = torch.randn(1, 8)
41
+ z2 = torch.randn(1, 8)
42
+ with pytest.raises(ValueError, match="batch size"):
43
+ contrastive_loss(z1, z2)
44
+
45
+ def test_supervised_contrastive_loss(self):
46
+ emb = torch.randn(6, 8, requires_grad=True)
47
+ labels = torch.tensor([0, 0, 1, 1, 2, 2])
48
+ loss = supervised_contrastive_loss(emb, labels)
49
+ assert torch.isfinite(loss)
50
+ loss.backward()
51
+ assert emb.grad is not None
52
+
53
+ def test_triplet_loss(self):
54
+ a = torch.randn(4, 8, requires_grad=True)
55
+ p = torch.randn(4, 8, requires_grad=True)
56
+ n = torch.randn(4, 8, requires_grad=True)
57
+ loss = triplet_loss(a, p, n)
58
+ assert loss >= 0
59
+ loss.backward()
60
+ assert a.grad is not None
61
+
62
+ def test_bpr_loss(self):
63
+ pos = torch.randn(4, requires_grad=True)
64
+ neg = torch.randn(4, requires_grad=True)
65
+ loss = bpr_loss(pos, neg)
66
+ assert torch.isfinite(loss)
67
+ loss.backward()
68
+
69
+ def test_reconstruction_loss(self):
70
+ x = torch.randn(4, 8)
71
+ recon = torch.randn(4, 8, requires_grad=True)
72
+ loss = reconstruction_loss(x, recon)
73
+ assert torch.isfinite(loss)
74
+ loss.backward()
75
+
76
+
77
+ class TestAugmentations:
78
+ def test_drop_edges_deterministic(self):
79
+ ei, N = _chain_ei()
80
+ new1, _ = drop_edges(ei, p=0.3, seed=42)
81
+ new2, _ = drop_edges(ei, p=0.3, seed=42)
82
+ assert torch.equal(new1, new2)
83
+
84
+ def test_drop_edges_all(self):
85
+ ei, N = _chain_ei()
86
+ new_ei, _ = drop_edges(ei, p=1.0)
87
+ assert new_ei.size(1) == 0
88
+
89
+ def test_drop_edges_none(self):
90
+ ei, N = _chain_ei()
91
+ new_ei, _ = drop_edges(ei, p=0.0)
92
+ assert torch.equal(new_ei, ei)
93
+
94
+ def test_drop_nodes_shape(self):
95
+ ei, N = _chain_ei()
96
+ x = torch.randn(N, 4)
97
+ new_ei, new_N, new_x, kept = drop_nodes(ei, N, p=0.3, seed=0, node_features=x)
98
+ assert new_x.size(0) == new_N
99
+ assert new_ei.size(0) == 2
100
+
101
+ def test_drop_nodes_deterministic(self):
102
+ ei, N = _chain_ei()
103
+ _, _, _, k1 = drop_nodes(ei, N, p=0.3, seed=7)
104
+ _, _, _, k2 = drop_nodes(ei, N, p=0.3, seed=7)
105
+ assert torch.equal(k1, k2)
106
+
107
+ def test_mask_node_features(self):
108
+ x = torch.ones(5, 8)
109
+ masked, mask = mask_node_features(x, p=0.5, seed=0)
110
+ assert masked.shape == x.shape
111
+ assert mask.dtype == torch.bool
112
+ # Masked elements should be 0.
113
+ assert (masked[mask] == 0.0).all()
114
+ # Non-masked elements should be 1.
115
+ assert (masked[~mask] == 1.0).all()
116
+
117
+ def test_mask_features_deterministic(self):
118
+ x = torch.ones(5, 8)
119
+ _, m1 = mask_node_features(x, p=0.3, seed=1)
120
+ _, m2 = mask_node_features(x, p=0.3, seed=1)
121
+ assert torch.equal(m1, m2)
122
+
123
+ def test_add_random_edges(self):
124
+ ei, N = _chain_ei()
125
+ new_ei = add_random_edges(ei, N, num_add=3, seed=0)
126
+ assert new_ei.size(1) == ei.size(1) + 3
127
+
128
+ def test_subgraph_sampling(self):
129
+ ei, N = _chain_ei(8)
130
+ x = torch.randn(8, 4)
131
+ new_ei, new_N, new_x, sampled = subgraph_sampling(ei, 8, 4, seed=0, node_features=x)
132
+ assert new_N == 4
133
+ assert new_x.size(0) == 4
134
+ assert sampled.size(0) == 4
135
+
136
+
137
+ class TestSelfSupervisedObjectives:
138
+ def test_dgi_objective_forward(self):
139
+ dgi = DGIObjective(embed_dim=8, summary_dim=8)
140
+ pos_emb = torch.randn(5, 8, requires_grad=True)
141
+ neg_emb = torch.randn(5, 8, requires_grad=True)
142
+ loss = dgi(pos_emb, neg_emb)
143
+ assert torch.isfinite(loss)
144
+ loss.backward()
145
+ assert pos_emb.grad is not None
146
+
147
+ def test_graphcl_objective(self):
148
+ gcl = GraphCLObjective(project_dim=8)
149
+ z1 = torch.randn(4, 8, requires_grad=True)
150
+ z2 = torch.randn(4, 8, requires_grad=True)
151
+ loss = gcl(z1, z2)
152
+ assert torch.isfinite(loss)
153
+ loss.backward()
154
+
155
+
156
+ class TestStructuralEncodings:
157
+ def test_degree_encoding_shape(self):
158
+ ei, N = _chain_ei()
159
+ enc = degree_encoding(ei, N)
160
+ assert enc.shape == (N, 2)
161
+ assert enc.dtype == torch.float32
162
+
163
+ def test_degree_encoding_star_hub(self):
164
+ from tgraphx.mining import star_graph
165
+ ei, N = star_graph(5)
166
+ enc = degree_encoding(ei, N, normalize=False)
167
+ # Hub (node 0) should have highest degree.
168
+ assert float(enc[0, 0]) == 4.0
169
+
170
+ def test_centrality_encoding_shape(self):
171
+ ei, N = _chain_ei(6)
172
+ enc = centrality_encoding(ei, N, include=["degree", "pagerank"])
173
+ assert enc.shape == (N, 2)
174
+
175
+ def test_attach_concat(self):
176
+ ei, N = _chain_ei()
177
+ enc = degree_encoding(ei, N)
178
+ x = torch.randn(N, 6)
179
+ aug = attach_structural_encodings(x, enc)
180
+ assert aug.shape == (N, 8) # 6 + 2
181
+
182
+ def test_attach_spatial_raises(self):
183
+ ei, N = _chain_ei()
184
+ enc = degree_encoding(ei, N)
185
+ x = torch.randn(N, 3, 4, 4) # spatial
186
+ with pytest.raises(ValueError, match="mode='side'"):
187
+ attach_structural_encodings(x, enc, mode="concat")
188
+
189
+ def test_attach_side_mode(self):
190
+ ei, N = _chain_ei()
191
+ enc = degree_encoding(ei, N)
192
+ x = torch.randn(N, 3, 4, 4)
193
+ result = attach_structural_encodings(x, enc, mode="side")
194
+ assert result is enc
195
+
196
+ def test_structural_encoding_module(self):
197
+ mod = StructuralEncodingModule(in_dim=2, out_dim=8)
198
+ enc = torch.randn(5, 2)
199
+ out = mod(enc)
200
+ assert out.shape == (5, 8)
201
+ out.sum().backward()
202
+ assert all(p.grad is not None for p in mod.parameters())
203
+
204
+
205
+ class TestSequenceModels:
206
+ def test_bfs_sequence_encode(self):
207
+ ei, N = _chain_ei()
208
+ x = torch.randn(N, 4)
209
+ seq = bfs_sequence_encode(ei, N, node_features=x, start=0)
210
+ assert seq.shape[1] == 4
211
+ assert seq.shape[0] == N
212
+
213
+ def test_random_walk_sequence_encode(self):
214
+ ei, N = _chain_ei()
215
+ x = torch.randn(N, 4)
216
+ seq = random_walk_sequence_encode(ei, N, walk_length=5, node_features=x, seed=0)
217
+ assert seq.shape == (6, 4) # walk_length + 1
218
+
219
+ def test_pad_sequences(self):
220
+ seqs = [torch.randn(3, 4), torch.randn(5, 4), torch.randn(2, 4)]
221
+ padded, lengths = pad_sequences(seqs)
222
+ assert padded.shape == (3, 5, 4)
223
+ assert lengths.tolist() == [3, 5, 2]
224
+
225
+ def test_graph_sequence_encoder_forward(self):
226
+ enc = GraphSequenceEncoder(input_dim=4, hidden_dim=16, num_layers=2)
227
+ x = torch.randn(3, 7, 4)
228
+ out = enc(x)
229
+ assert out.shape == (3, 16)
230
+
231
+ def test_graph_sequence_encoder_backward(self):
232
+ enc = GraphSequenceEncoder(input_dim=4, hidden_dim=16, num_layers=2)
233
+ x = torch.randn(3, 7, 4)
234
+ out = enc(x)
235
+ out.sum().backward()
236
+ for p in enc.parameters():
237
+ assert p.grad is not None
238
+
239
+ def test_graph_sequence_classifier_overfit(self):
240
+ clf = GraphSequenceClassifier(input_dim=4, hidden_dim=32, num_classes=2)
241
+ opt = torch.optim.Adam(clf.parameters(), lr=1e-2)
242
+ # Class 0: mostly-zero sequences; class 1: mostly-one sequences.
243
+ x0 = torch.randn(3, 5, 4) * 0.01
244
+ x1 = torch.randn(3, 5, 4) + 5.0
245
+ X = torch.cat([x0, x1])
246
+ y = torch.tensor([0,0,0,1,1,1])
247
+ losses = []
248
+ for _ in range(30):
249
+ opt.zero_grad()
250
+ logits = clf(X)
251
+ loss = torch.nn.functional.cross_entropy(logits, y)
252
+ loss.backward()
253
+ opt.step()
254
+ losses.append(loss.detach().item())
255
+ assert losses[-1] < losses[0], f"Loss did not decrease: {losses[0]:.4f}→{losses[-1]:.4f}"
256
+
257
+ def test_graph_rnn_generator_shape(self):
258
+ gen = GraphRNNEdgeGenerator(max_nodes=8)
259
+ adj = gen.generate(5, seed=0)
260
+ assert adj.shape == (5, 5)
261
+ # Symmetric.
262
+ assert torch.equal(adj, adj.t())
263
+
264
+ def test_graph_rnn_generator_deterministic(self):
265
+ gen = GraphRNNEdgeGenerator(max_nodes=8)
266
+ adj1 = gen.generate(5, seed=42)
267
+ adj2 = gen.generate(5, seed=42)
268
+ assert torch.equal(adj1, adj2)
269
+
270
+ def test_graph_rnn_generator_backward(self):
271
+ gen = GraphRNNEdgeGenerator(max_nodes=6)
272
+ # Teacher-forcing training step.
273
+ opt = torch.optim.Adam(gen.parameters(), lr=1e-2)
274
+ # Target: random binary adjacency rows.
275
+ N = 4
276
+ B = 2
277
+ target = torch.zeros(B, N, 6)
278
+ for i in range(1, N):
279
+ target[:, i, :i] = (torch.rand(B, i) > 0.5).float()
280
+ logits, _ = gen(target)
281
+ # BCEWithLogits loss on upper triangle.
282
+ loss = torch.nn.functional.binary_cross_entropy_with_logits(logits, target)
283
+ loss.backward()
284
+ assert all(p.grad is not None for p in gen.parameters())
285
+
286
+
287
+ class TestNegativePairUtils:
288
+ def test_create_negative_pairs_no_false_neg(self):
289
+ ei = torch.tensor([[0,1],[1,2]], dtype=torch.long)
290
+ neg = create_negative_pairs(ei, num_nodes=4, num_neg=4, seed=0)
291
+ pos_set = {(int(ei[0,i]), int(ei[1,i])) for i in range(ei.size(1))}
292
+ neg_set = {(int(neg[0,i]), int(neg[1,i])) for i in range(neg.size(1))}
293
+ assert not (pos_set & neg_set)
294
+
295
+ def test_create_positive_pairs(self):
296
+ labels = torch.tensor([0, 0, 1, 1, 0])
297
+ pa, pb = create_positive_pairs_from_batch(labels)
298
+ assert pa.size(0) > 0
299
+ for a, b in zip(pa.tolist(), pb.tolist()):
300
+ assert labels[a] == labels[b]
301
+ assert a != b
@@ -0,0 +1,251 @@
1
+ """Tests for tgraphx.mining.paths — graph path algorithms."""
2
+ import math
3
+ import pytest
4
+ import torch
5
+ from tgraphx.mining import (
6
+ bfs_order, dfs_order, multi_source_bfs, reachable_nodes,
7
+ dijkstra_shortest_path, batched_shortest_path_length,
8
+ all_pairs_shortest_path_length, reconstruct_path,
9
+ minimum_spanning_tree, maximum_spanning_tree,
10
+ cut_size, normalized_cut, conductance, volume, boundary_edges,
11
+ write_path_summary,
12
+ )
13
+
14
+
15
+ def _chain(N=4):
16
+ src = list(range(N-1)) + list(range(1, N))
17
+ dst = list(range(1, N)) + list(range(N-1))
18
+ return torch.tensor([src, dst], dtype=torch.long), N
19
+
20
+
21
+ def _star(N=5):
22
+ src = [0]*(N-1) + list(range(1, N))
23
+ dst = list(range(1, N)) + [0]*(N-1)
24
+ return torch.tensor([src, dst], dtype=torch.long), N
25
+
26
+
27
+ class TestBFSOrder:
28
+ def test_chain_bfs(self):
29
+ ei, N = _chain()
30
+ order = bfs_order(ei, 0, N)
31
+ assert order[0].item() == 0
32
+ assert sorted(order.tolist()) == list(range(N))
33
+
34
+ def test_disconnected_partial(self):
35
+ ei = torch.tensor([[0, 2], [1, 3]], dtype=torch.long)
36
+ order = bfs_order(ei, 0, 4)
37
+ # Start at 0; only reaches 0 and 1.
38
+ assert set(order.tolist()) == {0, 1}
39
+
40
+ def test_invalid_start(self):
41
+ ei, N = _chain()
42
+ with pytest.raises(ValueError, match="start"):
43
+ bfs_order(ei, N + 5, N)
44
+
45
+ def test_single_node(self):
46
+ ei = torch.zeros((2, 0), dtype=torch.long)
47
+ order = bfs_order(ei, 0, 1)
48
+ assert order.tolist() == [0]
49
+
50
+ def test_star_hub_first(self):
51
+ ei, N = _star()
52
+ order = bfs_order(ei, 0, N)
53
+ assert order[0].item() == 0
54
+ assert order.size(0) == N
55
+
56
+
57
+ class TestDFSOrder:
58
+ def test_chain_dfs(self):
59
+ ei, N = _chain()
60
+ order = dfs_order(ei, 0, N)
61
+ assert order[0].item() == 0
62
+ assert sorted(order.tolist()) == list(range(N))
63
+
64
+ def test_disconnected(self):
65
+ ei = torch.tensor([[0], [1]], dtype=torch.long)
66
+ order = dfs_order(ei, 0, 4)
67
+ # Directed; 0->1 but 2,3 unreachable.
68
+ assert set(order.tolist()) == {0, 1}
69
+
70
+
71
+ class TestMultiSourceBFS:
72
+ def test_two_sources(self):
73
+ ei, N = _chain()
74
+ sources = torch.tensor([0, 3], dtype=torch.long)
75
+ dist = multi_source_bfs(ei, sources, N)
76
+ # node 0: dist 0, node 1: dist 1 from 0, node 3: dist 0, node 2: dist 1 from 3
77
+ assert dist[0].item() == 0
78
+ assert dist[3].item() == 0
79
+ assert dist[1].item() == 1
80
+ assert dist[2].item() == 1
81
+
82
+ def test_unreachable_minus_one(self):
83
+ ei = torch.tensor([[0], [1]], dtype=torch.long)
84
+ sources = torch.tensor([0], dtype=torch.long)
85
+ dist = multi_source_bfs(ei, sources, 4, directed=True)
86
+ assert dist[2].item() == -1
87
+ assert dist[3].item() == -1
88
+
89
+
90
+ class TestDijkstra:
91
+ def test_chain_unit_weights(self):
92
+ ei, N = _chain()
93
+ dist, pred = dijkstra_shortest_path(ei, 0, N)
94
+ assert dist.tolist() == [0.0, 1.0, 2.0, 3.0]
95
+
96
+ def test_weighted_graph(self):
97
+ # Triangle: 0-1 weight 1, 1-2 weight 1, 0-2 weight 10.
98
+ ei = torch.tensor([[0, 1, 0, 1, 2, 2], [1, 0, 2, 2, 0, 1]], dtype=torch.long)
99
+ w = torch.tensor([1.0, 1.0, 10.0, 1.0, 10.0, 1.0])
100
+ dist, pred = dijkstra_shortest_path(ei, 0, 3, edge_weight=w)
101
+ assert abs(float(dist[0]) - 0.0) < 1e-6
102
+ assert abs(float(dist[1]) - 1.0) < 1e-6
103
+ assert abs(float(dist[2]) - 2.0) < 1e-6 # via 0→1→2
104
+
105
+ def test_unreachable_inf(self):
106
+ ei = torch.tensor([[0], [1]], dtype=torch.long)
107
+ dist, _ = dijkstra_shortest_path(ei, 0, 4, directed=True)
108
+ assert dist[0].item() == 0.0
109
+ assert dist[1].item() == 1.0
110
+ assert math.isinf(float(dist[2]))
111
+ assert math.isinf(float(dist[3]))
112
+
113
+ def test_source_zero_dist(self):
114
+ ei, N = _chain()
115
+ dist, _ = dijkstra_shortest_path(ei, 2, N)
116
+ assert dist[2].item() == 0.0
117
+
118
+ def test_negative_weight_raises(self):
119
+ ei = torch.tensor([[0, 1], [1, 0]], dtype=torch.long)
120
+ w = torch.tensor([1.0, -1.0])
121
+ with pytest.raises(ValueError, match="non-negative"):
122
+ dijkstra_shortest_path(ei, 0, 2, edge_weight=w)
123
+
124
+ def test_reconstruct_path(self):
125
+ ei, N = _chain()
126
+ dist, pred = dijkstra_shortest_path(ei, 0, N)
127
+ path = reconstruct_path(0, 3, pred)
128
+ assert path == [0, 1, 2, 3]
129
+
130
+ def test_reconstruct_path_unreachable(self):
131
+ ei = torch.zeros((2, 0), dtype=torch.long)
132
+ _, pred = dijkstra_shortest_path(ei, 0, 4)
133
+ path = reconstruct_path(0, 3, pred)
134
+ assert path == []
135
+
136
+ def test_reconstruct_path_self(self):
137
+ ei, N = _chain()
138
+ _, pred = dijkstra_shortest_path(ei, 0, N)
139
+ path = reconstruct_path(0, 0, pred)
140
+ assert path == [0]
141
+
142
+
143
+ class TestAllPairsSP:
144
+ def test_chain_symmetric(self):
145
+ ei, N = _chain()
146
+ D = all_pairs_shortest_path_length(ei, N)
147
+ assert D.shape == (N, N)
148
+ assert torch.allclose(D, D.t()) # undirected → symmetric
149
+
150
+ def test_chain_diagonal_zero(self):
151
+ ei, N = _chain()
152
+ D = all_pairs_shortest_path_length(ei, N)
153
+ assert (D.diagonal() == 0).all()
154
+
155
+ def test_size_guard(self):
156
+ ei = torch.zeros((2, 0), dtype=torch.long)
157
+ with pytest.raises(ValueError, match="max_nodes"):
158
+ all_pairs_shortest_path_length(ei, 1001, max_nodes=1000)
159
+
160
+
161
+ class TestSpanningTree:
162
+ def test_triangle_mst_two_edges(self):
163
+ # Triangle K3: 3 edges, MST should pick 2 cheapest.
164
+ ei = torch.tensor([[0,1,2,1,2,0],[1,2,0,0,1,2]], dtype=torch.long)
165
+ w = torch.tensor([1.0, 2.0, 3.0, 1.0, 2.0, 3.0])
166
+ mst_ei, mst_w, total = minimum_spanning_tree(ei, 3, edge_weight=w)
167
+ # MST should have 2 undirected edges (= 4 directed edge_index entries).
168
+ assert mst_ei.size(1) == 4
169
+ assert abs(total - 3.0) < 1e-6 # edges of weight 1+2
170
+
171
+ def test_mst_unit_weights(self):
172
+ ei, N = _chain()
173
+ mst_ei, _, total = minimum_spanning_tree(ei, N)
174
+ # Chain is already a spanning tree; MST = all edges.
175
+ assert abs(total - float(N - 1)) < 1e-6
176
+
177
+ def test_maxst_total_ge_mst_total(self):
178
+ ei = torch.tensor([[0,1,2,1,2,0],[1,2,0,0,1,2]], dtype=torch.long)
179
+ w = torch.tensor([1.0, 2.0, 3.0, 1.0, 2.0, 3.0])
180
+ _, _, min_total = minimum_spanning_tree(ei, 3, w)
181
+ _, _, max_total = maximum_spanning_tree(ei, 3, w)
182
+ assert max_total >= min_total
183
+
184
+ def test_empty_graph(self):
185
+ ei = torch.zeros((2, 0), dtype=torch.long)
186
+ mst_ei, _, _ = minimum_spanning_tree(ei, 3)
187
+ assert mst_ei.size(1) == 0
188
+
189
+ def test_disconnected_forest(self):
190
+ # Two disjoint edges: 0-1 and 2-3.
191
+ ei = torch.tensor([[0,1,2,3],[1,0,3,2]], dtype=torch.long)
192
+ mst_ei, _, _ = minimum_spanning_tree(ei, 4)
193
+ assert mst_ei.size(1) == 4 # 2 undirected edges (forest)
194
+
195
+
196
+ class TestCutsAndMetrics:
197
+ def test_cut_size_chain(self):
198
+ ei, N = _chain()
199
+ subset = torch.tensor([0, 1])
200
+ # Both directed crossings (1→2 and 2→1) for the undirected 1-2 edge.
201
+ assert cut_size(ei, N, subset) == 2
202
+
203
+ def test_cut_size_empty_subset(self):
204
+ ei, N = _chain()
205
+ assert cut_size(ei, N, torch.zeros(0, dtype=torch.long)) == 0
206
+
207
+ def test_cut_size_full_subset(self):
208
+ ei, N = _chain()
209
+ assert cut_size(ei, N, torch.arange(N)) == 0
210
+
211
+ def test_volume_star(self):
212
+ ei, N = _star()
213
+ # Hub has degree N-1.
214
+ vol_hub = volume(ei, N, torch.tensor([0]))
215
+ assert vol_hub == N - 1
216
+
217
+ def test_conductance_two_cliques(self):
218
+ # Two cliques {0,1,2} and {3,4,5} connected by one edge 2-3.
219
+ src = [0,1,2,3,4,1,2,4,5,2]
220
+ dst = [1,2,3,4,5,0,1,3,4,3]
221
+ ei = torch.tensor([src+dst, dst+src], dtype=torch.long)
222
+ ei = torch.unique(ei, dim=1)
223
+ N = 6
224
+ subset = torch.tensor([0, 1, 2])
225
+ c = conductance(ei, N, subset)
226
+ assert c >= 0.0 and c <= 1.0
227
+
228
+ def test_normalized_cut_two_cliques(self):
229
+ src = [0,1,2,3,4,1,2,4,5]
230
+ dst = [1,2,0,4,5,0,1,3,4]
231
+ ei = torch.tensor([src+dst, dst+src], dtype=torch.long)
232
+ ei = torch.unique(ei, dim=1)
233
+ labels = torch.tensor([0,0,0,1,1,1])
234
+ nc = normalized_cut(ei, 6, labels)
235
+ assert nc >= 0.0
236
+
237
+ def test_boundary_edges_shape(self):
238
+ ei, N = _chain()
239
+ subset = torch.tensor([0, 1])
240
+ be = boundary_edges(ei, N, subset)
241
+ assert be.shape[0] == 2
242
+ assert be.shape[1] > 0
243
+
244
+ def test_write_path_summary(self, tmp_path):
245
+ p = write_path_summary(
246
+ str(tmp_path / "algo.json"),
247
+ source=0, num_reachable=4, mean_distance=1.5,
248
+ )
249
+ import json; d = json.loads(open(p).read())
250
+ assert d["source"] == 0
251
+ assert d["mean_distance"] == 1.5
@@ -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.2"
13
+ __version__ = "0.4.3"
14
14
 
15
15
  # ── Core data structures ──────────────────────────────────────────────────────
16
16
  from .core.graph import Graph, GraphBatch
@@ -221,6 +221,65 @@ from .api import (
221
221
  run_link_prediction_baseline,
222
222
  )
223
223
 
224
+ # ── Graph paths and algorithms (Beta) ────────────────────────────────────────
225
+ from .paths import (
226
+ bfs_order,
227
+ dfs_order,
228
+ multi_source_bfs,
229
+ reachable_nodes,
230
+ dijkstra_shortest_path,
231
+ batched_shortest_path_length,
232
+ all_pairs_shortest_path_length,
233
+ reconstruct_path,
234
+ minimum_spanning_tree,
235
+ maximum_spanning_tree,
236
+ cut_size,
237
+ normalized_cut,
238
+ conductance,
239
+ volume,
240
+ boundary_edges,
241
+ write_path_summary,
242
+ )
243
+
244
+ # ── Graph learning utilities (Experimental) ───────────────────────────────────
245
+ from .graph_learning import (
246
+ contrastive_loss,
247
+ supervised_contrastive_loss,
248
+ triplet_loss,
249
+ bpr_loss,
250
+ reconstruction_loss,
251
+ drop_edges,
252
+ drop_nodes,
253
+ mask_node_features,
254
+ add_random_edges,
255
+ subgraph_sampling,
256
+ DGIObjective,
257
+ GraphCLObjective,
258
+ create_negative_pairs,
259
+ create_positive_pairs_from_batch,
260
+ )
261
+
262
+ # ── Structural encodings (Beta) ───────────────────────────────────────────────
263
+ from .structural_encodings import (
264
+ degree_encoding,
265
+ random_walk_structural_encoding,
266
+ shortest_path_anchor_encoding,
267
+ centrality_encoding,
268
+ community_encoding,
269
+ StructuralEncodingModule,
270
+ attach_structural_encodings,
271
+ )
272
+
273
+ # ── Graph sequence models (Experimental) ─────────────────────────────────────
274
+ from .sequence_models import (
275
+ GraphSequenceEncoder,
276
+ GraphSequenceClassifier,
277
+ GraphRNNEdgeGenerator,
278
+ bfs_sequence_encode,
279
+ random_walk_sequence_encode,
280
+ pad_sequences,
281
+ )
282
+
224
283
  # ── Neural mining (Experimental) ─────────────────────────────────────────────
225
284
  from .neural import (
226
285
  PrototypeMembershipScorer,