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.
- exonware/__init__.py +8 -1
- exonware/xwnode/__init__.py +18 -5
- exonware/xwnode/add_strategy_types.py +165 -0
- exonware/xwnode/base.py +7 -5
- exonware/xwnode/common/__init__.py +1 -1
- exonware/xwnode/common/graph/__init__.py +30 -0
- exonware/xwnode/common/graph/caching.py +131 -0
- exonware/xwnode/common/graph/contracts.py +100 -0
- exonware/xwnode/common/graph/errors.py +44 -0
- exonware/xwnode/common/graph/indexing.py +260 -0
- exonware/xwnode/common/graph/manager.py +568 -0
- exonware/xwnode/common/management/__init__.py +3 -5
- exonware/xwnode/common/management/manager.py +9 -9
- exonware/xwnode/common/management/migration.py +6 -6
- exonware/xwnode/common/monitoring/__init__.py +3 -5
- exonware/xwnode/common/monitoring/metrics.py +7 -3
- exonware/xwnode/common/monitoring/pattern_detector.py +2 -2
- exonware/xwnode/common/monitoring/performance_monitor.py +6 -2
- exonware/xwnode/common/patterns/__init__.py +3 -5
- exonware/xwnode/common/patterns/advisor.py +1 -1
- exonware/xwnode/common/patterns/flyweight.py +6 -2
- exonware/xwnode/common/patterns/registry.py +203 -184
- exonware/xwnode/common/utils/__init__.py +25 -11
- exonware/xwnode/common/utils/simple.py +1 -1
- exonware/xwnode/config.py +3 -8
- exonware/xwnode/contracts.py +4 -105
- exonware/xwnode/defs.py +413 -159
- exonware/xwnode/edges/strategies/__init__.py +86 -4
- exonware/xwnode/edges/strategies/_base_edge.py +2 -2
- exonware/xwnode/edges/strategies/adj_list.py +287 -121
- exonware/xwnode/edges/strategies/adj_matrix.py +316 -222
- exonware/xwnode/edges/strategies/base.py +1 -1
- exonware/xwnode/edges/strategies/{edge_bidir_wrapper.py → bidir_wrapper.py} +45 -4
- exonware/xwnode/edges/strategies/bitemporal.py +520 -0
- exonware/xwnode/edges/strategies/{edge_block_adj_matrix.py → block_adj_matrix.py} +77 -6
- exonware/xwnode/edges/strategies/bv_graph.py +664 -0
- exonware/xwnode/edges/strategies/compressed_graph.py +217 -0
- exonware/xwnode/edges/strategies/{edge_coo.py → coo.py} +46 -4
- exonware/xwnode/edges/strategies/{edge_csc.py → csc.py} +45 -4
- exonware/xwnode/edges/strategies/{edge_csr.py → csr.py} +94 -12
- exonware/xwnode/edges/strategies/{edge_dynamic_adj_list.py → dynamic_adj_list.py} +46 -4
- exonware/xwnode/edges/strategies/edge_list.py +168 -0
- exonware/xwnode/edges/strategies/edge_property_store.py +2 -2
- exonware/xwnode/edges/strategies/euler_tour.py +560 -0
- exonware/xwnode/edges/strategies/{edge_flow_network.py → flow_network.py} +2 -2
- exonware/xwnode/edges/strategies/graphblas.py +449 -0
- exonware/xwnode/edges/strategies/hnsw.py +637 -0
- exonware/xwnode/edges/strategies/hop2_labels.py +467 -0
- exonware/xwnode/edges/strategies/{edge_hyperedge_set.py → hyperedge_set.py} +2 -2
- exonware/xwnode/edges/strategies/incidence_matrix.py +250 -0
- exonware/xwnode/edges/strategies/k2_tree.py +613 -0
- exonware/xwnode/edges/strategies/link_cut.py +626 -0
- exonware/xwnode/edges/strategies/multiplex.py +532 -0
- exonware/xwnode/edges/strategies/{edge_neural_graph.py → neural_graph.py} +2 -2
- exonware/xwnode/edges/strategies/{edge_octree.py → octree.py} +69 -11
- exonware/xwnode/edges/strategies/{edge_quadtree.py → quadtree.py} +66 -10
- exonware/xwnode/edges/strategies/roaring_adj.py +438 -0
- exonware/xwnode/edges/strategies/{edge_rtree.py → rtree.py} +43 -5
- exonware/xwnode/edges/strategies/{edge_temporal_edgeset.py → temporal_edgeset.py} +24 -5
- exonware/xwnode/edges/strategies/{edge_tree_graph_basic.py → tree_graph_basic.py} +78 -7
- exonware/xwnode/edges/strategies/{edge_weighted_graph.py → weighted_graph.py} +188 -10
- exonware/xwnode/errors.py +3 -6
- exonware/xwnode/facade.py +20 -20
- exonware/xwnode/nodes/strategies/__init__.py +29 -9
- exonware/xwnode/nodes/strategies/adjacency_list.py +650 -177
- exonware/xwnode/nodes/strategies/aho_corasick.py +358 -183
- exonware/xwnode/nodes/strategies/array_list.py +36 -3
- exonware/xwnode/nodes/strategies/art.py +581 -0
- exonware/xwnode/nodes/strategies/{node_avl_tree.py → avl_tree.py} +77 -6
- exonware/xwnode/nodes/strategies/{node_b_plus_tree.py → b_plus_tree.py} +81 -40
- exonware/xwnode/nodes/strategies/{node_btree.py → b_tree.py} +79 -9
- exonware/xwnode/nodes/strategies/base.py +469 -98
- exonware/xwnode/nodes/strategies/{node_bitmap.py → bitmap.py} +12 -12
- exonware/xwnode/nodes/strategies/{node_bitset_dynamic.py → bitset_dynamic.py} +11 -11
- exonware/xwnode/nodes/strategies/{node_bloom_filter.py → bloom_filter.py} +15 -2
- exonware/xwnode/nodes/strategies/bloomier_filter.py +519 -0
- exonware/xwnode/nodes/strategies/bw_tree.py +531 -0
- exonware/xwnode/nodes/strategies/contracts.py +1 -1
- exonware/xwnode/nodes/strategies/{node_count_min_sketch.py → count_min_sketch.py} +3 -2
- exonware/xwnode/nodes/strategies/{node_cow_tree.py → cow_tree.py} +135 -13
- exonware/xwnode/nodes/strategies/crdt_map.py +629 -0
- exonware/xwnode/nodes/strategies/{node_cuckoo_hash.py → cuckoo_hash.py} +2 -2
- exonware/xwnode/nodes/strategies/{node_xdata_optimized.py → data_interchange_optimized.py} +21 -4
- exonware/xwnode/nodes/strategies/dawg.py +876 -0
- exonware/xwnode/nodes/strategies/deque.py +321 -153
- exonware/xwnode/nodes/strategies/extendible_hash.py +93 -0
- exonware/xwnode/nodes/strategies/{node_fenwick_tree.py → fenwick_tree.py} +111 -19
- exonware/xwnode/nodes/strategies/hamt.py +403 -0
- exonware/xwnode/nodes/strategies/hash_map.py +354 -67
- exonware/xwnode/nodes/strategies/heap.py +105 -5
- exonware/xwnode/nodes/strategies/hopscotch_hash.py +525 -0
- exonware/xwnode/nodes/strategies/{node_hyperloglog.py → hyperloglog.py} +6 -5
- exonware/xwnode/nodes/strategies/interval_tree.py +742 -0
- exonware/xwnode/nodes/strategies/kd_tree.py +703 -0
- exonware/xwnode/nodes/strategies/learned_index.py +533 -0
- exonware/xwnode/nodes/strategies/linear_hash.py +93 -0
- exonware/xwnode/nodes/strategies/linked_list.py +316 -119
- exonware/xwnode/nodes/strategies/{node_lsm_tree.py → lsm_tree.py} +219 -15
- exonware/xwnode/nodes/strategies/masstree.py +130 -0
- exonware/xwnode/nodes/strategies/{node_persistent_tree.py → persistent_tree.py} +149 -9
- exonware/xwnode/nodes/strategies/priority_queue.py +544 -132
- exonware/xwnode/nodes/strategies/queue.py +249 -120
- exonware/xwnode/nodes/strategies/{node_red_black_tree.py → red_black_tree.py} +183 -72
- exonware/xwnode/nodes/strategies/{node_roaring_bitmap.py → roaring_bitmap.py} +19 -6
- exonware/xwnode/nodes/strategies/rope.py +717 -0
- exonware/xwnode/nodes/strategies/{node_segment_tree.py → segment_tree.py} +106 -106
- exonware/xwnode/nodes/strategies/{node_set_hash.py → set_hash.py} +30 -29
- exonware/xwnode/nodes/strategies/{node_skip_list.py → skip_list.py} +74 -6
- exonware/xwnode/nodes/strategies/sparse_matrix.py +427 -131
- exonware/xwnode/nodes/strategies/{node_splay_tree.py → splay_tree.py} +55 -6
- exonware/xwnode/nodes/strategies/stack.py +244 -112
- exonware/xwnode/nodes/strategies/{node_suffix_array.py → suffix_array.py} +5 -1
- exonware/xwnode/nodes/strategies/t_tree.py +94 -0
- exonware/xwnode/nodes/strategies/{node_treap.py → treap.py} +75 -6
- exonware/xwnode/nodes/strategies/{node_tree_graph_hybrid.py → tree_graph_hybrid.py} +46 -5
- exonware/xwnode/nodes/strategies/trie.py +153 -9
- exonware/xwnode/nodes/strategies/union_find.py +111 -5
- exonware/xwnode/nodes/strategies/veb_tree.py +856 -0
- exonware/xwnode/strategies/__init__.py +5 -51
- exonware/xwnode/version.py +3 -3
- {exonware_xwnode-0.0.1.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/METADATA +23 -3
- exonware_xwnode-0.0.1.23.dist-info/RECORD +130 -0
- exonware/xwnode/edges/strategies/edge_adj_list.py +0 -353
- exonware/xwnode/edges/strategies/edge_adj_matrix.py +0 -445
- exonware/xwnode/nodes/strategies/_base_node.py +0 -307
- exonware/xwnode/nodes/strategies/node_aho_corasick.py +0 -525
- exonware/xwnode/nodes/strategies/node_array_list.py +0 -179
- exonware/xwnode/nodes/strategies/node_hash_map.py +0 -273
- exonware/xwnode/nodes/strategies/node_heap.py +0 -196
- exonware/xwnode/nodes/strategies/node_linked_list.py +0 -413
- exonware/xwnode/nodes/strategies/node_trie.py +0 -257
- exonware/xwnode/nodes/strategies/node_union_find.py +0 -192
- exonware/xwnode/queries/executors/__init__.py +0 -47
- exonware/xwnode/queries/executors/advanced/__init__.py +0 -37
- exonware/xwnode/queries/executors/advanced/aggregate_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/ask_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/construct_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/describe_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/for_loop_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/foreach_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/join_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/let_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/mutation_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/options_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/pipe_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/subscribe_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/subscription_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/union_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/window_executor.py +0 -51
- exonware/xwnode/queries/executors/advanced/with_cte_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/__init__.py +0 -21
- exonware/xwnode/queries/executors/aggregation/avg_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/count_executor.py +0 -38
- exonware/xwnode/queries/executors/aggregation/distinct_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/group_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/having_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/max_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/min_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/sum_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/summarize_executor.py +0 -50
- exonware/xwnode/queries/executors/array/__init__.py +0 -9
- exonware/xwnode/queries/executors/array/indexing_executor.py +0 -51
- exonware/xwnode/queries/executors/array/slicing_executor.py +0 -51
- exonware/xwnode/queries/executors/base.py +0 -257
- exonware/xwnode/queries/executors/capability_checker.py +0 -204
- exonware/xwnode/queries/executors/contracts.py +0 -166
- exonware/xwnode/queries/executors/core/__init__.py +0 -17
- exonware/xwnode/queries/executors/core/create_executor.py +0 -96
- exonware/xwnode/queries/executors/core/delete_executor.py +0 -99
- exonware/xwnode/queries/executors/core/drop_executor.py +0 -100
- exonware/xwnode/queries/executors/core/insert_executor.py +0 -39
- exonware/xwnode/queries/executors/core/select_executor.py +0 -152
- exonware/xwnode/queries/executors/core/update_executor.py +0 -102
- exonware/xwnode/queries/executors/data/__init__.py +0 -13
- exonware/xwnode/queries/executors/data/alter_executor.py +0 -50
- exonware/xwnode/queries/executors/data/load_executor.py +0 -50
- exonware/xwnode/queries/executors/data/merge_executor.py +0 -50
- exonware/xwnode/queries/executors/data/store_executor.py +0 -50
- exonware/xwnode/queries/executors/defs.py +0 -93
- exonware/xwnode/queries/executors/engine.py +0 -221
- exonware/xwnode/queries/executors/errors.py +0 -68
- exonware/xwnode/queries/executors/filtering/__init__.py +0 -25
- exonware/xwnode/queries/executors/filtering/between_executor.py +0 -80
- exonware/xwnode/queries/executors/filtering/filter_executor.py +0 -79
- exonware/xwnode/queries/executors/filtering/has_executor.py +0 -70
- exonware/xwnode/queries/executors/filtering/in_executor.py +0 -70
- exonware/xwnode/queries/executors/filtering/like_executor.py +0 -76
- exonware/xwnode/queries/executors/filtering/optional_executor.py +0 -76
- exonware/xwnode/queries/executors/filtering/range_executor.py +0 -80
- exonware/xwnode/queries/executors/filtering/term_executor.py +0 -77
- exonware/xwnode/queries/executors/filtering/values_executor.py +0 -71
- exonware/xwnode/queries/executors/filtering/where_executor.py +0 -44
- exonware/xwnode/queries/executors/graph/__init__.py +0 -15
- exonware/xwnode/queries/executors/graph/in_traverse_executor.py +0 -51
- exonware/xwnode/queries/executors/graph/match_executor.py +0 -51
- exonware/xwnode/queries/executors/graph/out_executor.py +0 -51
- exonware/xwnode/queries/executors/graph/path_executor.py +0 -51
- exonware/xwnode/queries/executors/graph/return_executor.py +0 -51
- exonware/xwnode/queries/executors/ordering/__init__.py +0 -9
- exonware/xwnode/queries/executors/ordering/by_executor.py +0 -50
- exonware/xwnode/queries/executors/ordering/order_executor.py +0 -51
- exonware/xwnode/queries/executors/projection/__init__.py +0 -9
- exonware/xwnode/queries/executors/projection/extend_executor.py +0 -50
- exonware/xwnode/queries/executors/projection/project_executor.py +0 -50
- exonware/xwnode/queries/executors/registry.py +0 -173
- exonware/xwnode/queries/parsers/__init__.py +0 -26
- exonware/xwnode/queries/parsers/base.py +0 -86
- exonware/xwnode/queries/parsers/contracts.py +0 -46
- exonware/xwnode/queries/parsers/errors.py +0 -53
- exonware/xwnode/queries/parsers/sql_param_extractor.py +0 -318
- exonware/xwnode/queries/strategies/__init__.py +0 -24
- exonware/xwnode/queries/strategies/base.py +0 -236
- exonware/xwnode/queries/strategies/cql.py +0 -201
- exonware/xwnode/queries/strategies/cypher.py +0 -181
- exonware/xwnode/queries/strategies/datalog.py +0 -70
- exonware/xwnode/queries/strategies/elastic_dsl.py +0 -70
- exonware/xwnode/queries/strategies/eql.py +0 -70
- exonware/xwnode/queries/strategies/flux.py +0 -70
- exonware/xwnode/queries/strategies/gql.py +0 -70
- exonware/xwnode/queries/strategies/graphql.py +0 -240
- exonware/xwnode/queries/strategies/gremlin.py +0 -181
- exonware/xwnode/queries/strategies/hiveql.py +0 -214
- exonware/xwnode/queries/strategies/hql.py +0 -70
- exonware/xwnode/queries/strategies/jmespath.py +0 -219
- exonware/xwnode/queries/strategies/jq.py +0 -66
- exonware/xwnode/queries/strategies/json_query.py +0 -66
- exonware/xwnode/queries/strategies/jsoniq.py +0 -248
- exonware/xwnode/queries/strategies/kql.py +0 -70
- exonware/xwnode/queries/strategies/linq.py +0 -238
- exonware/xwnode/queries/strategies/logql.py +0 -70
- exonware/xwnode/queries/strategies/mql.py +0 -68
- exonware/xwnode/queries/strategies/n1ql.py +0 -210
- exonware/xwnode/queries/strategies/partiql.py +0 -70
- exonware/xwnode/queries/strategies/pig.py +0 -215
- exonware/xwnode/queries/strategies/promql.py +0 -70
- exonware/xwnode/queries/strategies/sparql.py +0 -220
- exonware/xwnode/queries/strategies/sql.py +0 -275
- exonware/xwnode/queries/strategies/xml_query.py +0 -66
- exonware/xwnode/queries/strategies/xpath.py +0 -223
- exonware/xwnode/queries/strategies/xquery.py +0 -258
- exonware/xwnode/queries/strategies/xwnode_executor.py +0 -332
- exonware/xwnode/queries/strategies/xwquery.py +0 -456
- exonware_xwnode-0.0.1.21.dist-info/RECORD +0 -214
- /exonware/xwnode/nodes/strategies/{node_ordered_map.py → ordered_map.py} +0 -0
- /exonware/xwnode/nodes/strategies/{node_ordered_map_balanced.py → ordered_map_balanced.py} +0 -0
- /exonware/xwnode/nodes/strategies/{node_patricia.py → patricia.py} +0 -0
- /exonware/xwnode/nodes/strategies/{node_radix_trie.py → radix_trie.py} +0 -0
- /exonware/xwnode/nodes/strategies/{node_set_tree.py → set_tree.py} +0 -0
- {exonware_xwnode-0.0.1.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/WHEEL +0 -0
- {exonware_xwnode-0.0.1.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/licenses/LICENSE +0 -0
@@ -7,11 +7,11 @@ in tree+graph hybrid structures, providing minimal graph capabilities.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from typing import Any, Dict, List, Optional, Set, Tuple, Iterator
|
10
|
-
from ._base_edge import
|
10
|
+
from ._base_edge import AEdgeStrategy
|
11
11
|
from ...defs import EdgeMode, EdgeTrait
|
12
12
|
|
13
13
|
|
14
|
-
class
|
14
|
+
class TreeGraphBasicStrategy(AEdgeStrategy):
|
15
15
|
"""
|
16
16
|
Basic edge strategy for tree+graph hybrid structures.
|
17
17
|
|
@@ -34,8 +34,13 @@ class xTreeGraphBasicStrategy(aEdgeStrategy):
|
|
34
34
|
self._max_degree = 0
|
35
35
|
|
36
36
|
def get_supported_traits(self) -> EdgeTrait:
|
37
|
-
"""
|
38
|
-
|
37
|
+
"""
|
38
|
+
Get the traits supported by the tree-graph basic strategy.
|
39
|
+
|
40
|
+
Root cause fixed: Missing HIERARCHICAL trait for tree structure support.
|
41
|
+
Priority: Maintainability #3 - Correct trait reporting
|
42
|
+
"""
|
43
|
+
return (EdgeTrait.DIRECTED | EdgeTrait.WEIGHTED | EdgeTrait.MULTI | EdgeTrait.HIERARCHICAL)
|
39
44
|
|
40
45
|
def _update_degree_stats(self, node: str) -> None:
|
41
46
|
"""Update degree statistics."""
|
@@ -89,12 +94,23 @@ class xTreeGraphBasicStrategy(aEdgeStrategy):
|
|
89
94
|
# ============================================================================
|
90
95
|
|
91
96
|
def add_edge(self, source: str, target: str, weight: float = 1.0,
|
92
|
-
metadata: Optional[Dict[str, Any]] = None) ->
|
93
|
-
"""
|
97
|
+
metadata: Optional[Dict[str, Any]] = None, **properties) -> str:
|
98
|
+
"""
|
99
|
+
Add an edge between source and target nodes.
|
100
|
+
|
101
|
+
Root cause fixed: Method returned bool instead of edge_id string, violating
|
102
|
+
strategy interface contract.
|
103
|
+
|
104
|
+
Priority: Maintainability #3 - Consistent strategy interface
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
Edge ID string
|
108
|
+
"""
|
94
109
|
if not isinstance(source, str) or not isinstance(target, str):
|
95
110
|
raise ValueError("Source and target must be strings")
|
96
111
|
|
97
|
-
|
112
|
+
self._add_edge_internal(source, target, weight, metadata)
|
113
|
+
return f"edge_{source}_{target}"
|
98
114
|
|
99
115
|
def remove_edge(self, source: str, target: str) -> bool:
|
100
116
|
"""Remove an edge between source and target nodes."""
|
@@ -131,6 +147,18 @@ class xTreeGraphBasicStrategy(aEdgeStrategy):
|
|
131
147
|
|
132
148
|
return list(self._edges.get(node, set()))
|
133
149
|
|
150
|
+
def get_children(self, node: str) -> List[str]:
|
151
|
+
"""
|
152
|
+
Get children of a node in tree structure.
|
153
|
+
|
154
|
+
Root cause fixed: Missing method for tree navigation API.
|
155
|
+
Priority: Usability #2 - Complete tree navigation interface
|
156
|
+
|
157
|
+
Returns:
|
158
|
+
List of child node identifiers
|
159
|
+
"""
|
160
|
+
return self.get_outgoing(node)
|
161
|
+
|
134
162
|
def get_degree(self, node: str) -> int:
|
135
163
|
"""Get the degree (number of neighbors) of a node."""
|
136
164
|
if not isinstance(node, str):
|
@@ -195,6 +223,49 @@ class xTreeGraphBasicStrategy(aEdgeStrategy):
|
|
195
223
|
"""Iterate over all edges."""
|
196
224
|
yield from self.edges()
|
197
225
|
|
226
|
+
def __len__(self) -> int:
|
227
|
+
"""
|
228
|
+
Get number of edges.
|
229
|
+
|
230
|
+
Root cause fixed: Missing abstract method implementation.
|
231
|
+
Priority: Maintainability #3 - Complete interface implementation
|
232
|
+
"""
|
233
|
+
return self._edge_count
|
234
|
+
|
235
|
+
def degree(self, vertex: str) -> int:
|
236
|
+
"""
|
237
|
+
Get degree of a vertex.
|
238
|
+
|
239
|
+
Root cause fixed: Missing abstract method implementation.
|
240
|
+
Priority: Maintainability #3 - Complete interface implementation
|
241
|
+
"""
|
242
|
+
return self.get_degree(vertex)
|
243
|
+
|
244
|
+
def neighbors(self, vertex: str, direction: str = 'out') -> Iterator[str]:
|
245
|
+
"""
|
246
|
+
Get neighbors of a vertex.
|
247
|
+
|
248
|
+
Root cause fixed: Missing abstract method implementation.
|
249
|
+
Priority: Maintainability #3 - Complete interface implementation
|
250
|
+
"""
|
251
|
+
if direction == 'out':
|
252
|
+
yield from self.get_outgoing(vertex)
|
253
|
+
elif direction == 'in':
|
254
|
+
yield from self.get_incoming(vertex)
|
255
|
+
else:
|
256
|
+
# Both directions
|
257
|
+
yield from self.get_outgoing(vertex)
|
258
|
+
yield from self.get_incoming(vertex)
|
259
|
+
|
260
|
+
def vertices(self) -> Iterator[str]:
|
261
|
+
"""
|
262
|
+
Iterate over all vertices.
|
263
|
+
|
264
|
+
Root cause fixed: Missing abstract method implementation.
|
265
|
+
Priority: Maintainability #3 - Complete interface implementation
|
266
|
+
"""
|
267
|
+
yield from self.get_nodes()
|
268
|
+
|
198
269
|
# ============================================================================
|
199
270
|
# TREE-GRAPH BASIC SPECIFIC OPERATIONS
|
200
271
|
# ============================================================================
|
@@ -7,7 +7,7 @@ edge weights, optimized for network algorithms and shortest path computations.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from typing import Any, Dict, List, Optional, Set, Tuple, Iterator
|
10
|
-
from ._base_edge import
|
10
|
+
from ._base_edge import AEdgeStrategy
|
11
11
|
from ...defs import EdgeMode, EdgeTrait
|
12
12
|
from ...errors import XWNodeUnsupportedCapabilityError
|
13
13
|
import threading
|
@@ -42,12 +42,54 @@ class WeightedEdge:
|
|
42
42
|
return f"WeightedEdge({self.source} -> {self.target}, weight={self.weight})"
|
43
43
|
|
44
44
|
|
45
|
-
class
|
45
|
+
class WeightedGraphStrategy(AEdgeStrategy):
|
46
46
|
"""
|
47
47
|
Weighted graph edge strategy for graphs with numerical edge weights.
|
48
48
|
|
49
|
-
|
50
|
-
|
49
|
+
WHY this strategy:
|
50
|
+
- Real-world networks have weighted edges (distances, costs, capacities, probabilities)
|
51
|
+
- Enables classic algorithms: Dijkstra, Bellman-Ford, Kruskal, Prim
|
52
|
+
- Optimized for network optimization problems
|
53
|
+
- First-class weight support (not retrofitted properties)
|
54
|
+
|
55
|
+
WHY this implementation:
|
56
|
+
- Adjacency list backbone for sparse weight storage
|
57
|
+
- WeightedEdge class encapsulates edge with weight
|
58
|
+
- Hash caching for performance in set operations
|
59
|
+
- Thread-safe operations with optional locking
|
60
|
+
|
61
|
+
Time Complexity:
|
62
|
+
- Add Edge: O(1) - append to adjacency list
|
63
|
+
- Has Edge: O(degree) - linear scan of neighbors
|
64
|
+
- Get Weight: O(degree) - find edge then access weight
|
65
|
+
- Shortest Path: O((V+E) log V) - Dijkstra with heap
|
66
|
+
- Delete Edge: O(degree) - find and remove from list
|
67
|
+
|
68
|
+
Space Complexity: O(V + E) - sparse storage
|
69
|
+
|
70
|
+
Trade-offs:
|
71
|
+
- Advantage: Natural weight handling, algorithm-ready
|
72
|
+
- Limitation: Slower than plain adjacency list (weight objects)
|
73
|
+
- Compared to ADJ_LIST: Use when weights drive decisions
|
74
|
+
|
75
|
+
Best for:
|
76
|
+
- Transportation networks (roads, flights with distances)
|
77
|
+
- Network flow problems (capacity constraints)
|
78
|
+
- Routing and pathfinding (GPS, logistics)
|
79
|
+
- Cost optimization (supply chain, telecommunications)
|
80
|
+
- Recommendation systems (similarity scores)
|
81
|
+
|
82
|
+
Not recommended for:
|
83
|
+
- Unweighted graphs - use plain ADJ_LIST
|
84
|
+
- Simple connectivity checks - overhead unnecessary
|
85
|
+
- Extremely large graphs - use CSR/CSC for better compression
|
86
|
+
|
87
|
+
Following eXonware Priorities:
|
88
|
+
1. Security: Weight validation prevents invalid values
|
89
|
+
2. Usability: Natural API for weighted operations
|
90
|
+
3. Maintainability: Clean WeightedEdge abstraction
|
91
|
+
4. Performance: Optimized Dijkstra implementation
|
92
|
+
5. Extensibility: Easy to add new weighted algorithms
|
51
93
|
"""
|
52
94
|
|
53
95
|
def __init__(self, traits: EdgeTrait = EdgeTrait.NONE, **options):
|
@@ -113,18 +155,29 @@ class xWeightedGraphStrategy(aEdgeStrategy):
|
|
113
155
|
# CORE OPERATIONS
|
114
156
|
# ============================================================================
|
115
157
|
|
116
|
-
def add_edge(self, source: str, target: str, weight: float = None, data: Any = None) ->
|
117
|
-
"""
|
158
|
+
def add_edge(self, source: str, target: str, weight: float = None, data: Any = None, **properties) -> str:
|
159
|
+
"""
|
160
|
+
Add a weighted edge between source and target.
|
161
|
+
|
162
|
+
Root cause fixed: Method returned bool instead of edge_id string, violating
|
163
|
+
strategy interface contract.
|
164
|
+
|
165
|
+
Priority: Maintainability #3 - Consistent strategy interface
|
166
|
+
|
167
|
+
Returns:
|
168
|
+
Edge ID string
|
169
|
+
"""
|
118
170
|
if not isinstance(source, str) or not isinstance(target, str):
|
119
|
-
|
171
|
+
raise ValueError("Source and target must be strings")
|
120
172
|
|
121
173
|
if weight is None:
|
122
|
-
weight = self.default_weight
|
174
|
+
weight = properties.get('weight', self.default_weight)
|
123
175
|
|
124
176
|
weight = self._normalize_weight(weight)
|
125
177
|
|
126
178
|
with self._lock:
|
127
179
|
edge_key = (source, target)
|
180
|
+
edge_id = f"edge_{source}_{target}"
|
128
181
|
|
129
182
|
# Check if edge already exists
|
130
183
|
if edge_key in self._edges:
|
@@ -134,7 +187,7 @@ class xWeightedGraphStrategy(aEdgeStrategy):
|
|
134
187
|
old_edge.data = data
|
135
188
|
self._total_weight_updates += 1
|
136
189
|
self._update_weight_stats(weight)
|
137
|
-
return
|
190
|
+
return edge_id
|
138
191
|
|
139
192
|
# Create new edge
|
140
193
|
edge = WeightedEdge(source, target, weight, data)
|
@@ -152,7 +205,7 @@ class xWeightedGraphStrategy(aEdgeStrategy):
|
|
152
205
|
self._edge_count += 1
|
153
206
|
self._total_edges_added += 1
|
154
207
|
self._update_weight_stats(weight)
|
155
|
-
return
|
208
|
+
return edge_id
|
156
209
|
|
157
210
|
def get_edge(self, source: str, target: str) -> Optional[WeightedEdge]:
|
158
211
|
"""Get edge between source and target."""
|
@@ -168,6 +221,27 @@ class xWeightedGraphStrategy(aEdgeStrategy):
|
|
168
221
|
edge = self.get_edge(source, target)
|
169
222
|
return edge.weight if edge else None
|
170
223
|
|
224
|
+
def get_edge_data(self, source: str, target: str) -> Optional[Dict[str, Any]]:
|
225
|
+
"""
|
226
|
+
Get edge data between source and target vertices.
|
227
|
+
|
228
|
+
Root cause fixed: Missing method caused test failures.
|
229
|
+
Returns dict with weight and properties for interface compliance.
|
230
|
+
|
231
|
+
Priority: Usability #2 - Complete API implementation
|
232
|
+
|
233
|
+
Returns:
|
234
|
+
Dict with 'weight' and other edge properties, or None if edge doesn't exist
|
235
|
+
"""
|
236
|
+
edge = self.get_edge(source, target)
|
237
|
+
if edge is None:
|
238
|
+
return None
|
239
|
+
|
240
|
+
return {
|
241
|
+
'weight': edge.weight,
|
242
|
+
'data': edge.data
|
243
|
+
}
|
244
|
+
|
171
245
|
def set_edge_weight(self, source: str, target: str, weight: float) -> bool:
|
172
246
|
"""Set weight of edge between source and target."""
|
173
247
|
if not isinstance(source, str) or not isinstance(target, str):
|
@@ -409,3 +483,107 @@ class xWeightedGraphStrategy(aEdgeStrategy):
|
|
409
483
|
'backend': 'Weighted graph with numerical edge weights',
|
410
484
|
'traits': [trait.name for trait in EdgeTrait if self.has_trait(trait)]
|
411
485
|
}
|
486
|
+
|
487
|
+
# ============================================================================
|
488
|
+
# REQUIRED INTERFACE METHODS
|
489
|
+
# ============================================================================
|
490
|
+
|
491
|
+
def __len__(self) -> int:
|
492
|
+
"""Get number of edges."""
|
493
|
+
return self._edge_count
|
494
|
+
|
495
|
+
def vertices(self) -> Set[str]:
|
496
|
+
"""Get all vertices."""
|
497
|
+
vertices = set()
|
498
|
+
for source in self._adjacency.keys():
|
499
|
+
vertices.add(source)
|
500
|
+
for target in self._reverse_adjacency.keys():
|
501
|
+
vertices.add(target)
|
502
|
+
return vertices
|
503
|
+
|
504
|
+
def edges(self) -> Iterator[Tuple[str, str, float]]:
|
505
|
+
"""Iterate over all edges with weights."""
|
506
|
+
for edge in self._edges.values():
|
507
|
+
yield (edge.source, edge.target, edge.weight)
|
508
|
+
|
509
|
+
def neighbors(self, node: str) -> List[str]:
|
510
|
+
"""Get neighbors of a node (delegates to get_neighbors)."""
|
511
|
+
return self.get_neighbors(node)
|
512
|
+
|
513
|
+
def get_degree(self, node: str) -> int:
|
514
|
+
"""
|
515
|
+
Get degree of a node (number of edges).
|
516
|
+
|
517
|
+
Root cause fixed: Method was calling itself recursively. Implemented proper logic.
|
518
|
+
Priority: Maintainability #3 - Correct method implementation
|
519
|
+
"""
|
520
|
+
if not isinstance(node, str):
|
521
|
+
return 0
|
522
|
+
|
523
|
+
with self._lock:
|
524
|
+
if self.directed:
|
525
|
+
out_degree = len(self._adjacency.get(node, {}))
|
526
|
+
in_degree = len(self._reverse_adjacency.get(node, {}))
|
527
|
+
return out_degree + in_degree
|
528
|
+
else:
|
529
|
+
return len(self._adjacency.get(node, {}))
|
530
|
+
|
531
|
+
def degree(self, node: str) -> int:
|
532
|
+
"""Get degree of a node."""
|
533
|
+
return self.get_degree(node)
|
534
|
+
|
535
|
+
def shortest_path(self, source: str, target: str) -> Optional[List[str]]:
|
536
|
+
"""
|
537
|
+
Find shortest path between source and target using Dijkstra's algorithm.
|
538
|
+
|
539
|
+
Root cause fixed: Missing method for weighted graph algorithms.
|
540
|
+
Also fixed: Don't require target to be in adjacency (it might only have incoming edges).
|
541
|
+
Priority: Performance #4 - Efficient shortest path computation
|
542
|
+
|
543
|
+
Returns:
|
544
|
+
List of vertices in shortest path, or None if no path exists
|
545
|
+
"""
|
546
|
+
if source not in self._adjacency:
|
547
|
+
return None
|
548
|
+
|
549
|
+
if source == target:
|
550
|
+
return [source]
|
551
|
+
|
552
|
+
import heapq
|
553
|
+
|
554
|
+
with self._lock:
|
555
|
+
# Dijkstra's algorithm
|
556
|
+
distances = {source: 0.0}
|
557
|
+
previous = {}
|
558
|
+
pq = [(0.0, source)]
|
559
|
+
visited = set()
|
560
|
+
|
561
|
+
while pq:
|
562
|
+
current_dist, current = heapq.heappop(pq)
|
563
|
+
|
564
|
+
if current in visited:
|
565
|
+
continue
|
566
|
+
visited.add(current)
|
567
|
+
|
568
|
+
if current == target:
|
569
|
+
# Reconstruct path
|
570
|
+
path = []
|
571
|
+
while current is not None:
|
572
|
+
path.append(current)
|
573
|
+
current = previous.get(current)
|
574
|
+
return list(reversed(path))
|
575
|
+
|
576
|
+
if current in self._adjacency:
|
577
|
+
for neighbor, weight in self._adjacency[current].items():
|
578
|
+
if neighbor not in visited:
|
579
|
+
new_dist = current_dist + weight
|
580
|
+
if neighbor not in distances or new_dist < distances[neighbor]:
|
581
|
+
distances[neighbor] = new_dist
|
582
|
+
previous[neighbor] = current
|
583
|
+
heapq.heappush(pq, (new_dist, neighbor))
|
584
|
+
|
585
|
+
return None
|
586
|
+
|
587
|
+
def remove_edge(self, from_node: str, to_node: str) -> bool:
|
588
|
+
"""Remove an edge (delegates to delete_edge)."""
|
589
|
+
return self.delete_edge(from_node, to_node)
|
exonware/xwnode/errors.py
CHANGED
@@ -327,12 +327,9 @@ class XWNodePresetError(XWNodeStrategyError):
|
|
327
327
|
}
|
328
328
|
|
329
329
|
# Import here to avoid circular imports
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
suggestions = [f"Available presets: {', '.join(available)}"]
|
334
|
-
except ImportError:
|
335
|
-
suggestions = ["Check preset name spelling"]
|
330
|
+
from .defs import list_presets
|
331
|
+
available = list_presets()
|
332
|
+
suggestions = [f"Available presets: {', '.join(available)}"]
|
336
333
|
|
337
334
|
super().__init__(message,
|
338
335
|
error_code="PRESET_ERROR",
|
exonware/xwnode/facade.py
CHANGED
@@ -9,7 +9,7 @@ a clean, intuitive interface.
|
|
9
9
|
Company: eXonware.com
|
10
10
|
Author: Eng. Muhammad AlShehri
|
11
11
|
Email: connect@exonware.com
|
12
|
-
Version: 0.0.1.
|
12
|
+
Version: 0.0.1.23
|
13
13
|
Generation Date: 07-Sep-2025
|
14
14
|
"""
|
15
15
|
|
@@ -61,6 +61,25 @@ class XWNode(XWNodeBase):
|
|
61
61
|
from .common.utils.simple import SimpleNodeStrategy
|
62
62
|
self._strategy = SimpleNodeStrategy.create_from_data(self._data or {})
|
63
63
|
|
64
|
+
# ============================================================================
|
65
|
+
# FACTORY METHODS
|
66
|
+
# ============================================================================
|
67
|
+
|
68
|
+
@classmethod
|
69
|
+
def from_native(cls, data: Any, mode: str = 'AUTO', **options) -> 'XWNode':
|
70
|
+
"""
|
71
|
+
Create XWNode from native Python data.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
data: Native Python data (dict, list, etc.)
|
75
|
+
mode: Strategy mode to use
|
76
|
+
**options: Additional configuration options
|
77
|
+
|
78
|
+
Returns:
|
79
|
+
XWNode instance containing the data
|
80
|
+
"""
|
81
|
+
return cls(data=data, mode=mode, **options)
|
82
|
+
|
64
83
|
# ============================================================================
|
65
84
|
# CORE OPERATIONS
|
66
85
|
# ============================================================================
|
@@ -275,24 +294,6 @@ class XWNode(XWNodeBase):
|
|
275
294
|
return f"XWNode(mode='{self._mode}', size={self.size()})"
|
276
295
|
|
277
296
|
|
278
|
-
class XWQuery:
|
279
|
-
"""Query interface for XWNode."""
|
280
|
-
|
281
|
-
def __init__(self, node: XWNode):
|
282
|
-
"""Initialize query interface."""
|
283
|
-
self._node = node
|
284
|
-
|
285
|
-
def find(self, pattern: str) -> List[Any]:
|
286
|
-
"""Find items matching pattern."""
|
287
|
-
# TODO: Implement pattern matching
|
288
|
-
return []
|
289
|
-
|
290
|
-
def filter(self, predicate) -> List[Any]:
|
291
|
-
"""Filter items by predicate."""
|
292
|
-
# TODO: Implement filtering
|
293
|
-
return []
|
294
|
-
|
295
|
-
|
296
297
|
class XWFactory:
|
297
298
|
"""Factory for creating XWNode instances."""
|
298
299
|
|
@@ -444,7 +445,6 @@ def empty_node() -> XWNode:
|
|
444
445
|
__all__ = [
|
445
446
|
'XWNode',
|
446
447
|
'XWEdge',
|
447
|
-
'XWQuery',
|
448
448
|
'XWFactory',
|
449
449
|
'create_node',
|
450
450
|
'from_dict',
|
@@ -9,7 +9,7 @@ This package contains all node 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.
|
12
|
+
Version: 0.0.1.23
|
13
13
|
Generation Date: January 2, 2025
|
14
14
|
"""
|
15
15
|
|
@@ -20,13 +20,23 @@ from .array_list import ArrayListStrategy
|
|
20
20
|
from .linked_list import LinkedListStrategy
|
21
21
|
|
22
22
|
# Tree strategies
|
23
|
-
from .trie import
|
24
|
-
from .heap import
|
25
|
-
from .aho_corasick import
|
23
|
+
from .trie import TrieStrategy
|
24
|
+
from .heap import HeapStrategy
|
25
|
+
from .aho_corasick import AhoCorasickStrategy
|
26
26
|
|
27
27
|
# Graph strategies
|
28
28
|
from .hash_map import HashMapStrategy
|
29
|
-
from .union_find import
|
29
|
+
from .union_find import UnionFindStrategy
|
30
|
+
|
31
|
+
# Advanced specialized strategies
|
32
|
+
from .veb_tree import VebTreeStrategy
|
33
|
+
from .dawg import DawgStrategy
|
34
|
+
from .hopscotch_hash import HopscotchHashStrategy
|
35
|
+
from .interval_tree import IntervalTreeStrategy
|
36
|
+
from .kd_tree import KdTreeStrategy
|
37
|
+
from .rope import RopeStrategy
|
38
|
+
from .crdt_map import CRDTMapStrategy
|
39
|
+
from .bloomier_filter import BloomierFilterStrategy
|
30
40
|
|
31
41
|
__all__ = [
|
32
42
|
# Base classes
|
@@ -40,11 +50,21 @@ __all__ = [
|
|
40
50
|
'LinkedListStrategy',
|
41
51
|
|
42
52
|
# Tree strategies
|
43
|
-
'
|
44
|
-
'
|
45
|
-
'
|
53
|
+
'TrieStrategy',
|
54
|
+
'HeapStrategy',
|
55
|
+
'AhoCorasickStrategy',
|
46
56
|
|
47
57
|
# Graph strategies
|
48
58
|
'HashMapStrategy',
|
49
|
-
'
|
59
|
+
'UnionFindStrategy',
|
60
|
+
|
61
|
+
# Advanced specialized strategies
|
62
|
+
'VebTreeStrategy',
|
63
|
+
'DawgStrategy',
|
64
|
+
'HopscotchHashStrategy',
|
65
|
+
'IntervalTreeStrategy',
|
66
|
+
'KdTreeStrategy',
|
67
|
+
'RopeStrategy',
|
68
|
+
'CRDTMapStrategy',
|
69
|
+
'BloomierFilterStrategy',
|
50
70
|
]
|