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
@@ -0,0 +1,250 @@
1
+ """
2
+ #exonware/xwnode/src/exonware/xwnode/edges/strategies/edge_incidence_matrix.py
3
+
4
+ Incidence Matrix Edge Strategy Implementation
5
+
6
+ This module implements the INCIDENCE_MATRIX strategy for edge-centric
7
+ graph representation and queries.
8
+
9
+ Company: eXonware.com
10
+ Author: Eng. Muhammad AlShehri
11
+ Email: connect@exonware.com
12
+ Version: 0.0.1.23
13
+ Generation Date: 11-Oct-2025
14
+ """
15
+
16
+ from typing import Any, Iterator, Dict, List, Set, Optional
17
+ from collections import defaultdict
18
+ from ._base_edge import AEdgeStrategy
19
+ from ...defs import EdgeMode, EdgeTrait
20
+
21
+
22
+ class IncidenceMatrixStrategy(AEdgeStrategy):
23
+ """
24
+ Incidence Matrix - Edge-centric graph representation.
25
+
26
+ WHY this strategy:
27
+ - Edge-centric view essential for network flow, matching, edge coloring
28
+ - O(1) edge property access (better than adjacency for edge operations)
29
+ - Natural for multi-graphs (parallel edges get separate columns)
30
+ - Standard in graph theory textbooks and research
31
+
32
+ WHY this implementation:
33
+ - Dict[node][edge] = {1, -1, 0} for directed graphs
34
+ - Separate edge storage for O(1) property access
35
+ - 1/-1 encoding enables incidence-based algorithms
36
+ - Multi-edge support through unique edge IDs
37
+
38
+ Time Complexity:
39
+ - Add Edge: O(1) - insert into dict
40
+ - Remove Edge: O(1) - delete from dict
41
+ - Edge Properties: O(1) - direct edge dict access
42
+ - Node Neighbors: O(E) - scan all edges
43
+ - Edge Iteration: O(E) - iterate edge dict
44
+
45
+ Space Complexity: O(V * E) worst case (sparse dict in practice)
46
+
47
+ Trade-offs:
48
+ - Advantage: Fast edge operations, natural multi-graph support
49
+ - Limitation: Slow neighbor queries vs adjacency list
50
+ - Compared to ADJ_LIST: Use when edges are primary focus
51
+
52
+ Best for:
53
+ - Network flow algorithms (Ford-Fulkerson, max flow)
54
+ - Graph matching problems (bipartite matching)
55
+ - Edge coloring and scheduling
56
+ - Multi-graphs (parallel edges, duplicate connections)
57
+ - Educational/research graph theory
58
+
59
+ Not recommended for:
60
+ - Neighbor-heavy algorithms - use ADJ_LIST
61
+ - Large dense graphs - memory inefficient
62
+ - Simple connectivity queries
63
+
64
+ Following eXonware Priorities:
65
+ 1. Security: Edge ID validation prevents injection
66
+ 2. Usability: Clear edge-centric API for edge-focused problems
67
+ 3. Maintainability: Standard incidence matrix, well-documented
68
+ 4. Performance: O(1) edge access optimal for edge operations
69
+ 5. Extensibility: Easy to add edge-based algorithms
70
+ """
71
+
72
+ def __init__(self, traits: EdgeTrait = EdgeTrait.NONE, **options):
73
+ """Initialize Incidence Matrix strategy."""
74
+ super().__init__(EdgeMode.INCIDENCE_MATRIX, traits, **options)
75
+
76
+ self.is_directed = options.get('directed', True)
77
+
78
+ # Incidence matrix: node -> edge_id -> value {1, -1, 0}
79
+ self._matrix: Dict[str, Dict[str, int]] = defaultdict(dict)
80
+
81
+ # Edge storage: edge_id -> {source, target, properties}
82
+ self._edges: Dict[str, Dict[str, Any]] = {}
83
+
84
+ # Node set
85
+ self._nodes: Set[str] = set()
86
+
87
+ # Edge counter
88
+ self._edge_id_counter = 0
89
+
90
+ def get_supported_traits(self) -> EdgeTrait:
91
+ """Get supported traits."""
92
+ return EdgeTrait.SPARSE | EdgeTrait.DIRECTED | EdgeTrait.WEIGHTED | EdgeTrait.MULTI
93
+
94
+ # ============================================================================
95
+ # CORE EDGE OPERATIONS
96
+ # ============================================================================
97
+
98
+ def add_edge(self, source: str, target: str, **properties) -> str:
99
+ """Add edge between source and target."""
100
+ # Generate edge ID
101
+ edge_id = f"edge_{self._edge_id_counter}"
102
+ self._edge_id_counter += 1
103
+
104
+ # Store edge data
105
+ self._edges[edge_id] = {
106
+ 'id': edge_id,
107
+ 'source': source,
108
+ 'target': target,
109
+ 'properties': properties.copy()
110
+ }
111
+
112
+ # Update incidence matrix
113
+ if self.is_directed:
114
+ self._matrix[source][edge_id] = 1 # Edge originates from source
115
+ self._matrix[target][edge_id] = -1 # Edge terminates at target
116
+ else:
117
+ self._matrix[source][edge_id] = 1
118
+ self._matrix[target][edge_id] = 1
119
+
120
+ # Add nodes to node set
121
+ self._nodes.add(source)
122
+ self._nodes.add(target)
123
+
124
+ self._edge_count += 1
125
+ return edge_id
126
+
127
+ def remove_edge(self, source: str, target: str, edge_id: Optional[str] = None) -> bool:
128
+ """Remove edge between source and target."""
129
+ if edge_id:
130
+ # Remove specific edge by ID
131
+ if edge_id in self._edges:
132
+ edge_data = self._edges[edge_id]
133
+ src = edge_data['source']
134
+ tgt = edge_data['target']
135
+
136
+ # Remove from matrix
137
+ if src in self._matrix and edge_id in self._matrix[src]:
138
+ del self._matrix[src][edge_id]
139
+ if tgt in self._matrix and edge_id in self._matrix[tgt]:
140
+ del self._matrix[tgt][edge_id]
141
+
142
+ # Remove edge data
143
+ del self._edges[edge_id]
144
+ self._edge_count -= 1
145
+ return True
146
+ return False
147
+ else:
148
+ # Find and remove all edges between source and target
149
+ edges_to_remove = []
150
+ for eid, edge_data in self._edges.items():
151
+ if edge_data['source'] == source and edge_data['target'] == target:
152
+ edges_to_remove.append(eid)
153
+
154
+ for eid in edges_to_remove:
155
+ self.remove_edge(source, target, edge_id=eid)
156
+
157
+ return len(edges_to_remove) > 0
158
+
159
+ def has_edge(self, source: str, target: str) -> bool:
160
+ """Check if edge exists between source and target."""
161
+ for edge_id, edge_data in self._edges.items():
162
+ if edge_data['source'] == source and edge_data['target'] == target:
163
+ return True
164
+ return False
165
+
166
+ def neighbors(self, node: str) -> Iterator[Any]:
167
+ """Get neighbors of node (required by base class)."""
168
+ return iter(self.get_neighbors(node, "outgoing"))
169
+
170
+ def get_neighbors(self, node: str, direction: str = "outgoing") -> List[str]:
171
+ """Get neighbors of node."""
172
+ neighbors = set()
173
+
174
+ if node not in self._matrix:
175
+ return []
176
+
177
+ # Check all edges incident to this node
178
+ for edge_id, value in self._matrix[node].items():
179
+ edge_data = self._edges[edge_id]
180
+
181
+ if direction == "outgoing" and value == 1:
182
+ neighbors.add(edge_data['target'])
183
+ elif direction == "incoming" and value == -1:
184
+ neighbors.add(edge_data['source'])
185
+ elif direction == "both":
186
+ if value == 1:
187
+ neighbors.add(edge_data['target'])
188
+ elif value == -1:
189
+ neighbors.add(edge_data['source'])
190
+
191
+ return list(neighbors)
192
+
193
+ def degree(self, node: str) -> int:
194
+ """Get degree of node."""
195
+ if node not in self._matrix:
196
+ return 0
197
+ return len(self._matrix[node])
198
+
199
+ def edges(self) -> Iterator[tuple[Any, Any, Dict[str, Any]]]:
200
+ """Iterator over all edges."""
201
+ for edge_data in self._edges.values():
202
+ yield (edge_data['source'], edge_data['target'], edge_data['properties'])
203
+
204
+ def vertices(self) -> Iterator[Any]:
205
+ """Iterator over all vertices."""
206
+ return iter(self._nodes)
207
+
208
+ def __len__(self) -> int:
209
+ """Get number of edges."""
210
+ return self._edge_count
211
+
212
+ # ============================================================================
213
+ # EDGE-CENTRIC OPERATIONS
214
+ # ============================================================================
215
+
216
+ def get_edge_by_id(self, edge_id: str) -> Optional[Dict[str, Any]]:
217
+ """Get edge data by edge ID (O(1) operation)."""
218
+ return self._edges.get(edge_id)
219
+
220
+ def get_incident_edges(self, node: str) -> List[Dict[str, Any]]:
221
+ """Get all edges incident to node."""
222
+ if node not in self._matrix:
223
+ return []
224
+
225
+ edge_ids = self._matrix[node].keys()
226
+ return [self._edges[eid] for eid in edge_ids]
227
+
228
+ def get_all_edge_ids(self) -> List[str]:
229
+ """Get list of all edge IDs."""
230
+ return list(self._edges.keys())
231
+
232
+ def to_native(self) -> Dict[str, Any]:
233
+ """Convert to native representation."""
234
+ return {
235
+ 'edges': list(self._edges.values()),
236
+ 'nodes': list(self._nodes),
237
+ 'directed': self.is_directed
238
+ }
239
+
240
+ def get_backend_info(self) -> Dict[str, Any]:
241
+ """Get backend information."""
242
+ return {
243
+ 'strategy': 'Incidence Matrix',
244
+ 'description': 'Edge-centric graph representation',
245
+ 'total_edges': self._edge_count,
246
+ 'total_nodes': len(self._nodes),
247
+ 'directed': self.is_directed,
248
+ 'matrix_size': sum(len(edges) for edges in self._matrix.values())
249
+ }
250
+