From ae9e13815359d115dbed4f8927367a672c4eccc5 Mon Sep 17 00:00:00 2001 From: boris-kz Date: Wed, 25 Dec 2024 21:55:19 -0500 Subject: [PATCH] edits with Chee --- .../vectorize_edge_blob/agg_recursion.py | 65 ++++++++--------- frame_2D_alg/vectorize_edge_blob/vect_edge.py | 72 +++++++++++-------- 2 files changed, 72 insertions(+), 65 deletions(-) diff --git a/frame_2D_alg/vectorize_edge_blob/agg_recursion.py b/frame_2D_alg/vectorize_edge_blob/agg_recursion.py index 31e313b93..b5a6d4e98 100644 --- a/frame_2D_alg/vectorize_edge_blob/agg_recursion.py +++ b/frame_2D_alg/vectorize_edge_blob/agg_recursion.py @@ -22,67 +22,64 @@ def cross_comp(root): # breadth-first node_,link_ cross-comp, connect.clusterin N_,L_,Et = comp_node_(root.subG_) # cross-comp exemplars, extrapolate to their node_s # mfork if val_(Et, fo=1) > 0: - mlay = L_[0].copy_.add_tree([L.derH for L in L_[-1]]); mlay.fd_=[]; root.derH.append_(mlay) + mlay = CH().add_tree([L.derH for L in L_]); mlay.fd_=[]; root.derH.append_(mlay) pL_ = {l for n in N_ for l,_ in get_rim(n, fd=0)} if len(pL_) > ave_L: - cluster_N_(root, pL_, fd=0) # optional divisive clustering, calls centroid and higher connect.clustering + cluster_N_([root], pL_, fd=0) # optional divisive clustering, calls centroid and higher connect.clustering # dfork if val_(Et, mEt=Et,fo=1) > 0: # same root for L_, root.link_ was compared in root-forming for alt clustering for L in L_: L.extH, L.root, L.mL_t, L.rimt, L.aRad, L.visited_, L.Et = CH(), root, [[],[]], [[],[]], 0, [L], copy(L.derH.Et) lN_,lL_,dEt = comp_link_(L_,Et) if val_(dEt, mEt=Et, fo=1) > 0: - dlay = lL_[0].copy_.add_tree([L.derH for L in lL_[-1]]); dlay.fd_= []; root.derH.append_(dlay) + dlay = CH().add_tree([L.derH for L in lL_]); dlay.fd_= []; root.derH.append_(dlay) plL_ = {l for n in lN_ for l,_ in get_rim(n, fd=1)} if len(plL_) > ave_L: - cluster_N_(root, plL_, fd=1) - # add in higher roots derH: - feedback(root) + cluster_N_([root], plL_, fd=1) + feedback(root) # add root derH to higher roots derH -def cluster_N_(root, L_, fd, nest=0): # top-down segment L_ by >ave ratio of L.dists - L_ = sorted(L_, key=lambda x: x.dist, reverse=True) # lower-dist links +def cluster_N_(root_, L_, fd, nest=0): # top-down segment L_ by >ave ratio of L.dists + + L_ = sorted(L_, key=lambda x: x.dist, reverse=True) # shorter links _L = L_[0] - N_, et = {*_L.nodet}, _L.derH.Et + N_, et = _L.nodet, _L.derH.Et # current dist segment: for i, L in enumerate(L_[1:], start=1): # long links first rel_dist = _L.dist / L.dist # >1 - if rel_dist < 1.2 or et[0] < ave or len(L_[i:]) < ave_L: # ~=dist Ns or either side of L is weak - _L = L; et += L.derH.Et - for n in L.nodet: N_.add(n) # in current dist span + if rel_dist < 1.2 or val_(et)>0 or len(L_[i:]) < ave_L: # ~=dist Ns or either side of L is weak + _L = L; N_ += L.nodet; et += L.derH.Et else: break # terminate contiguous-distance segment - min_dist = _L.dist G_ = [] + min_dist = _L.dist; N_ = {N_} + for N in N_: N.fin = 0 for N in N_: # cluster current distance segment - if len(N.root) > nest: continue # merged, root[0] = edge - node_,link_, et = set(), set(), np.zeros(4) - Gt = [node_,link_,et,min_dist]; N.root += [Gt] - _eN_ = {N} + if N.fin: continue + _eN_, node_,link_, et, = [N], [],[], np.zeros(4) while _eN_: - eN_ = set() + eN_ = [] for eN in _eN_: # cluster rim-connected ext Ns, all in root Gt - try: eN.root += [Gt] - except TypeError: eN.root = [eN.root, Gt] # convert to list - node_.add(eN) # of all rim + node_+=[eN]; eN.fin = 1 # all rim for L,_ in get_rim(eN, fd): - if L not in link_: - # if L.derH.Et[0]/ave * n.extH m/ave or L.derH.Et[0] + n.extH m*.1: density? - eN_.update([n for n in L.nodet if len(n.root) <= nest]) + if L not in link_: # if L.derH.Et[0]/ave * n.extH m/ave or L.derH.Et[0] + n.extH m*.1: density? + eN_ += [n for n in L.nodet if not n.fin] if L.dist >= min_dist: - link_.add(L); et += L.derH.Et - _eN_ = eN_ - # cluster shorter links, depth-first: + link_+=[L]; et+=L.derH.Et + _eN_ = [] + for n in {eN_}: + n.fin = 0; _eN_ += [n] + G = sum2graph(root_, [list({node_}),list({link_}), et, min_dist], fd, nest) + # higher root_ assign to all sub_G nodes sub_L_ = {l for n in node_ for l,_ in get_rim(n,fd) if l.dist < min_dist} - G = sum2graph(root, Gt, fd, nest) if len(sub_L_) > ave_L: - Et = np.sum([sL.derH.Et for sL in sub_L_], axis=1); Et[3] += nest # overlap + Et = np.sum([sL.derH.Et for sL in sub_L_],axis=0); Et[3]+=nest if val_(Et, fo=1) > 0: - cluster_N_(G, sub_L_, fd, nest+1) # sub-clusters will be nested in G.subG_ + cluster_N_(root_+[G], sub_L_, fd, nest+1) # sub-cluster shorter links, nest in G.subG_ G_ += [G] - root.subG_ = G_ - cluster_C_(root) # per dist segment + root_[-1].subG_ = G_ + cluster_C_(root_[-1]) # root per higher dist segment ''' Hierarchical clustering should alternate between two phases: generative via connectivity and compressive via centroid. @@ -108,8 +105,8 @@ def centroid(dnode_, node_, C=None): # sum|subtract and average Rim nodes C.latuple += n.latuple * s C.vert += n.vert * s C.yx += n.yx - if n.derH: C.derH.add_tree(n.derH, dir=s, fc=1) - if n.extH: C.extH.add_tree(n.extH, dir=s, fc=1) + if n.derH: C.derH.add_tree(n.derH, rev = s==-1, fc=1) + if n.extH: C.extH.add_tree(n.extH, rev = s==-1, fc=1) # get averages: k = len(dnode_); C.Et/=k; C.latuple/=k; C.vert/=k; C.aRad/=k; C.yx /= k if C.derH: C.derH.norm_(k) # derH/=k diff --git a/frame_2D_alg/vectorize_edge_blob/vect_edge.py b/frame_2D_alg/vectorize_edge_blob/vect_edge.py index 90a778d31..18ca1fa39 100644 --- a/frame_2D_alg/vectorize_edge_blob/vect_edge.py +++ b/frame_2D_alg/vectorize_edge_blob/vect_edge.py @@ -50,9 +50,9 @@ class CH(CBase): # generic derivation hierarchy of variable nesting: extH | derH, their layers and sub-layers name = "H" - def __init__(He, Et=np.zeros(4), tft=None, lft=None, fd_=None, root=None, node_=None, altH=None): + def __init__(He, Et=None, tft=None, lft=None, fd_=None, root=None, node_=None, altH=None): super().__init__() - He.Et = Et + He.Et = np.zeros(4) if Et is None else Et He.tft = [] if tft is None else tft # top fork tuple: arrays m_t, d_t He.lft = [] if lft is None else lft # lower fork tuple: CH /comp N_,L_, each mediates its own tft and lft He.fd_ = [] if fd_ is None else fd_ # 0: sum CGs, 1: sum CLs, + concat from comparands @@ -70,53 +70,61 @@ def __init__(He, Et=np.zeros(4), tft=None, lft=None, fd_=None, root=None, node_= def __bool__(H): return bool(H.tft) # empty CH - def copy_(He, root, rev=0, fc=0): # comp direction may be reversed to -1 + def copy_(He, root=None, rev=0, fc=0, i=None): # comp direction may be reversed to -1 - C = CH(fd_=copy(He.fd_), root=root, node_=copy(He.node_), Et=He.Et * -1 if (fc and rev) else copy(He.Et)) + if i: # from other source + C = He; C.lft = []; C.tft=[]; C.fd_=copy(i.fd_); C.root=root; C.node_=copy(i.node_) + else: # from self + C = CH(fd_=copy(He.fd_), root=root, node_=copy(He.node_)) + C.Et = i.Et * -1 if (fc and rev) else copy(i.Et) for fd, tt in enumerate(He.tft): # nested array tuples C.tft += [tt * -1 if rev and (fd or fc) else deepcopy(tt)] # empty in bottom layer: for fork in He.lft: C.lft += [fork.copy_(root=C, rev=rev, fc=fc)] - return C - def add_tree(HE, He_, root, rev=0, fc=0): # rev = dir==-1, unpack derH trees down to numericals and sum/subtract them + if not i: return C + + def add_tree(HE, He_, root=None, rev=0, fc=0): # rev = dir==-1, unpack derH trees down to numericals and sum/subtract them if not isinstance(He_,list): He_ = [He_] for He in He_: - if HE: - # sum tft: + if HE: # sum tft: for fd, (F_t, f_t) in enumerate(zip(HE.tft, He.tft)): # m_t and d_t for F_,f_ in zip(F_t, f_t): F_ += f_ * -1 if rev and (fd or fc) else f_ # m_| d_ in [dext,dlat,dver] for F, f in zip_longest(HE.lft, He.lft, fillvalue=None): # CH forks - if f: # empty in bottom layer - F.add_tree(f,rev,fc) # unpack both forks + if f: # not bottom layer + if F: F.add_tree(f,rev,fc) # unpack both forks + else: HE.append_(f.copy_) HE.node_ += [node for node in He.node_ if node not in HE.node_] # empty in CL derH? HE.Et += He.Et * -1 if rev and fc else He.Et else: - HE = He.copy_(root,rev,fc) - return HE # root should be updated by returned HE + HE.copy_(root,rev,fc, i=He) + return HE def append_(HE, He): # unpack HE lft tree down to He.fd_ and append He, or sum if fork exists, if He.fd_ - root = HE; fd_ = copy(He.fd_); full=1 - while fd_: - fd = fd_.pop(0) - if len(root.lft)>fd: root = root.lft[fd] + fork = root = HE + add = 1 + for fd in He.fd_: # unpack top-down, each fd was assigned by corresponding level of roots + if len(fork.lft) > fd: + root = fork + fork = fork.lft[fd] else: - root.root.lft += [He]; full=0 # fork was empty + fork.lft += [He.copy_]; add = 0 # fork was empty, init with He break - if full: - root.add_tree(He, root) # sum fork if filled by feedback + if add: + fork.add_tree(He, root) # add in fork initialized by feedback He.root = root - root.Et += He.Et - if not root.tft: root.tft = He.tft # if init from sum mlink_ + fork.Et += He.Et + if not fork.tft: + fork.tft = deepcopy(He.tft) # if init from sum mlink_ - def comp_tree(_He, He, rn, root, dir=1, fd=0): # unpack derH trees down to numericals and compare them + def comp_tree(_He, He, rn, root, dir=1): # unpack derH trees down to numericals and compare them _d_t, d_t = _He.tft[1], He.tft[1] # comp_tft: d_t = d_t * rn # norm by accum span @@ -141,7 +149,7 @@ def norm_(He, n): for f in He.tft: # arrays f *= n for fork in He.lft: # CHs - fork.norm_C(n) + fork.norm_(n) fork.Et *= n He.Et *= n @@ -275,7 +283,7 @@ def cluster_PP_(edge, fd): edge.subG_ = N_ edge.link_ = L_ if val_(Et, fo=1) > 0: # cancel by borrowing d? - mlay = L_[0].derH.copy_.add_tree([L.derH for L in L_[1:]]); mlay.fd_=[]; edge.derH.append_(mlay) + mlay = CH().add_tree([L.derH for L in L_]); mlay.fd_=[]; edge.derH.append_(mlay) if len(N_) > ave_L: cluster_PP_(edge, fd=0) if val_(Et, mEt=Et, fo=1) > 0: # likely not from the same links @@ -284,7 +292,7 @@ def cluster_PP_(edge, fd): # comp dPP_: lN_,lL_,dEt = comp_link_(L_, Et) if val_(dEt, fo=1) > 0: - dlay = lL_[0].derH.copy_.add_tree([L.derH for L in lL_[1:]]); dlay.fd_= []; edge.derH.append_(dlay) + dlay = CH().add_tree([L.derH for L in lL_]); dlay.fd_= []; edge.derH.append_(dlay) if len(lN_) > ave_L: cluster_PP_(edge, fd=1) @@ -437,8 +445,10 @@ def get_rim(N,fd): return N.rimt[0] + N.rimt[1] if fd else N.rim # add nesting def sum2graph(root, grapht, fd, nest): # sum node and link params into graph, aggH in agg+ or player in sub+ node_, link_, Et = grapht[:3] - graph = CG(fd=fd, Et=Et*icoef, root=root, node_=node_, link_=link_, rng=nest) # arg Et is internalized - if len(grapht) == 5: # called from cluster_N + fsub = len(grapht) == 5 # called from divisive cluster_N + graph = CG(fd=fd, Et=Et*icoef, root=root[-1] if fsub else root, node_=node_, link_=link_, rng=nest) + # arg Et is internalized; only immediate root assign to G? + if fsub: # called from cluster_N minL, subG_ = grapht[3:] if fd: graph.subL_ = subG_ else: graph.subG_ = subG_ @@ -454,11 +464,11 @@ def sum2graph(root, grapht, fd, nest): # sum node and link params into graph, a derH.add_tree(N.derH, graph) graph.Et += N.Et * icoef ** 2 # deeper, lower weight if nest: - if nest==1: N.root = [N.root] # init conversion - N.root += [graph] # root_ in distance-layered cluster - else: N.root = graph + if nest==1: N.root = [N.root] # initial conversion + N.root += [graph + root] # root_ in distance-layered cluster_N_ + else: N.root = graph # single root # sum link_ derH: - derLay = link_[0].derH.copy_.add_tree([link.derH for link in link_[1:]]); derLay.root = graph + derLay = CH().add_tree([link.derH for link in link_],root=graph) # root added in copy_ within add_tree if derH: derH.fd_=[]; derLay.append_(derH) graph.derH = derLay