fromscratch-pygraphdb 0.4.0__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.
- fromscratch_pygraphdb-0.4.0.dist-info/METADATA +145 -0
- fromscratch_pygraphdb-0.4.0.dist-info/RECORD +17 -0
- fromscratch_pygraphdb-0.4.0.dist-info/WHEEL +4 -0
- fromscratch_pygraphdb-0.4.0.dist-info/licenses/LICENSE +21 -0
- pygraphdb/__init__.py +72 -0
- pygraphdb/_version.py +24 -0
- pygraphdb/atomic.py +66 -0
- pygraphdb/edge.py +177 -0
- pygraphdb/errors.py +63 -0
- pygraphdb/graph.py +509 -0
- pygraphdb/matching.py +111 -0
- pygraphdb/node.py +149 -0
- pygraphdb/pathfinding.py +147 -0
- pygraphdb/persistence.py +129 -0
- pygraphdb/py.typed +0 -0
- pygraphdb/query.py +241 -0
- pygraphdb/traversal.py +105 -0
pygraphdb/traversal.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Graph traversal algorithms -- BFS and DFS.
|
|
2
|
+
|
|
3
|
+
Imagine you're exploring a maze. There are two main strategies:
|
|
4
|
+
|
|
5
|
+
**BFS (Breadth-First Search)** -- Like ripples in a pond. You explore
|
|
6
|
+
all rooms one step away first, then all rooms two steps away, and so on.
|
|
7
|
+
This guarantees you find the shortest path (fewest steps) first.
|
|
8
|
+
|
|
9
|
+
**DFS (Depth-First Search)** -- Like following one tunnel as deep as
|
|
10
|
+
you can go before backtracking. You pick a direction and keep going
|
|
11
|
+
until you hit a dead end, then you go back and try another path.
|
|
12
|
+
|
|
13
|
+
Both algorithms visit every reachable node exactly once, but in
|
|
14
|
+
different orders. BFS uses a queue (first-in, first-out), while
|
|
15
|
+
DFS uses a stack (last-in, first-out).
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
>>> from pygraphdb.graph import Graph
|
|
19
|
+
>>> from pygraphdb.traversal import bfs, dfs
|
|
20
|
+
>>> g = Graph()
|
|
21
|
+
>>> g.add_node("a")
|
|
22
|
+
>>> g.add_node("b")
|
|
23
|
+
>>> g.add_node("c")
|
|
24
|
+
>>> g.add_edge("e1", "a", "b")
|
|
25
|
+
>>> g.add_edge("e2", "b", "c")
|
|
26
|
+
>>> [n.node_id for n in bfs(g, "a")]
|
|
27
|
+
['a', 'b', 'c']
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from collections import deque
|
|
32
|
+
|
|
33
|
+
from pygraphdb.graph import Graph
|
|
34
|
+
from pygraphdb.node import Node
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def bfs(graph: Graph, start_id: str) -> list[Node]:
|
|
38
|
+
"""Perform a breadth-first search starting from a given node.
|
|
39
|
+
|
|
40
|
+
Like ripples spreading outward from a stone dropped in water:
|
|
41
|
+
visit all neighbors first, then neighbors-of-neighbors, etc.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
graph: The graph to traverse.
|
|
45
|
+
start_id: The ID of the starting node.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
A list of nodes in BFS visit order.
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
NodeNotFoundError: If the start node does not exist.
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
graph.get_node(start_id) # Validate existence
|
|
55
|
+
visited: set[str] = set()
|
|
56
|
+
result: list[Node] = []
|
|
57
|
+
queue: deque[str] = deque([start_id])
|
|
58
|
+
visited.add(start_id)
|
|
59
|
+
|
|
60
|
+
while queue:
|
|
61
|
+
current_id = queue.popleft()
|
|
62
|
+
node = graph.get_node(current_id)
|
|
63
|
+
result.append(node)
|
|
64
|
+
for edge in graph.get_outgoing_edges(current_id):
|
|
65
|
+
if edge.target_id not in visited:
|
|
66
|
+
visited.add(edge.target_id)
|
|
67
|
+
queue.append(edge.target_id)
|
|
68
|
+
|
|
69
|
+
return result
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def dfs(graph: Graph, start_id: str) -> list[Node]:
|
|
73
|
+
"""Perform a depth-first search starting from a given node.
|
|
74
|
+
|
|
75
|
+
Like exploring a cave -- go as deep as you can down one tunnel,
|
|
76
|
+
then backtrack and try the next tunnel.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
graph: The graph to traverse.
|
|
80
|
+
start_id: The ID of the starting node.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
A list of nodes in DFS visit order.
|
|
84
|
+
|
|
85
|
+
Raises:
|
|
86
|
+
NodeNotFoundError: If the start node does not exist.
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
graph.get_node(start_id) # Validate existence
|
|
90
|
+
visited: set[str] = set()
|
|
91
|
+
result: list[Node] = []
|
|
92
|
+
stack: list[str] = [start_id]
|
|
93
|
+
|
|
94
|
+
while stack:
|
|
95
|
+
current_id = stack.pop()
|
|
96
|
+
if current_id in visited:
|
|
97
|
+
continue
|
|
98
|
+
visited.add(current_id)
|
|
99
|
+
node = graph.get_node(current_id)
|
|
100
|
+
result.append(node)
|
|
101
|
+
# Add neighbors in reverse order so we visit them in a predictable order
|
|
102
|
+
outgoing = graph.get_outgoing_edges(current_id)
|
|
103
|
+
stack.extend(edge.target_id for edge in reversed(outgoing) if edge.target_id not in visited)
|
|
104
|
+
|
|
105
|
+
return result
|