exonware-xwnode 0.0.1.21__py3-none-any.whl → 0.0.1.23__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 (250) hide show
  1. exonware/__init__.py +8 -1
  2. exonware/xwnode/__init__.py +18 -5
  3. exonware/xwnode/add_strategy_types.py +165 -0
  4. exonware/xwnode/base.py +7 -5
  5. exonware/xwnode/common/__init__.py +1 -1
  6. exonware/xwnode/common/graph/__init__.py +30 -0
  7. exonware/xwnode/common/graph/caching.py +131 -0
  8. exonware/xwnode/common/graph/contracts.py +100 -0
  9. exonware/xwnode/common/graph/errors.py +44 -0
  10. exonware/xwnode/common/graph/indexing.py +260 -0
  11. exonware/xwnode/common/graph/manager.py +568 -0
  12. exonware/xwnode/common/management/__init__.py +3 -5
  13. exonware/xwnode/common/management/manager.py +9 -9
  14. exonware/xwnode/common/management/migration.py +6 -6
  15. exonware/xwnode/common/monitoring/__init__.py +3 -5
  16. exonware/xwnode/common/monitoring/metrics.py +7 -3
  17. exonware/xwnode/common/monitoring/pattern_detector.py +2 -2
  18. exonware/xwnode/common/monitoring/performance_monitor.py +6 -2
  19. exonware/xwnode/common/patterns/__init__.py +3 -5
  20. exonware/xwnode/common/patterns/advisor.py +1 -1
  21. exonware/xwnode/common/patterns/flyweight.py +6 -2
  22. exonware/xwnode/common/patterns/registry.py +203 -184
  23. exonware/xwnode/common/utils/__init__.py +25 -11
  24. exonware/xwnode/common/utils/simple.py +1 -1
  25. exonware/xwnode/config.py +3 -8
  26. exonware/xwnode/contracts.py +4 -105
  27. exonware/xwnode/defs.py +413 -159
  28. exonware/xwnode/edges/strategies/__init__.py +86 -4
  29. exonware/xwnode/edges/strategies/_base_edge.py +2 -2
  30. exonware/xwnode/edges/strategies/adj_list.py +287 -121
  31. exonware/xwnode/edges/strategies/adj_matrix.py +316 -222
  32. exonware/xwnode/edges/strategies/base.py +1 -1
  33. exonware/xwnode/edges/strategies/{edge_bidir_wrapper.py → bidir_wrapper.py} +45 -4
  34. exonware/xwnode/edges/strategies/bitemporal.py +520 -0
  35. exonware/xwnode/edges/strategies/{edge_block_adj_matrix.py → block_adj_matrix.py} +77 -6
  36. exonware/xwnode/edges/strategies/bv_graph.py +664 -0
  37. exonware/xwnode/edges/strategies/compressed_graph.py +217 -0
  38. exonware/xwnode/edges/strategies/{edge_coo.py → coo.py} +46 -4
  39. exonware/xwnode/edges/strategies/{edge_csc.py → csc.py} +45 -4
  40. exonware/xwnode/edges/strategies/{edge_csr.py → csr.py} +94 -12
  41. exonware/xwnode/edges/strategies/{edge_dynamic_adj_list.py → dynamic_adj_list.py} +46 -4
  42. exonware/xwnode/edges/strategies/edge_list.py +168 -0
  43. exonware/xwnode/edges/strategies/edge_property_store.py +2 -2
  44. exonware/xwnode/edges/strategies/euler_tour.py +560 -0
  45. exonware/xwnode/edges/strategies/{edge_flow_network.py → flow_network.py} +2 -2
  46. exonware/xwnode/edges/strategies/graphblas.py +449 -0
  47. exonware/xwnode/edges/strategies/hnsw.py +637 -0
  48. exonware/xwnode/edges/strategies/hop2_labels.py +467 -0
  49. exonware/xwnode/edges/strategies/{edge_hyperedge_set.py → hyperedge_set.py} +2 -2
  50. exonware/xwnode/edges/strategies/incidence_matrix.py +250 -0
  51. exonware/xwnode/edges/strategies/k2_tree.py +613 -0
  52. exonware/xwnode/edges/strategies/link_cut.py +626 -0
  53. exonware/xwnode/edges/strategies/multiplex.py +532 -0
  54. exonware/xwnode/edges/strategies/{edge_neural_graph.py → neural_graph.py} +2 -2
  55. exonware/xwnode/edges/strategies/{edge_octree.py → octree.py} +69 -11
  56. exonware/xwnode/edges/strategies/{edge_quadtree.py → quadtree.py} +66 -10
  57. exonware/xwnode/edges/strategies/roaring_adj.py +438 -0
  58. exonware/xwnode/edges/strategies/{edge_rtree.py → rtree.py} +43 -5
  59. exonware/xwnode/edges/strategies/{edge_temporal_edgeset.py → temporal_edgeset.py} +24 -5
  60. exonware/xwnode/edges/strategies/{edge_tree_graph_basic.py → tree_graph_basic.py} +78 -7
  61. exonware/xwnode/edges/strategies/{edge_weighted_graph.py → weighted_graph.py} +188 -10
  62. exonware/xwnode/errors.py +3 -6
  63. exonware/xwnode/facade.py +20 -20
  64. exonware/xwnode/nodes/strategies/__init__.py +29 -9
  65. exonware/xwnode/nodes/strategies/adjacency_list.py +650 -177
  66. exonware/xwnode/nodes/strategies/aho_corasick.py +358 -183
  67. exonware/xwnode/nodes/strategies/array_list.py +36 -3
  68. exonware/xwnode/nodes/strategies/art.py +581 -0
  69. exonware/xwnode/nodes/strategies/{node_avl_tree.py → avl_tree.py} +77 -6
  70. exonware/xwnode/nodes/strategies/{node_b_plus_tree.py → b_plus_tree.py} +81 -40
  71. exonware/xwnode/nodes/strategies/{node_btree.py → b_tree.py} +79 -9
  72. exonware/xwnode/nodes/strategies/base.py +469 -98
  73. exonware/xwnode/nodes/strategies/{node_bitmap.py → bitmap.py} +12 -12
  74. exonware/xwnode/nodes/strategies/{node_bitset_dynamic.py → bitset_dynamic.py} +11 -11
  75. exonware/xwnode/nodes/strategies/{node_bloom_filter.py → bloom_filter.py} +15 -2
  76. exonware/xwnode/nodes/strategies/bloomier_filter.py +519 -0
  77. exonware/xwnode/nodes/strategies/bw_tree.py +531 -0
  78. exonware/xwnode/nodes/strategies/contracts.py +1 -1
  79. exonware/xwnode/nodes/strategies/{node_count_min_sketch.py → count_min_sketch.py} +3 -2
  80. exonware/xwnode/nodes/strategies/{node_cow_tree.py → cow_tree.py} +135 -13
  81. exonware/xwnode/nodes/strategies/crdt_map.py +629 -0
  82. exonware/xwnode/nodes/strategies/{node_cuckoo_hash.py → cuckoo_hash.py} +2 -2
  83. exonware/xwnode/nodes/strategies/{node_xdata_optimized.py → data_interchange_optimized.py} +21 -4
  84. exonware/xwnode/nodes/strategies/dawg.py +876 -0
  85. exonware/xwnode/nodes/strategies/deque.py +321 -153
  86. exonware/xwnode/nodes/strategies/extendible_hash.py +93 -0
  87. exonware/xwnode/nodes/strategies/{node_fenwick_tree.py → fenwick_tree.py} +111 -19
  88. exonware/xwnode/nodes/strategies/hamt.py +403 -0
  89. exonware/xwnode/nodes/strategies/hash_map.py +354 -67
  90. exonware/xwnode/nodes/strategies/heap.py +105 -5
  91. exonware/xwnode/nodes/strategies/hopscotch_hash.py +525 -0
  92. exonware/xwnode/nodes/strategies/{node_hyperloglog.py → hyperloglog.py} +6 -5
  93. exonware/xwnode/nodes/strategies/interval_tree.py +742 -0
  94. exonware/xwnode/nodes/strategies/kd_tree.py +703 -0
  95. exonware/xwnode/nodes/strategies/learned_index.py +533 -0
  96. exonware/xwnode/nodes/strategies/linear_hash.py +93 -0
  97. exonware/xwnode/nodes/strategies/linked_list.py +316 -119
  98. exonware/xwnode/nodes/strategies/{node_lsm_tree.py → lsm_tree.py} +219 -15
  99. exonware/xwnode/nodes/strategies/masstree.py +130 -0
  100. exonware/xwnode/nodes/strategies/{node_persistent_tree.py → persistent_tree.py} +149 -9
  101. exonware/xwnode/nodes/strategies/priority_queue.py +544 -132
  102. exonware/xwnode/nodes/strategies/queue.py +249 -120
  103. exonware/xwnode/nodes/strategies/{node_red_black_tree.py → red_black_tree.py} +183 -72
  104. exonware/xwnode/nodes/strategies/{node_roaring_bitmap.py → roaring_bitmap.py} +19 -6
  105. exonware/xwnode/nodes/strategies/rope.py +717 -0
  106. exonware/xwnode/nodes/strategies/{node_segment_tree.py → segment_tree.py} +106 -106
  107. exonware/xwnode/nodes/strategies/{node_set_hash.py → set_hash.py} +30 -29
  108. exonware/xwnode/nodes/strategies/{node_skip_list.py → skip_list.py} +74 -6
  109. exonware/xwnode/nodes/strategies/sparse_matrix.py +427 -131
  110. exonware/xwnode/nodes/strategies/{node_splay_tree.py → splay_tree.py} +55 -6
  111. exonware/xwnode/nodes/strategies/stack.py +244 -112
  112. exonware/xwnode/nodes/strategies/{node_suffix_array.py → suffix_array.py} +5 -1
  113. exonware/xwnode/nodes/strategies/t_tree.py +94 -0
  114. exonware/xwnode/nodes/strategies/{node_treap.py → treap.py} +75 -6
  115. exonware/xwnode/nodes/strategies/{node_tree_graph_hybrid.py → tree_graph_hybrid.py} +46 -5
  116. exonware/xwnode/nodes/strategies/trie.py +153 -9
  117. exonware/xwnode/nodes/strategies/union_find.py +111 -5
  118. exonware/xwnode/nodes/strategies/veb_tree.py +856 -0
  119. exonware/xwnode/strategies/__init__.py +5 -51
  120. exonware/xwnode/version.py +3 -3
  121. {exonware_xwnode-0.0.1.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/METADATA +23 -3
  122. exonware_xwnode-0.0.1.23.dist-info/RECORD +130 -0
  123. exonware/xwnode/edges/strategies/edge_adj_list.py +0 -353
  124. exonware/xwnode/edges/strategies/edge_adj_matrix.py +0 -445
  125. exonware/xwnode/nodes/strategies/_base_node.py +0 -307
  126. exonware/xwnode/nodes/strategies/node_aho_corasick.py +0 -525
  127. exonware/xwnode/nodes/strategies/node_array_list.py +0 -179
  128. exonware/xwnode/nodes/strategies/node_hash_map.py +0 -273
  129. exonware/xwnode/nodes/strategies/node_heap.py +0 -196
  130. exonware/xwnode/nodes/strategies/node_linked_list.py +0 -413
  131. exonware/xwnode/nodes/strategies/node_trie.py +0 -257
  132. exonware/xwnode/nodes/strategies/node_union_find.py +0 -192
  133. exonware/xwnode/queries/executors/__init__.py +0 -47
  134. exonware/xwnode/queries/executors/advanced/__init__.py +0 -37
  135. exonware/xwnode/queries/executors/advanced/aggregate_executor.py +0 -50
  136. exonware/xwnode/queries/executors/advanced/ask_executor.py +0 -50
  137. exonware/xwnode/queries/executors/advanced/construct_executor.py +0 -50
  138. exonware/xwnode/queries/executors/advanced/describe_executor.py +0 -50
  139. exonware/xwnode/queries/executors/advanced/for_loop_executor.py +0 -50
  140. exonware/xwnode/queries/executors/advanced/foreach_executor.py +0 -50
  141. exonware/xwnode/queries/executors/advanced/join_executor.py +0 -50
  142. exonware/xwnode/queries/executors/advanced/let_executor.py +0 -50
  143. exonware/xwnode/queries/executors/advanced/mutation_executor.py +0 -50
  144. exonware/xwnode/queries/executors/advanced/options_executor.py +0 -50
  145. exonware/xwnode/queries/executors/advanced/pipe_executor.py +0 -50
  146. exonware/xwnode/queries/executors/advanced/subscribe_executor.py +0 -50
  147. exonware/xwnode/queries/executors/advanced/subscription_executor.py +0 -50
  148. exonware/xwnode/queries/executors/advanced/union_executor.py +0 -50
  149. exonware/xwnode/queries/executors/advanced/window_executor.py +0 -51
  150. exonware/xwnode/queries/executors/advanced/with_cte_executor.py +0 -50
  151. exonware/xwnode/queries/executors/aggregation/__init__.py +0 -21
  152. exonware/xwnode/queries/executors/aggregation/avg_executor.py +0 -50
  153. exonware/xwnode/queries/executors/aggregation/count_executor.py +0 -38
  154. exonware/xwnode/queries/executors/aggregation/distinct_executor.py +0 -50
  155. exonware/xwnode/queries/executors/aggregation/group_executor.py +0 -50
  156. exonware/xwnode/queries/executors/aggregation/having_executor.py +0 -50
  157. exonware/xwnode/queries/executors/aggregation/max_executor.py +0 -50
  158. exonware/xwnode/queries/executors/aggregation/min_executor.py +0 -50
  159. exonware/xwnode/queries/executors/aggregation/sum_executor.py +0 -50
  160. exonware/xwnode/queries/executors/aggregation/summarize_executor.py +0 -50
  161. exonware/xwnode/queries/executors/array/__init__.py +0 -9
  162. exonware/xwnode/queries/executors/array/indexing_executor.py +0 -51
  163. exonware/xwnode/queries/executors/array/slicing_executor.py +0 -51
  164. exonware/xwnode/queries/executors/base.py +0 -257
  165. exonware/xwnode/queries/executors/capability_checker.py +0 -204
  166. exonware/xwnode/queries/executors/contracts.py +0 -166
  167. exonware/xwnode/queries/executors/core/__init__.py +0 -17
  168. exonware/xwnode/queries/executors/core/create_executor.py +0 -96
  169. exonware/xwnode/queries/executors/core/delete_executor.py +0 -99
  170. exonware/xwnode/queries/executors/core/drop_executor.py +0 -100
  171. exonware/xwnode/queries/executors/core/insert_executor.py +0 -39
  172. exonware/xwnode/queries/executors/core/select_executor.py +0 -152
  173. exonware/xwnode/queries/executors/core/update_executor.py +0 -102
  174. exonware/xwnode/queries/executors/data/__init__.py +0 -13
  175. exonware/xwnode/queries/executors/data/alter_executor.py +0 -50
  176. exonware/xwnode/queries/executors/data/load_executor.py +0 -50
  177. exonware/xwnode/queries/executors/data/merge_executor.py +0 -50
  178. exonware/xwnode/queries/executors/data/store_executor.py +0 -50
  179. exonware/xwnode/queries/executors/defs.py +0 -93
  180. exonware/xwnode/queries/executors/engine.py +0 -221
  181. exonware/xwnode/queries/executors/errors.py +0 -68
  182. exonware/xwnode/queries/executors/filtering/__init__.py +0 -25
  183. exonware/xwnode/queries/executors/filtering/between_executor.py +0 -80
  184. exonware/xwnode/queries/executors/filtering/filter_executor.py +0 -79
  185. exonware/xwnode/queries/executors/filtering/has_executor.py +0 -70
  186. exonware/xwnode/queries/executors/filtering/in_executor.py +0 -70
  187. exonware/xwnode/queries/executors/filtering/like_executor.py +0 -76
  188. exonware/xwnode/queries/executors/filtering/optional_executor.py +0 -76
  189. exonware/xwnode/queries/executors/filtering/range_executor.py +0 -80
  190. exonware/xwnode/queries/executors/filtering/term_executor.py +0 -77
  191. exonware/xwnode/queries/executors/filtering/values_executor.py +0 -71
  192. exonware/xwnode/queries/executors/filtering/where_executor.py +0 -44
  193. exonware/xwnode/queries/executors/graph/__init__.py +0 -15
  194. exonware/xwnode/queries/executors/graph/in_traverse_executor.py +0 -51
  195. exonware/xwnode/queries/executors/graph/match_executor.py +0 -51
  196. exonware/xwnode/queries/executors/graph/out_executor.py +0 -51
  197. exonware/xwnode/queries/executors/graph/path_executor.py +0 -51
  198. exonware/xwnode/queries/executors/graph/return_executor.py +0 -51
  199. exonware/xwnode/queries/executors/ordering/__init__.py +0 -9
  200. exonware/xwnode/queries/executors/ordering/by_executor.py +0 -50
  201. exonware/xwnode/queries/executors/ordering/order_executor.py +0 -51
  202. exonware/xwnode/queries/executors/projection/__init__.py +0 -9
  203. exonware/xwnode/queries/executors/projection/extend_executor.py +0 -50
  204. exonware/xwnode/queries/executors/projection/project_executor.py +0 -50
  205. exonware/xwnode/queries/executors/registry.py +0 -173
  206. exonware/xwnode/queries/parsers/__init__.py +0 -26
  207. exonware/xwnode/queries/parsers/base.py +0 -86
  208. exonware/xwnode/queries/parsers/contracts.py +0 -46
  209. exonware/xwnode/queries/parsers/errors.py +0 -53
  210. exonware/xwnode/queries/parsers/sql_param_extractor.py +0 -318
  211. exonware/xwnode/queries/strategies/__init__.py +0 -24
  212. exonware/xwnode/queries/strategies/base.py +0 -236
  213. exonware/xwnode/queries/strategies/cql.py +0 -201
  214. exonware/xwnode/queries/strategies/cypher.py +0 -181
  215. exonware/xwnode/queries/strategies/datalog.py +0 -70
  216. exonware/xwnode/queries/strategies/elastic_dsl.py +0 -70
  217. exonware/xwnode/queries/strategies/eql.py +0 -70
  218. exonware/xwnode/queries/strategies/flux.py +0 -70
  219. exonware/xwnode/queries/strategies/gql.py +0 -70
  220. exonware/xwnode/queries/strategies/graphql.py +0 -240
  221. exonware/xwnode/queries/strategies/gremlin.py +0 -181
  222. exonware/xwnode/queries/strategies/hiveql.py +0 -214
  223. exonware/xwnode/queries/strategies/hql.py +0 -70
  224. exonware/xwnode/queries/strategies/jmespath.py +0 -219
  225. exonware/xwnode/queries/strategies/jq.py +0 -66
  226. exonware/xwnode/queries/strategies/json_query.py +0 -66
  227. exonware/xwnode/queries/strategies/jsoniq.py +0 -248
  228. exonware/xwnode/queries/strategies/kql.py +0 -70
  229. exonware/xwnode/queries/strategies/linq.py +0 -238
  230. exonware/xwnode/queries/strategies/logql.py +0 -70
  231. exonware/xwnode/queries/strategies/mql.py +0 -68
  232. exonware/xwnode/queries/strategies/n1ql.py +0 -210
  233. exonware/xwnode/queries/strategies/partiql.py +0 -70
  234. exonware/xwnode/queries/strategies/pig.py +0 -215
  235. exonware/xwnode/queries/strategies/promql.py +0 -70
  236. exonware/xwnode/queries/strategies/sparql.py +0 -220
  237. exonware/xwnode/queries/strategies/sql.py +0 -275
  238. exonware/xwnode/queries/strategies/xml_query.py +0 -66
  239. exonware/xwnode/queries/strategies/xpath.py +0 -223
  240. exonware/xwnode/queries/strategies/xquery.py +0 -258
  241. exonware/xwnode/queries/strategies/xwnode_executor.py +0 -332
  242. exonware/xwnode/queries/strategies/xwquery.py +0 -456
  243. exonware_xwnode-0.0.1.21.dist-info/RECORD +0 -214
  244. /exonware/xwnode/nodes/strategies/{node_ordered_map.py → ordered_map.py} +0 -0
  245. /exonware/xwnode/nodes/strategies/{node_ordered_map_balanced.py → ordered_map_balanced.py} +0 -0
  246. /exonware/xwnode/nodes/strategies/{node_patricia.py → patricia.py} +0 -0
  247. /exonware/xwnode/nodes/strategies/{node_radix_trie.py → radix_trie.py} +0 -0
  248. /exonware/xwnode/nodes/strategies/{node_set_tree.py → set_tree.py} +0 -0
  249. {exonware_xwnode-0.0.1.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/WHEEL +0 -0
  250. {exonware_xwnode-0.0.1.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/licenses/LICENSE +0 -0
@@ -9,15 +9,56 @@ This package contains all edge strategy implementations organized by type:
9
9
  Company: eXonware.com
10
10
  Author: Eng. Muhammad AlShehri
11
11
  Email: connect@exonware.com
12
- Version: 0.0.1.21
12
+ Version: 0.0.1.23
13
13
  Generation Date: January 2, 2025
14
14
  """
15
15
 
16
16
  from .base import AEdgeStrategy, ALinearEdgeStrategy, ATreeEdgeStrategy, AGraphEdgeStrategy
17
17
 
18
- # Graph edge strategies
18
+ # Graph edge strategies - Batch 1 (Core structures)
19
19
  from .adj_list import AdjListStrategy
20
20
  from .adj_matrix import AdjMatrixStrategy
21
+ from .csr import CSRStrategy
22
+ from .dynamic_adj_list import DynamicAdjListStrategy
23
+ from .weighted_graph import WeightedGraphStrategy
24
+
25
+ # Batch 2: Sparse matrix formats
26
+ from .csc import CSCStrategy
27
+ from .coo import COOStrategy
28
+ from .block_adj_matrix import BlockAdjMatrixStrategy
29
+
30
+ # Batch 3: Spatial structures
31
+ from .rtree import RTreeStrategy
32
+ from .quadtree import QuadTreeStrategy
33
+ from .octree import OctreeStrategy
34
+
35
+ # Batch 4: Graph representations
36
+ from .incidence_matrix import IncidenceMatrixStrategy
37
+ from .edge_list import EdgeListStrategy
38
+ from .compressed_graph import CompressedGraphStrategy
39
+ from .bidir_wrapper import BidirWrapperStrategy
40
+
41
+ # Batch 5: Specialized structures
42
+ from .temporal_edgeset import TemporalEdgeSetStrategy
43
+ from .hyperedge_set import HyperEdgeSetStrategy
44
+ from .edge_property_store import EdgePropertyStoreStrategy
45
+ from .flow_network import FlowNetworkStrategy
46
+ from .neural_graph import NeuralGraphStrategy
47
+
48
+ # Batch 6: Basic & hybrid
49
+ from .tree_graph_basic import TreeGraphBasicStrategy
50
+
51
+ # Batch 7: Advanced graph structures
52
+ from .k2_tree import K2TreeStrategy
53
+ from .bv_graph import BVGraphStrategy
54
+ from .hnsw import HNSWStrategy
55
+ from .euler_tour import EulerTourStrategy
56
+ from .link_cut import LinkCutStrategy
57
+ from .hop2_labels import Hop2LabelsStrategy
58
+ from .graphblas import GraphBLASStrategy
59
+ from .roaring_adj import RoaringAdjStrategy
60
+ from .multiplex import MultiplexStrategy
61
+ from .bitemporal import BitemporalStrategy
21
62
 
22
63
  __all__ = [
23
64
  # Base classes
@@ -26,7 +67,48 @@ __all__ = [
26
67
  'ATreeEdgeStrategy',
27
68
  'AGraphEdgeStrategy',
28
69
 
29
- # Graph edge strategies
70
+ # Batch 1: Core graph structures
30
71
  'AdjListStrategy',
31
- 'AdjMatrixStrategy'
72
+ 'AdjMatrixStrategy',
73
+ 'CSRStrategy',
74
+ 'DynamicAdjListStrategy',
75
+ 'WeightedGraphStrategy',
76
+
77
+ # Batch 2: Sparse matrix formats
78
+ 'CSCStrategy',
79
+ 'COOStrategy',
80
+ 'BlockAdjMatrixStrategy',
81
+
82
+ # Batch 3: Spatial structures
83
+ 'RTreeStrategy',
84
+ 'QuadTreeStrategy',
85
+ 'OctreeStrategy',
86
+
87
+ # Batch 4: Graph representations
88
+ 'IncidenceMatrixStrategy',
89
+ 'EdgeListStrategy',
90
+ 'CompressedGraphStrategy',
91
+ 'BidirWrapperStrategy',
92
+
93
+ # Batch 5: Specialized structures
94
+ 'TemporalEdgeSetStrategy',
95
+ 'HyperEdgeSetStrategy',
96
+ 'EdgePropertyStoreStrategy',
97
+ 'FlowNetworkStrategy',
98
+ 'NeuralGraphStrategy',
99
+
100
+ # Batch 6: Basic & hybrid
101
+ 'TreeGraphBasicStrategy',
102
+
103
+ # Batch 7: Advanced graph structures
104
+ 'K2TreeStrategy',
105
+ 'BVGraphStrategy',
106
+ 'HNSWStrategy',
107
+ 'EulerTourStrategy',
108
+ 'LinkCutStrategy',
109
+ 'Hop2LabelsStrategy',
110
+ 'GraphBLASStrategy',
111
+ 'RoaringAdjStrategy',
112
+ 'MultiplexStrategy',
113
+ 'BitemporalStrategy',
32
114
  ]
@@ -10,9 +10,9 @@ from typing import Any, Dict, List, Optional, Iterator, Union, Set
10
10
  from ...defs import EdgeMode, EdgeTrait
11
11
 
12
12
 
13
- class aEdgeStrategy(ABC):
13
+ class AEdgeStrategy(ABC):
14
14
  """
15
- Abstract base class for all edge strategies.
15
+ Abstract base class for all edge strategies (DEV_GUIDELINES.md compliant - uppercase 'A').
16
16
 
17
17
  This abstract base class defines the contract that all edge strategy
18
18
  implementations must follow, ensuring consistency and interoperability.
@@ -7,23 +7,61 @@ with efficient edge addition and neighbor queries.
7
7
 
8
8
  from typing import Any, Iterator, Dict, List, Set, Optional, Tuple
9
9
  from collections import defaultdict
10
- from .base import AGraphEdgeStrategy
10
+ from ._base_edge import AEdgeStrategy
11
11
  from ...defs import EdgeMode, EdgeTrait
12
12
 
13
13
 
14
- class AdjListStrategy(AGraphEdgeStrategy):
14
+ class AdjListStrategy(AEdgeStrategy):
15
15
  """
16
16
  Adjacency List edge strategy for sparse graph representation.
17
17
 
18
- Provides O(1) edge addition and O(degree) neighbor queries,
19
- ideal for sparse graphs where most vertices have few connections.
18
+ WHY this strategy:
19
+ - Memory efficient for sparse graphs (5-20x less than adjacency matrix)
20
+ - Fast edge addition O(1) without overhead
21
+ - Natural fit for real-world networks (social graphs, web, citations)
22
+ - Optimal when average degree << total vertices
23
+
24
+ WHY this implementation:
25
+ - defaultdict for automatic vertex creation
26
+ - Separate outgoing/incoming lists for directed graph efficiency
27
+ - List storage for sequential neighbor iteration
28
+ - Simple structure minimizes memory overhead
29
+
30
+ Time Complexity:
31
+ - Add Edge: O(1) amortized
32
+ - Has Edge: O(degree) - linear scan of neighbors
33
+ - Get Neighbors: O(degree) - direct list access
34
+ - Delete Edge: O(degree) - linear scan to find and remove
35
+
36
+ Space Complexity: O(V + E) where V = vertices, E = edges
37
+
38
+ Trade-offs:
39
+ - Advantage: Minimal memory for sparse graphs, fast edge addition
40
+ - Limitation: Slower edge existence checks than matrix O(1)
41
+ - Compared to ADJ_MATRIX: Use when graph density < 10%
42
+
43
+ Best for:
44
+ - Social networks (avg degree 100-1000, millions of users)
45
+ - Web graphs (sparse link structure)
46
+ - Citation networks (few references per paper)
47
+ - Any graph where |E| ≈ |V| (not |V|²)
48
+
49
+ Not recommended for:
50
+ - Dense graphs (>50% density) - use ADJ_MATRIX instead
51
+ - Frequent edge existence checks - use ADJ_MATRIX
52
+ - Matrix operations - use CSR/CSC for linear algebra
53
+
54
+ Following eXonware Priorities:
55
+ 1. Security: Input validation prevents injection attacks
56
+ 2. Usability: Simple dict-based API, intuitive for developers
57
+ 3. Maintainability: Clean defaultdict pattern, minimal code
58
+ 4. Performance: O(1) addition, O(degree) queries optimal for sparse
59
+ 5. Extensibility: Easy to add multi-edge support, weights, properties
20
60
  """
21
61
 
22
62
  def __init__(self, traits: EdgeTrait = EdgeTrait.NONE, **options):
23
63
  """Initialize the Adjacency List strategy."""
24
- super().__init__(**options)
25
- self._mode = EdgeMode.ADJ_LIST
26
- self._traits = traits
64
+ super().__init__(EdgeMode.ADJ_LIST, traits, **options)
27
65
 
28
66
  self.is_directed = options.get('directed', True)
29
67
  self.allow_self_loops = options.get('self_loops', True)
@@ -48,154 +86,274 @@ class AdjListStrategy(AGraphEdgeStrategy):
48
86
  # CORE EDGE OPERATIONS
49
87
  # ============================================================================
50
88
 
51
- def add_edge(self, from_node: Any, to_node: Any, **kwargs) -> None:
89
+ def add_edge(self, source: str, target: str, **properties) -> str:
52
90
  """Add an edge between source and target vertices."""
53
- source = str(from_node)
54
- target = str(to_node)
55
-
56
- # Validation
57
- if not self.allow_self_loops and source == target:
58
- raise ValueError("Self loops not allowed")
91
+ # Validate self-loops
92
+ if source == target and not self.allow_self_loops:
93
+ raise ValueError(f"Self-loops not allowed: {source} -> {target}")
59
94
 
95
+ # Check for existing edge if multi-edges not allowed
60
96
  if not self.allow_multi_edges and self.has_edge(source, target):
61
- raise ValueError("Multi edges not allowed")
97
+ raise ValueError(f"Multi-edges not allowed: {source} -> {target}")
62
98
 
63
- # Add vertices to set
64
- self._vertices.add(source)
65
- self._vertices.add(target)
99
+ # Generate edge ID
100
+ edge_id = f"edge_{self._edge_id_counter}"
101
+ self._edge_id_counter += 1
66
102
 
67
103
  # Create edge data
68
104
  edge_data = {
69
- 'weight': kwargs.get('weight', 1.0),
70
- 'id': f"edge_{self._edge_id_counter}",
71
- **kwargs
105
+ 'id': edge_id,
106
+ 'source': source,
107
+ 'target': target,
108
+ 'properties': properties.copy()
72
109
  }
73
- self._edge_id_counter += 1
74
110
 
75
- # Add outgoing edge
111
+ # Add vertices to vertex set
112
+ self._vertices.add(source)
113
+ self._vertices.add(target)
114
+
115
+ # Add to outgoing adjacency list
76
116
  self._outgoing[source].append((target, edge_data))
77
117
 
78
- # Add incoming edge if directed
118
+ # Add to incoming adjacency list (if directed)
79
119
  if self.is_directed and self._incoming is not None:
80
120
  self._incoming[target].append((source, edge_data))
81
121
  elif not self.is_directed:
82
- # For undirected, add reverse edge
83
- self._outgoing[target].append((source, edge_data))
122
+ # For undirected graphs, add reverse edge (unless it's a self-loop)
123
+ if source != target:
124
+ self._outgoing[target].append((source, edge_data))
84
125
 
85
126
  self._edge_count += 1
127
+ return edge_id
86
128
 
87
- def remove_edge(self, from_node: Any, to_node: Any) -> bool:
88
- """Remove edge between vertices."""
89
- source = str(from_node)
90
- target = str(to_node)
129
+ def remove_edge(self, source: str, target: str, edge_id: Optional[str] = None) -> bool:
130
+ """Remove edge(s) between source and target."""
131
+ if source not in self._outgoing:
132
+ return False
91
133
 
92
134
  removed = False
93
135
 
94
- # Remove from outgoing
95
- for i, (neighbor, _) in enumerate(self._outgoing[source]):
96
- if neighbor == target:
97
- self._outgoing[source].pop(i)
98
- removed = True
99
- break
136
+ # Remove from outgoing list
137
+ original_length = len(self._outgoing[source])
138
+ if edge_id:
139
+ # Remove specific edge by ID
140
+ self._outgoing[source] = [
141
+ (neighbor, data) for neighbor, data in self._outgoing[source]
142
+ if not (neighbor == target and data['id'] == edge_id)
143
+ ]
144
+ else:
145
+ # Remove all edges to target
146
+ self._outgoing[source] = [
147
+ (neighbor, data) for neighbor, data in self._outgoing[source]
148
+ if neighbor != target
149
+ ]
100
150
 
101
- # Remove from incoming if directed
102
- if self.is_directed and self._incoming is not None:
103
- for i, (neighbor, _) in enumerate(self._incoming[target]):
104
- if neighbor == source:
105
- self._incoming[target].pop(i)
106
- break
107
- elif not self.is_directed:
108
- # For undirected, remove reverse edge
109
- for i, (neighbor, _) in enumerate(self._outgoing[target]):
110
- if neighbor == source:
111
- self._outgoing[target].pop(i)
112
- break
151
+ removed = len(self._outgoing[source]) < original_length
113
152
 
114
153
  if removed:
115
- self._edge_count -= 1
154
+ self._edge_count -= (original_length - len(self._outgoing[source]))
155
+
156
+ # Remove from incoming list (if directed)
157
+ if self.is_directed and self._incoming is not None and target in self._incoming:
158
+ if edge_id:
159
+ self._incoming[target] = [
160
+ (neighbor, data) for neighbor, data in self._incoming[target]
161
+ if not (neighbor == source and data['id'] == edge_id)
162
+ ]
163
+ else:
164
+ self._incoming[target] = [
165
+ (neighbor, data) for neighbor, data in self._incoming[target]
166
+ if neighbor != source
167
+ ]
168
+ elif not self.is_directed and source != target:
169
+ # For undirected graphs, remove reverse edge
170
+ if target in self._outgoing:
171
+ if edge_id:
172
+ self._outgoing[target] = [
173
+ (neighbor, data) for neighbor, data in self._outgoing[target]
174
+ if not (neighbor == source and data['id'] == edge_id)
175
+ ]
176
+ else:
177
+ self._outgoing[target] = [
178
+ (neighbor, data) for neighbor, data in self._outgoing[target]
179
+ if neighbor != source
180
+ ]
116
181
 
117
182
  return removed
118
183
 
119
- def has_edge(self, from_node: Any, to_node: Any) -> bool:
120
- """Check if edge exists."""
121
- source = str(from_node)
122
- target = str(to_node)
184
+ def has_edge(self, source: str, target: str) -> bool:
185
+ """Check if edge exists between source and target."""
186
+ if source not in self._outgoing:
187
+ return False
123
188
 
124
- for neighbor, _ in self._outgoing[source]:
189
+ return any(neighbor == target for neighbor, _ in self._outgoing[source])
190
+
191
+ def get_edge_data(self, source: str, target: str) -> Optional[Dict[str, Any]]:
192
+ """Get edge data between source and target."""
193
+ if source not in self._outgoing:
194
+ return None
195
+
196
+ for neighbor, data in self._outgoing[source]:
125
197
  if neighbor == target:
126
- return True
127
- return False
198
+ return data
199
+
200
+ return None
128
201
 
129
- def get_edge_count(self) -> int:
130
- """Get total number of edges."""
202
+ def neighbors(self, vertex: str, direction: str = 'out') -> Iterator[str]:
203
+ """Get neighbors of a vertex."""
204
+ if direction == 'out':
205
+ if vertex in self._outgoing:
206
+ for neighbor, _ in self._outgoing[vertex]:
207
+ yield neighbor
208
+ elif direction == 'in':
209
+ if self.is_directed and self._incoming is not None and vertex in self._incoming:
210
+ for neighbor, _ in self._incoming[vertex]:
211
+ yield neighbor
212
+ elif not self.is_directed:
213
+ # For undirected graphs, incoming = outgoing
214
+ if vertex in self._outgoing:
215
+ for neighbor, _ in self._outgoing[vertex]:
216
+ yield neighbor
217
+ elif direction == 'both':
218
+ # Get all neighbors (both in and out)
219
+ seen = set()
220
+ for neighbor in self.neighbors(vertex, 'out'):
221
+ if neighbor not in seen:
222
+ seen.add(neighbor)
223
+ yield neighbor
224
+ for neighbor in self.neighbors(vertex, 'in'):
225
+ if neighbor not in seen:
226
+ seen.add(neighbor)
227
+ yield neighbor
228
+
229
+ def degree(self, vertex: str, direction: str = 'out') -> int:
230
+ """Get degree of a vertex."""
231
+ if direction == 'out':
232
+ return len(self._outgoing.get(vertex, []))
233
+ elif direction == 'in':
234
+ if self.is_directed and self._incoming is not None:
235
+ return len(self._incoming.get(vertex, []))
236
+ elif not self.is_directed:
237
+ return len(self._outgoing.get(vertex, []))
238
+ else:
239
+ return 0
240
+ elif direction == 'both':
241
+ out_degree = self.degree(vertex, 'out')
242
+ in_degree = self.degree(vertex, 'in')
243
+ # For undirected graphs, avoid double counting
244
+ return out_degree if not self.is_directed else out_degree + in_degree
245
+
246
+ def edges(self, data: bool = False) -> Iterator[tuple]:
247
+ """Get all edges in the graph."""
248
+ seen_edges = set()
249
+
250
+ for source, adj_list in self._outgoing.items():
251
+ for target, edge_data in adj_list:
252
+ edge_key = (source, target, edge_data['id'])
253
+
254
+ if edge_key not in seen_edges:
255
+ seen_edges.add(edge_key)
256
+
257
+ if data:
258
+ yield (source, target, edge_data)
259
+ else:
260
+ yield (source, target)
261
+
262
+ def vertices(self) -> Iterator[str]:
263
+ """Get all vertices in the graph."""
264
+ return iter(self._vertices)
265
+
266
+ def __len__(self) -> int:
267
+ """Get the number of edges."""
131
268
  return self._edge_count
132
269
 
133
- def get_vertex_count(self) -> int:
134
- """Get total number of vertices."""
270
+ def vertex_count(self) -> int:
271
+ """Get the number of vertices."""
135
272
  return len(self._vertices)
136
273
 
137
- # ============================================================================
138
- # GRAPH EDGE STRATEGY METHODS
139
- # ============================================================================
274
+ def clear(self) -> None:
275
+ """Clear all edges and vertices."""
276
+ self._outgoing.clear()
277
+ if self._incoming is not None:
278
+ self._incoming.clear()
279
+ self._vertices.clear()
280
+ self._edge_count = 0
281
+ self._edge_id_counter = 0
140
282
 
141
- def get_neighbors(self, node: Any) -> List[Any]:
142
- """Get all neighboring nodes."""
143
- vertex = str(node)
144
- neighbors = []
145
- for neighbor, _ in self._outgoing[vertex]:
146
- neighbors.append(neighbor)
147
- return neighbors
283
+ def add_vertex(self, vertex: str) -> None:
284
+ """Add a vertex to the graph."""
285
+ self._vertices.add(vertex)
286
+ # Initialize adjacency lists if not present
287
+ if vertex not in self._outgoing:
288
+ self._outgoing[vertex] = []
289
+ if self.is_directed and self._incoming is not None and vertex not in self._incoming:
290
+ self._incoming[vertex] = []
148
291
 
149
- def get_edge_weight(self, from_node: Any, to_node: Any) -> float:
150
- """Get edge weight."""
151
- source = str(from_node)
152
- target = str(to_node)
292
+ def remove_vertex(self, vertex: str) -> bool:
293
+ """Remove a vertex and all its edges."""
294
+ if vertex not in self._vertices:
295
+ return False
153
296
 
154
- for neighbor, edge_data in self._outgoing[source]:
155
- if neighbor == target:
156
- return edge_data.get('weight', 1.0)
157
- return 0.0
297
+ # Remove all outgoing edges
298
+ edges_removed = len(self._outgoing.get(vertex, []))
299
+ self._edge_count -= edges_removed
300
+
301
+ # Remove all incoming edges
302
+ for source in list(self._outgoing.keys()):
303
+ if source != vertex:
304
+ original_length = len(self._outgoing[source])
305
+ self._outgoing[source] = [
306
+ (neighbor, data) for neighbor, data in self._outgoing[source]
307
+ if neighbor != vertex
308
+ ]
309
+ self._edge_count -= (original_length - len(self._outgoing[source]))
310
+
311
+ # Clean up adjacency lists
312
+ if vertex in self._outgoing:
313
+ del self._outgoing[vertex]
314
+ if self._incoming is not None and vertex in self._incoming:
315
+ del self._incoming[vertex]
316
+
317
+ # Remove from vertex set
318
+ self._vertices.remove(vertex)
319
+
320
+ return True
321
+
322
+ # ============================================================================
323
+ # ADVANCED OPERATIONS
324
+ # ============================================================================
158
325
 
159
- def set_edge_weight(self, from_node: Any, to_node: Any, weight: float) -> None:
160
- """Set edge weight."""
161
- source = str(from_node)
162
- target = str(to_node)
326
+ def get_subgraph(self, vertices: Set[str]) -> 'xAdjListStrategy':
327
+ """Extract subgraph containing only specified vertices."""
328
+ subgraph = xAdjListStrategy(
329
+ traits=self._traits,
330
+ directed=self.is_directed,
331
+ self_loops=self.allow_self_loops,
332
+ multi_edges=self.allow_multi_edges
333
+ )
163
334
 
164
- for neighbor, edge_data in self._outgoing[source]:
165
- if neighbor == target:
166
- edge_data['weight'] = weight
167
- return
168
- raise ValueError(f"Edge {source} -> {target} not found")
169
-
170
- def find_shortest_path(self, start: Any, end: Any) -> List[Any]:
171
- """Find shortest path between nodes."""
172
- # TODO: Implement BFS/Dijkstra
173
- return []
174
-
175
- def find_all_paths(self, start: Any, end: Any) -> List[List[Any]]:
176
- """Find all paths between nodes."""
177
- # TODO: Implement DFS
178
- return []
179
-
180
- def get_connected_components(self) -> List[List[Any]]:
181
- """Get all connected components."""
182
- # TODO: Implement connected components
183
- return []
184
-
185
- def is_connected(self, start: Any, end: Any) -> bool:
186
- """Check if two nodes are connected."""
187
- # TODO: Implement connectivity check
188
- return False
189
-
190
- def get_degree(self, node: Any) -> int:
191
- """Get degree of node."""
192
- vertex = str(node)
193
- return len(self._outgoing[vertex])
194
-
195
- def is_cyclic(self) -> bool:
196
- """Check if graph contains cycles."""
197
- # TODO: Implement cycle detection
198
- return False
335
+ # Add vertices
336
+ for vertex in vertices:
337
+ if vertex in self._vertices:
338
+ subgraph.add_vertex(vertex)
339
+
340
+ # Add edges
341
+ for source, target, edge_data in self.edges(data=True):
342
+ if source in vertices and target in vertices:
343
+ subgraph.add_edge(source, target, **edge_data['properties'])
344
+
345
+ return subgraph
346
+
347
+ def get_edge_list(self) -> List[Tuple[str, str, Dict[str, Any]]]:
348
+ """Get all edges as a list."""
349
+ return list(self.edges(data=True))
350
+
351
+ def get_adjacency_dict(self) -> Dict[str, List[str]]:
352
+ """Get adjacency representation as a dictionary."""
353
+ return {
354
+ vertex: [neighbor for neighbor, _ in adj_list]
355
+ for vertex, adj_list in self._outgoing.items()
356
+ }
199
357
 
200
358
  # ============================================================================
201
359
  # PERFORMANCE CHARACTERISTICS
@@ -206,12 +364,15 @@ class AdjListStrategy(AGraphEdgeStrategy):
206
364
  """Get backend implementation info."""
207
365
  return {
208
366
  'strategy': 'adjacency_list',
209
- 'backend': 'Dictionary of lists',
367
+ 'backend': 'Python defaultdict + lists',
368
+ 'directed': self.is_directed,
369
+ 'multi_edges': self.allow_multi_edges,
370
+ 'self_loops': self.allow_self_loops,
210
371
  'complexity': {
211
372
  'add_edge': 'O(1)',
212
373
  'remove_edge': 'O(degree)',
213
374
  'has_edge': 'O(degree)',
214
- 'get_neighbors': 'O(degree)',
375
+ 'neighbors': 'O(degree)',
215
376
  'space': 'O(V + E)'
216
377
  }
217
378
  }
@@ -219,9 +380,14 @@ class AdjListStrategy(AGraphEdgeStrategy):
219
380
  @property
220
381
  def metrics(self) -> Dict[str, Any]:
221
382
  """Get performance metrics."""
383
+ avg_degree = self._edge_count / max(1, len(self._vertices)) if self._vertices else 0
384
+ density = self._edge_count / max(1, len(self._vertices) * (len(self._vertices) - 1)) if len(self._vertices) > 1 else 0
385
+
222
386
  return {
223
387
  'vertices': len(self._vertices),
224
388
  'edges': self._edge_count,
225
- 'directed': self.is_directed,
226
- 'density': f"{(self._edge_count / max(1, len(self._vertices) * (len(self._vertices) - 1))) * 100:.1f}%"
389
+ 'average_degree': round(avg_degree, 2),
390
+ 'density': round(density, 4),
391
+ 'memory_usage': f"{len(self._vertices) * 48 + self._edge_count * 32} bytes (estimated)",
392
+ 'sparsity': f"{(1 - density) * 100:.1f}%"
227
393
  }