Skip to content

Commit 5e2304a

Browse files
committed
new proposed core intrenal representation and implementation
1 parent 35e43ac commit 5e2304a

19 files changed

Lines changed: 5861 additions & 6710 deletions

annnet/_support/dataframe_backend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def dataframe_height(df) -> int:
131131
"""Return the row count for a dataframe-like object."""
132132
if df is None:
133133
return 0
134-
return len(_to_nw(df).rows())
134+
return _to_nw(df).shape[0]
135135

136136

137137
def dataframe_width(df) -> int:

annnet/_support/metadata.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ def _repr_html_(self) -> str:
298298
('Repository', _link(urls['Repository'])) if urls.get('Repository') else None,
299299
('Documentation', _link(urls['Documentation'])) if urls.get('Documentation') else None,
300300
(
301-
'Default graph backend',
301+
'Default adapter backend',
302302
_chips({graph_default: graph_default != 'none'}, icons=False),
303303
),
304304
('Default plot backend', _chips({plot_default: plot_default != 'none'}, icons=False)),

annnet/algorithms/traversal.py

Lines changed: 85 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,26 @@ def _hyper_meta(rec):
99

1010

1111
class Traversal:
12-
"""Local neighborhood traversal helpers over the incidence-backed graph."""
12+
"""Local neighborhood traversal over the incidence-backed graph.
13+
14+
Binary adjacency is answered from the ``_src_to_edges`` / ``_tgt_to_edges`` indices in
15+
O(degree); hyperedges are handled from a per-version cached list, so a graph with no
16+
hyperedges never pays a full edge scan.
17+
"""
18+
19+
def _iter_hyperedges(self):
20+
"""Return ``(eid, rec)`` for live hyperedges, cached against the graph version."""
21+
version = getattr(self, '_version', None)
22+
cache = getattr(self, '_hyper_items_cache', None)
23+
if cache is None or cache[0] != version:
24+
items = [
25+
(eid, rec)
26+
for eid, rec in self._edges.items()
27+
if rec.etype == 'hyper' and rec.col_idx >= 0
28+
]
29+
self._hyper_items_cache = (version, items)
30+
return items
31+
return cache[1]
1332

1433
def neighbors(self, entity_id):
1534
"""Return adjacent entities for a vertex or edge-entity.
@@ -27,35 +46,37 @@ def neighbors(self, entity_id):
2746
ekey = self._resolve_entity_key(entity_id)
2847
if ekey not in self._entities:
2948
return []
49+
self._ensure_edge_indexes()
3050

3151
out = set()
3252
default_dir = self.directed if self.directed is not None else True
3353
ekey_kind = self._entities[ekey].kind
3454
probe = ekey if self._aspects != ('_',) else entity_id
35-
36-
for _eid, rec in self._edges.items():
55+
edges = self._edges
56+
57+
for eid in self._src_to_edges.get(probe, ()):
58+
rec = edges[eid]
59+
if rec.col_idx >= 0:
60+
out.add(rec.tgt)
61+
for eid in self._tgt_to_edges.get(probe, ()):
62+
rec = edges[eid]
3763
if rec.col_idx < 0:
3864
continue
39-
if rec.etype == 'hyper':
40-
meta = _hyper_meta(rec)
41-
if meta['directed']:
42-
if probe in meta['head']:
43-
out |= meta['tail']
44-
elif probe in meta['tail']:
45-
out |= meta['head']
46-
else:
47-
members = meta.get('members', set())
48-
if probe in members:
49-
out |= members - {probe}
65+
edir = rec.directed if rec.directed is not None else default_dir
66+
if (not edir) or ekey_kind == 'edge_entity':
67+
out.add(rec.src)
68+
69+
for _eid, rec in self._iter_hyperedges():
70+
meta = _hyper_meta(rec)
71+
if meta['directed']:
72+
if probe in meta['head']:
73+
out |= meta['tail']
74+
elif probe in meta['tail']:
75+
out |= meta['head']
5076
else:
51-
s, t = rec.src, rec.tgt
52-
if s is None or t is None:
53-
continue
54-
edir = rec.directed if rec.directed is not None else default_dir
55-
if s == probe:
56-
out.add(t)
57-
elif t == probe and (not edir or ekey_kind == 'edge_entity'):
58-
out.add(s)
77+
members = meta['members']
78+
if probe in members:
79+
out |= members - {probe}
5980
return list(out)
6081

6182
def out_neighbors(self, vertex_id):
@@ -74,32 +95,34 @@ def out_neighbors(self, vertex_id):
7495
ekey = self._resolve_entity_key(vertex_id)
7596
if ekey not in self._entities:
7697
return []
98+
self._ensure_edge_indexes()
7799

78100
out = set()
79101
default_dir = self.directed if self.directed is not None else True
80102
probe = ekey if self._aspects != ('_',) else vertex_id
81-
82-
for _eid, rec in self._edges.items():
103+
edges = self._edges
104+
105+
for eid in self._src_to_edges.get(probe, ()):
106+
rec = edges[eid]
107+
if rec.col_idx >= 0:
108+
out.add(rec.tgt)
109+
for eid in self._tgt_to_edges.get(probe, ()):
110+
rec = edges[eid]
83111
if rec.col_idx < 0:
84112
continue
85-
if rec.etype == 'hyper':
86-
meta = _hyper_meta(rec)
87-
if meta['directed']:
88-
if probe in meta['head']:
89-
out |= meta['tail']
90-
else:
91-
members = meta.get('members', set())
92-
if probe in members:
93-
out |= members - {probe}
113+
edir = rec.directed if rec.directed is not None else default_dir
114+
if not edir:
115+
out.add(rec.src)
116+
117+
for _eid, rec in self._iter_hyperedges():
118+
meta = _hyper_meta(rec)
119+
if meta['directed']:
120+
if probe in meta['head']:
121+
out |= meta['tail']
94122
else:
95-
s, t = rec.src, rec.tgt
96-
if s is None or t is None:
97-
continue
98-
edir = rec.directed if rec.directed is not None else default_dir
99-
if s == probe:
100-
out.add(t)
101-
elif t == probe and not edir:
102-
out.add(s)
123+
members = meta['members']
124+
if probe in members:
125+
out |= members - {probe}
103126
return list(out)
104127

105128
def successors(self, vertex_id):
@@ -133,32 +156,34 @@ def in_neighbors(self, vertex_id):
133156
ekey = self._resolve_entity_key(vertex_id)
134157
if ekey not in self._entities:
135158
return []
159+
self._ensure_edge_indexes()
136160

137161
inn = set()
138162
default_dir = self.directed if self.directed is not None else True
139163
probe = ekey if self._aspects != ('_',) else vertex_id
140-
141-
for _eid, rec in self._edges.items():
164+
edges = self._edges
165+
166+
for eid in self._tgt_to_edges.get(probe, ()):
167+
rec = edges[eid]
168+
if rec.col_idx >= 0:
169+
inn.add(rec.src)
170+
for eid in self._src_to_edges.get(probe, ()):
171+
rec = edges[eid]
142172
if rec.col_idx < 0:
143173
continue
144-
if rec.etype == 'hyper':
145-
meta = _hyper_meta(rec)
146-
if meta['directed']:
147-
if probe in meta['tail']:
148-
inn |= meta['head']
149-
else:
150-
members = meta.get('members', set())
151-
if probe in members:
152-
inn |= members - {probe}
174+
edir = rec.directed if rec.directed is not None else default_dir
175+
if not edir:
176+
inn.add(rec.tgt)
177+
178+
for _eid, rec in self._iter_hyperedges():
179+
meta = _hyper_meta(rec)
180+
if meta['directed']:
181+
if probe in meta['tail']:
182+
inn |= meta['head']
153183
else:
154-
s, t = rec.src, rec.tgt
155-
if s is None or t is None:
156-
continue
157-
edir = rec.directed if rec.directed is not None else default_dir
158-
if t == probe:
159-
inn.add(s)
160-
elif s == probe and not edir:
161-
inn.add(t)
184+
members = meta['members']
185+
if probe in members:
186+
inn |= members - {probe}
162187
return list(inn)
163188

164189
def predecessors(self, vertex_id):

0 commit comments

Comments
 (0)