Skip to content

Commit 841c0b8

Browse files
committed
fix(edits): look for new edges by dialting lifted edges down to l2
1 parent 9520d5d commit 841c0b8

File tree

3 files changed

+51
-33
lines changed

3 files changed

+51
-33
lines changed

pychunkedgraph/debug/utils.py

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,14 @@ def print_node(cg, node: np.uint64, indent: int = 0, stop_layer: int = 2) -> Non
2626
print_node(cg, child, indent=indent + 4, stop_layer=stop_layer)
2727

2828

29-
def get_l2children(cg, node: np.uint64) -> np.ndarray:
30-
nodes = np.array([node], dtype=np.uint64)
31-
layers = cg.get_chunk_layers(nodes)
32-
assert np.all(layers >= 2), "nodes must be at layers >= 2"
33-
l2children = []
34-
while nodes.size:
35-
children = cg.get_children(nodes, flatten=True)
36-
layers = cg.get_chunk_layers(children)
37-
l2children.append(children[layers == 2])
38-
nodes = children[layers > 2]
39-
return np.concatenate(l2children)
40-
41-
4229
def sanity_check(cg, new_roots, operation_id):
4330
"""
4431
Check for duplicates in hierarchy, useful for debugging.
4532
"""
4633
# print(f"{len(new_roots)} new ids from {operation_id}")
4734
l2c_d = {}
4835
for new_root in new_roots:
49-
l2c_d[new_root] = get_l2children(cg, new_root)
36+
l2c_d[new_root] = cg.get_l2children([new_root])
5037
success = True
5138
for k, v in l2c_d.items():
5239
success = success and (len(v) == np.unique(v).size)
@@ -56,7 +43,7 @@ def sanity_check(cg, new_roots, operation_id):
5643

5744

5845
def sanity_check_single(cg, node, operation_id):
59-
v = get_l2children(cg, node)
46+
v = cg.get_l2children([node])
6047
msg = f"invalid node {node}:"
6148
msg += f" found {len(v)} l2 ids, must be {np.unique(v).size}"
6249
assert np.unique(v).size == len(v), f"{msg}, from {operation_id}."

pychunkedgraph/graph/chunkedgraph.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,3 +1036,18 @@ def get_chunk_layers_and_coordinates(self, node_or_chunk_ids: typing.Sequence):
10361036
self.meta, _nodes
10371037
)
10381038
return layers, chunk_coords
1039+
1040+
def get_l2children(self, node_ids) -> np.ndarray:
1041+
"""
1042+
Get L2 children of all node_ids, returns a flat array.
1043+
"""
1044+
node_ids = np.asarray(node_ids, dtype=basetypes.NODE_ID)
1045+
layers = self.get_chunk_layers(node_ids)
1046+
assert np.all(layers > 2), "nodes must be at layers > 2"
1047+
l2children = [types.empty_1d]
1048+
while node_ids.size:
1049+
children = self.get_children(node_ids, flatten=True)
1050+
layers = self.get_chunk_layers(children)
1051+
l2children.append(children[layers == 2])
1052+
node_ids = children[layers > 2]
1053+
return np.concatenate(l2children)

pychunkedgraph/graph/edges/__init__.py

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,22 +54,20 @@ def __init__(
5454
affinities: Optional[np.ndarray] = None,
5555
areas: Optional[np.ndarray] = None,
5656
):
57-
self.node_ids1 = np.array(node_ids1, dtype=basetypes.NODE_ID, copy=False)
58-
self.node_ids2 = np.array(node_ids2, dtype=basetypes.NODE_ID, copy=False)
57+
self.node_ids1 = np.array(node_ids1, dtype=basetypes.NODE_ID)
58+
self.node_ids2 = np.array(node_ids2, dtype=basetypes.NODE_ID)
5959
assert self.node_ids1.size == self.node_ids2.size
6060

6161
self._as_pairs = None
6262

6363
if affinities is not None and len(affinities) > 0:
64-
self._affinities = np.array(
65-
affinities, dtype=basetypes.EDGE_AFFINITY, copy=False
66-
)
64+
self._affinities = np.array(affinities, dtype=basetypes.EDGE_AFFINITY)
6765
assert self.node_ids1.size == self._affinities.size
6866
else:
6967
self._affinities = np.full(len(self.node_ids1), DEFAULT_AFFINITY)
7068

7169
if areas is not None and len(areas) > 0:
72-
self._areas = np.array(areas, dtype=basetypes.EDGE_AREA, copy=False)
70+
self._areas = np.array(areas, dtype=basetypes.EDGE_AREA)
7371
assert self.node_ids1.size == self._areas.size
7472
else:
7573
self._areas = np.full(len(self.node_ids1), DEFAULT_AREA)
@@ -430,7 +428,7 @@ def _check_hierarchy_a_from_b(nodes_a, hierarchy_a, layer, parent_ts):
430428
_children = _children[_children_layers > 2]
431429

432430
_hierarchy_a_from_b = np.concatenate(_hierarchy_a_from_b)
433-
return np.isin(_hierarchy_a_from_b, hierarchy_a)
431+
return np.any(np.isin(_hierarchy_a_from_b, hierarchy_a))
434432

435433
def _get_parents_b(edges, parent_ts, layer, fallback: bool = False):
436434
"""
@@ -451,7 +449,7 @@ def _get_parents_b(edges, parent_ts, layer, fallback: bool = False):
451449
_parents_b_missed = np.unique(cg.get_parents(missing, time_stamp=parent_ts))
452450
parents_b = np.concatenate([_parents_b, _parents_b_missed])
453451

454-
parents_a = edges[:, 0]
452+
parents_a = np.unique(edges[:, 0])
455453
stale_a = get_stale_nodes(cg, parents_a, parent_ts=parent_ts)
456454
if stale_a.size == parents_a.size or fallback:
457455
# this is applicable only for v2 to v3 migration
@@ -510,6 +508,18 @@ def _get_cx_edges(l2ids_a, max_node_ts, raw_only: bool = True):
510508
_edges.append(v[edge_layer])
511509
return np.concatenate(_edges)
512510

511+
def _get_dilated_edges(edges):
512+
layers_b = cg.get_chunk_layers(edges[:, 1])
513+
_mask = layers_b == 2
514+
_l2_edges = [edges[_mask]]
515+
for _edge in edges[~_mask]:
516+
_node_a, _node_b = _edge
517+
_nodes_b = cg.get_l2children([_node_b])
518+
_l2_edges.append(
519+
np.array([[_node_a, _b] for _b in _nodes_b], dtype=basetypes.NODE_ID)
520+
)
521+
return np.unique(np.concatenate(_l2_edges), axis=0)
522+
513523
def _get_new_edge(edge, edge_layer, parent_ts, padding, fallback: bool = False):
514524
"""
515525
Attempts to find new edge(s) for the stale `edge`.
@@ -534,16 +544,22 @@ def _get_new_edge(edge, edge_layer, parent_ts, padding, fallback: bool = False):
534544
if np.any(mask):
535545
parents_b = _get_parents_b(_edges[mask], parent_ts, edge_layer)
536546
else:
537-
# if none of `l2ids_b` were found in edges, `l2ids_a` already have new edges
538-
# so get the new identities of `l2ids_b` by using chunk mask
539-
try:
540-
parents_b = _get_parents_b_with_chunk_mask(
541-
l2ids_b, _edges[:, 1], max_node_ts, edge
542-
)
543-
except AssertionError:
544-
parents_b = []
545-
if fallback:
546-
parents_b = _get_parents_b(_edges, parent_ts, edge_layer, True)
547+
# partner edges likely lifted, dilate and retry
548+
_edges = _get_dilated_edges(_edges)
549+
mask = np.isin(_edges[:, 1], l2ids_b)
550+
if np.any(mask):
551+
parents_b = _get_parents_b(_edges[mask], parent_ts, edge_layer)
552+
else:
553+
# if none of `l2ids_b` were found in edges, `l2ids_a` already have new edges
554+
# so get the new identities of `l2ids_b` by using chunk mask
555+
try:
556+
parents_b = _get_parents_b_with_chunk_mask(
557+
l2ids_b, _edges[:, 1], max_node_ts, edge
558+
)
559+
except AssertionError:
560+
parents_b = []
561+
if fallback:
562+
parents_b = _get_parents_b(_edges, parent_ts, edge_layer, True)
547563

548564
parents_b = np.unique(
549565
cg.get_roots(parents_b, stop_layer=mlayer, ceil=False, time_stamp=parent_ts)

0 commit comments

Comments
 (0)