Skip to content

Commit

Permalink
edits with Chee
Browse files Browse the repository at this point in the history
  • Loading branch information
boris-kz committed Dec 26, 2024
1 parent aa78934 commit ae9e138
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 65 deletions.
65 changes: 31 additions & 34 deletions frame_2D_alg/vectorize_edge_blob/agg_recursion.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
72 changes: 41 additions & 31 deletions frame_2D_alg/vectorize_edge_blob/vect_edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand Down Expand Up @@ -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_
Expand All @@ -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
Expand Down

0 comments on commit ae9e138

Please sign in to comment.