def islocalnettree(self): if self.T.root.getchild() is None: return True q = [self.T.root.getchild()] for node in q: # nesting if len(node.ch) > 0 and node.point not in [ ch.point for ch in node.ch ]: print('Violates LNT nesting') return False # parent for par in rel(node.par): if dist(node, node.par) > dist(node, par): print('Violates LNT parent') return False for ch1 in node.ch: q.append(ch1) # covering if dist(node, ch1) > self.T.cc * self.T.tau**node.level: print('Violates LNT covering') return False # packing for ch2 in node.ch: if ch1 != ch2 and dist(ch1, ch2) <= self.T.cp * self.T.tau**max( ch1.level, ch2.level): print('Violates LNT packing: ', ch1.point, ch1.level, ch2.point, ch2.level) return False return True
def changernn(self, point, fromnode, tonode, todist=None): """ Changes the center of an uninserted point to a different node and moves the point to the cell of that node. Parameters: ---------- point : Point An uninserted point. fromnode : Node The current center of the uninserted point. tonode : Node The next center of the uninserted point. todist : float The precomputed distance between point and tonode. If todist==None, then the method computed the distance. """ todist = todist or dist(tonode, point) self._nn[point] = tonode self._nndist[point] = todist if todist <= self.tree.cp * (self.tree.tau ** (tonode.level - 1)) / 2: self._rnn_in[tonode].add(point) else: self._rnn_out[tonode].add(point) self._rnn_in[fromnode].discard(point) self._rnn_out[fromnode].discard(point)
def isglobalnettree(self): if self.T.root.getchild() is None: return True q = [self.T.root.getchild()] dictnry = dict() self.findleaves(self.T.root, dictnry) for node in q: # nesting if len(node.ch) > 0 and node.point not in [ ch.point for ch in node.ch ]: print('Violates NT nesting') return False # covering for leaf in dictnry[node]: if dist(node, leaf) > self.T.cc * self.T.tau**node.level: print('Violates NT covering') return False # packing others = [n.point for n in dictnry[self.T.root] - dictnry[node]] if len(others) > 0 and node.point.distto( *others) <= self.T.cp * self.T.tau**node.level: print('Violates NT packing') return False for ch1 in node.ch: q.append(ch1) return True
def nnhelper(self, point, currentnodes, level): if point.distto(*[n.point for n in currentnodes ]) > self.tree.cr * self.tree.tau**level: return None nextnodes = { n if n.level == level - 1 else n.par for n in ch(currentnodes) if dist(n, point) <= self.tree.cr * self.tree.tau**level } nn = self.nnhelper(point, nextnodes, level - 1) return nn if nn else min(currentnodes, key=lambda n: point.distto(n.point))
def update_ch(self, node): """ Updates children of a given node. Parameters: ---------- node : Node The node that its relatives should be updated. """ for other in ch(rel(node)): if dist(node, other) < dist(other, other.par): oldpar = other.par node.addch(other) child = oldpar.getchild() # The old parent should not be removed from the tree because it is a relative of `node` # However, if it has only one child, then that child should be checked # against the semi-compressed condition if len(oldpar.ch) == 1 and len(child.ch) == 1 and len(child.rel) == 1: if hasattr(self, 'ploc'): self.ploc.updateonremoval(child) oldpar.addch(child.getchild()) oldpar.ch.discard(child)
def update_par(self, node, closest): """ Updates the parent of a given node. Parameters: ---------- node : Node The node that its parent should be updated. closest : Node The node closest to `node` at the same level. """ newpar = nearest(node, rel(par(closest))) # There may be a node in rel(par(@closest)) such that # its distance to `node` is the same as the distance of `node` to `closest.par`. # In this case, the right parent should be the parent of `closest`, # otherwise we may need to delete `closest` from the tree. if dist(node, newpar) == dist(node, closest.par): newpar = closest.par if newpar.getchild().level < node.level: self.splitbelow(newpar, node.level) if self.isrel(node, newpar): node.addrel(newpar.getchild()) node.setpar(newpar)
def nn(self, point): currentnode = self.tree.root nextnode = self.tree.root.getchild() self.basictouchno += 1 while dist(nextnode, point) <= self.tree.cr * self.tree.tau**nextnode.level: currentnode = nextnode allnodes = ch(rel(currentnode)) nextlevel = max(n.level for n in allnodes) nextnode = min(allnodes, key=functools.partial(self.mincoveringdist, point=point, level=nextlevel)) return currentnode
def iscovered(self, node): """ Tests whether the covering property for `node.par` is satisfied or not. Parameters: ---------- node : Node The node that should be tested against the covering property. Returns: ------- bool Returns True is the distance between `node` and its parent is not greater than c_c\tau**(node.level+1). """ return dist(node, node.par) <= self.cc * (self.tau**(node.level + 1))
def insert(self, point, closest=None): """ Insertes a point into the net-tree. Parameters: ---------- point : Point The point to be inserted. closest : Node The center of the point. If `closest` is not provided, then the point location data structure is used to find it. """ closest = closest or self.ploc.nn(point) if hasattr(self, 'ploc'): dst = self.ploc.nndist(point, closest) # Removes the uninserted point from the point location data structure. # In other words, the point is removed from the mappings for the centers and cells self.ploc.removepoint(point) else: dst = dist(closest, point) # Finding the lowest level so that the point can still be a relative of the closest node level = self.minlevelrelatives(closest, point, dst) # If the packing property does not hold at this level, # then the node should be inserted in exactly one level down if dst <= self.cp * self.tau**level: level -= 1 if level < closest.level: # If there is a jump, then we should split it if closest.getchild().level < level: closest = self.splitbelow(closest, level) # Otehrwise, find the node associated to the same point else: closest = [n for n in closest.ch if n.point == closest.point][0] node = Node(point, level) self.update(node, closest) lowest = node # We ensure that the nesting property is satisfied if len(node.ch) > 0: lowest = Node(node.point, node.level - 1) if lowest not in node.ch: lowest.setpar(node) if hasattr(self, 'ploc'): self.ploc.updateonsplit(lowest) self.splitbelow(lowest, config.arithmatic.ninfty) while not self.iscovered(node): node = self.promote(node)
def nndist(self, point, nn = None): """ Returns the distance of point to its center. Parameters: ---------- point : Point An uninserted point. nn : Node The center of the uninserted point. If nn==None, then the data structure looks up the center in its saved mapping. Returns: ------- float The distance between the uninserted point and its center. """ return self._nndist[point] or dist(nn or self._nn[point], point)
def isrel(self, node, other, computeddist=None): """ Determines if the distance between `node` and `other` is at most c_r\tau**(node.level). Parameters: ---------- node : Node A node of the tree. other : Node or Point Either a node of the tree or a point. computeddist : float If the distance between `node` and `other` is precomputed, then we do not recompute it. Otherwise, if computeddist==None, then the method calculates the distance. Returns: bool """ return (computeddist or dist(node, other)) <= self.cr * (self.tau ** node.level)
def nnhelper(self, point, currentnodes, level): if len(currentnodes) != 0: self.basictouchno += len(currentnodes) if len(currentnodes) == 0 or \ point.distto(*[n.point for n in currentnodes]) > self.tree.cr * self.tree.tau ** level: return None children = ch(currentnodes) nextlevel = max(n.level for n in children) nextnodes = { n if n.level == nextlevel else n.par for n in children if dist(n, point) <= self.tree.cr * self.tree.tau**nextlevel } self.basictouchno += len(children) nn = self.nnhelper(point, nextnodes, nextlevel) if nn: return nn self.basictouchno += len(currentnodes) return min(currentnodes, key=lambda n: point.distto(n.point))
def minlevelrelatives(self, first, second, computeddist=None): """ Determines the minimum level so that `first` and `second` can be relatives. Parameters: ---------- first : Node A node of the tree. second : Node or Point A node of the tree or a point. computeddist : float If the distance between `node` and `other` is precomputed, then we do not recompute it. Otherwise, if computeddist==None, then the method calculates the distance. Returns: ------- float """ return math.ceil(math.log((computeddist or dist(first, second)) / self.cr, self.tau))
def nn(self, point): currentnode = self.tree.root nextnode = self.tree.root.getchild() # closestdist = dist(nextnode, point) # while closestdist <= self.tree.cr * self.tree.tau ** nextnode.level: # currentnode = nextnode # allnodes = ch(rel(currentnode)) # nextnode = allnodes.pop() # closestdist = dist(nextnode, point) # for n in allnodes: # newdist = dist(n,point) # if newdist < closestdist and newdist <= self.tree.cr * self.tree.tau ** n.level: # nextnode, closestdist = n, newdist while dist(nextnode, point) <= self.tree.cr * self.tree.tau**nextnode.level: currentnode = nextnode allnodes = ch(rel(currentnode)) nextlevel = max(n.level for n in allnodes) nextnode = min(allnodes, key=functools.partial(self.mincoveringdist, point=point, level=nextlevel)) return currentnode
def trytochangernn(self, point, tonode): """ Determines whether an uninserted point should change its cells or not. If so, the change will happen. Parameters: ---------- point : Point The uninserted point. tonode : Node The node that may be the new center for the uninserted point. """ fromnode = self._nn[point] todist = self.nndist(point) if fromnode.point == tonode.point else dist(tonode, point) if self.tree.isrel(tonode, point, todist): ''' we change the center of a point if either: 1) the current and next centers are associated to the same point and we have a split below (next center has a lower level) 2) the current and next centers are associated with different points and the next center is closer to the point than its current center ''' if (fromnode.point == tonode.point and fromnode.level > tonode.level) or \ (fromnode.point != tonode.point and todist < self.nndist(point)): self.changernn(point, fromnode, tonode, todist)
def mincoveringdist(self, node, point, level): dst = dist(node, point) self.basictouchno += 1 return dst if dst <= self.tree.cr * self.tree.tau**level else float( 'inf')
def nn(self, point): child = self.tree.root.getchild() if dist(child, point) > self.tree.cr * self.tree.tau**child.level: return self.tree.root return self.nnhelper(point, {child}, child.level) or self.tree.root
def nndist(self, point, nn=None): return dist(nn or self.nn(point), point)