[GRASS-SVN] r63917 - grass/trunk/lib/btree2

svn_grass at osgeo.org svn_grass at osgeo.org
Thu Jan 1 12:56:21 PST 2015


Author: mmetz
Date: 2015-01-01 12:56:20 -0800 (Thu, 01 Jan 2015)
New Revision: 63917

Modified:
   grass/trunk/lib/btree2/kdtree.c
   grass/trunk/lib/btree2/kdtree.h
Log:
kd tree: optimize, add comments

Modified: grass/trunk/lib/btree2/kdtree.c
===================================================================
--- grass/trunk/lib/btree2/kdtree.c	2015-01-01 20:17:59 UTC (rev 63916)
+++ grass/trunk/lib/btree2/kdtree.c	2015-01-01 20:56:20 UTC (rev 63917)
@@ -25,7 +25,7 @@
 #endif
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
 
-#define KD_BTOL 6
+#define KD_BTOL 7
 
 #ifdef KD_DEBUG
 #undef KD_DEBUG
@@ -78,7 +78,7 @@
     G_free(n);
 }
 
-/* create a new kd tree with ndims dimensions,
+/* create a new k-d tree with ndims dimensions,
  * optionally set balancing tolerance */
 struct kdtree *kdtree_create(char ndims, int *btol)
 {
@@ -146,7 +146,7 @@
     t = NULL;
 }
 
-/* insert an item (coordinates c and uid) into the kd tree
+/* insert an item (coordinates c and uid) into the k-d tree
  * dc == 1: allow duplicate coordinates */
 int kdtree_insert(struct kdtree *t, double *c, int uid, int dc)
 {
@@ -162,7 +162,7 @@
     return count < t->count;
 }
 
-/* remove an item from the kd tree
+/* remove an item from the k-d tree
  * coordinates c and uid must match */
 int kdtree_remove(struct kdtree *t, double *c, int uid)
 {
@@ -183,7 +183,7 @@
     s[top].n = t->root;
     dir = 1;
     found = 0;
-    while (found) {
+    while (!found) {
 	n = s[top].n;
 	found = (!cmpc(&sn, n, t) && sn.uid == n->uid);
 	if (!found) {
@@ -199,18 +199,25 @@
 	    }
 	}
     }
-    if (!kdtree_replace(t, s[top].n)) {
+
+    if (s[top].n->depth == 0) {
+	kdtree_free_node(s[top].n);
 	s[top].n = NULL;
 	if (top) {
-	    n = s[top - 1].n;
-	    dir = s[top - 1].dir;
+	    top--;
+	    n = s[top].n;
+	    dir = s[top].dir;
 	    n->child[dir] = NULL;
 	}
-	else
+	else {
 	    t->root = NULL;
 
-	return 1;
+	    return 1;
+	}
     }
+    else
+	kdtree_replace(t, s[top].n);
+
     if (top) {
 	int old_depth;
 
@@ -237,7 +244,7 @@
 	n->depth = MAX(ld, rd) + 1;
     }
 
-    kdtree_balance(t, t->root);
+    while (kdtree_balance(t, t->root));
 
     return 1;
 }
@@ -271,6 +278,8 @@
     s[top].v = 0;
     top++;
 
+    G_debug(1, "k-d tree optimization for %zd items:", t->count);
+
     /* top-down balancing */
     while (top) {
 	top--;
@@ -313,7 +322,6 @@
 	count++;
     }
     if (level < 2) {
-	G_debug(1, "k-d tree optimization for %zd items:", t->count);
 	G_debug(1, "%d steps, %d times balanced", count, bal);
 	
 	return;
@@ -356,15 +364,13 @@
 	}
 	count++;
     }
-    G_debug(1, "k-d tree optimization for %zd items:", t->count);
     G_debug(1, "%d steps, %d times balanced", count, bal);
 }
 
-
 /* find k nearest neighbors 
- * results are stored in uid (uids) and d (distances)
+ * results are stored in uid (uids) and d (squared distances)
  * optionally an uid to be skipped can be given
- * useful when searching for the nearest neighbor of an item 
+ * useful when searching for the nearest neighbors of an item 
  * that is also in the tree */
 int kdtree_knn(struct kdtree *t, double *c, int *uid, double *d, int k, int *skip)
 {
@@ -471,14 +477,11 @@
 	}
     }
 
-    for (i = 0; i < found; i++)
-	d[i] = sqrt(d[i]);
-
     return found;
 }
 
 /* find all nearest neighbors within distance aka radius search
- * results are stored in puid (uids) and pd (distances)
+ * results are stored in puid (uids) and pd (squared distances)
  * memory is allocated as needed, the calling fn must free the memory
  * optionally an uid to be skipped can be given */
 int kdtree_dnn(struct kdtree *t, double *c, int **puid, double **pd,
@@ -582,9 +585,6 @@
 	    }
 	}
     }
-    
-    for (i = 0; i < found; i++)
-	d[i] = sqrt(d[i]);
 
     *pd = d;
     *puid = uid;
@@ -627,13 +627,6 @@
 	rdir = 0;
     }
 
-    if (!or->child[rdir]) {
-	 /* no replacement, delete */
-	kdtree_free_node(r);
-
-	return nr;
-    }
-
     /* replace old root, make replacement the new root
      * repeat until replacement is leaf */
     ordir = rdir;
@@ -895,7 +888,7 @@
 static struct kdnode *kdtree_insert2(struct kdtree *t, struct kdnode *r,
                                  struct kdnode *nnew, int balance, int dc)
 {
-    struct kdnode *n;
+    struct kdnode *n, *nr;
     struct kdstack {
 	struct kdnode *n;
 	int dir;
@@ -949,7 +942,46 @@
 	    if (old_depth != n->depth)
 		go_back = top;
 	}
+	if (dc && n->depth == 1 && !n->child[!dir] && n->child[dir]) {
 
+	    if ((cmp(nnew, n->child[dir], n->dim) > 0) != dir) {
+		/* n -> nnew -> n->child[dir] */
+		nnew->child[dir] = n->child[dir];
+		n->child[dir] = NULL;
+		nnew->child[!dir] = n;
+		nnew->depth = n->depth;
+		n->depth = 0;
+		nnew->dim = n->dim;
+		n->dim = t->nextdim[nnew->dim];
+		nr = nnew;
+	    }
+	    else {
+		/* n -> n->child[dir] -> nnew */
+		nnew->child[dir] = n->child[dir]->child[dir];
+		nnew->child[!dir] = n->child[dir]->child[!dir];
+		nnew->depth = n->child[dir]->depth;
+		nnew->dim = n->child[dir]->dim;
+		nr = n->child[dir];
+		nr->dim = n->dim;
+		nr->depth = n->depth;
+		nr->child[dir] = nnew;
+		nr->child[!dir] = n;
+		n->child[dir] = NULL;
+		n->child[!dir] = NULL;
+		n->depth = 0;
+		n->dim = nnew->dim;
+	    }
+	    if (top) {
+		s[top - 1].n->child[s[top - 1].dir] = nr;
+	    }
+	    else {
+		r = nr;
+	    }
+	    t->count++;
+
+	    return r;
+	}
+
 	top++;
 	if (top > 255)
 	    G_fatal_error("depth too large: %d", top);
@@ -969,7 +1001,7 @@
     n->depth = (!n->child[!dir] ? 1 : n->child[!dir]->depth + 1);
 
     if (balance) {
-	/* balance root */
+	/* balance parent */
 	while (kdtree_balance(t, n));
     }
 

Modified: grass/trunk/lib/btree2/kdtree.h
===================================================================
--- grass/trunk/lib/btree2/kdtree.h	2015-01-01 20:17:59 UTC (rev 63916)
+++ grass/trunk/lib/btree2/kdtree.h	2015-01-01 20:56:20 UTC (rev 63917)
@@ -1,4 +1,54 @@
+/*!
+ * \file kdtree.c
+ *
+ * \brief binary search tree 
+ *
+ * Dynamic balanced k-d tree implementation
+ *
+ * (C) 2014 by the GRASS Development Team
+ *
+ * This program is free software under the GNU General Public License
+ * (>=v2).  Read the file COPYING that comes with GRASS for details.
+ *
+ * \author Markus Metz
+ */
 
+/***********************************************************************
+ * k-d tree: 
+ * multidimensional binary search tree for nearest neighbor search
+ * 
+ * Bentley, J. L. (1975). "Multidimensional binary search trees used for 
+ * associative searching". Communications of the ACM 18 (9): 509.
+ * doi:10.1145/361002.361007
+ * 
+ * This k-d tree is a dynamic tree:
+ * elements can be inserted and removed any time
+ * 
+ * this k-d tree is balanced:
+ * subtrees have a similar depth (the difference in subtrees' depths is 
+ * not allowed to be larger than the balancing tolerance)
+ * 
+ * USAGE:
+ * create a new k-d tree
+ * kdtree_create();
+ * 
+ * insert points into the tree
+ * kdtree_insert();
+ * 
+ * optionally optimize the tree:
+ * kdtre_optimize
+ * 
+ * search k nearest neighbours
+ * kdtree_knn();
+ * 
+ * search all neighbors in radius
+ * kdtree_dnn();
+ * 
+ * destroy the tree:
+ * kdtree_destroy();
+ * 
+ ***********************************************************************/
+
 struct kdnode
 {
     unsigned char dim;		/* split dimension of this node */
@@ -18,11 +68,54 @@
     struct kdnode *root;	/* tree root */
 };
 
-struct kdtree *kdtree_create(char, int *);
-void kdtree_destroy(struct kdtree *);
-void kdtree_clear(struct kdtree *);
-int kdtree_insert(struct kdtree *, double *, int, int);
-int kdtree_remove(struct kdtree *, double *, int);
-int kdtree_knn(struct kdtree *, double *, int *, double *, int, int *);
-int kdtree_dnn(struct kdtree *, double *, int **, double **, double, int *);
-void kdtree_optimize(struct kdtree *, int);
+/* creae a new k-d tree */
+struct kdtree *kdtree_create(char ndims,	/* number of dimensions */
+                             int *btol);	/* optional balancing tolerance */
+
+/* destroy a tree */
+void kdtree_destroy(struct kdtree *t);
+
+/* clear a tree, removing all entries */
+void kdtree_clear(struct kdtree *t);
+
+/* insert an item (coordinates c and uid) into the k-d tree */
+int kdtree_insert(struct kdtree *t,		/* k-d tree */
+                  double *c,			/* coordinates */
+		  int uid,			/* the point's unique id */
+		  int dc);			/* allow duplicate coordinates */
+
+/* remove an item from the k-d tree
+ * coordinates c and uid must match */
+int kdtree_remove(struct kdtree *t,		/* k-d tree */
+                  double *c,			/* coordinates */
+		  int uid);			/* the point's unique id */
+
+/* find k nearest neighbors 
+ * results are stored in uid (uids) and d (squared distances)
+ * optionally an uid to be skipped can be given
+ * useful when searching for the nearest neighbors of an item 
+ * that is also in the tree */
+int kdtree_knn(struct kdtree *t,		/* k-d tree */
+               double *c,			/* coordinates */
+	       int *uid,			/* unique ids of the neighbors */
+	       double *d,			/* squared distances to the neighbors */
+	       int k,				/* number of neighbors to find */
+	       int *skip);			/* unique id to skip */
+
+
+/* find all nearest neighbors within distance aka radius search
+ * results are stored in puid (uids) and pd (squared distances)
+ * memory is allocated as needed, the calling fn must free the memory
+ * optionally an uid to be skipped can be given */
+int kdtree_dnn(struct kdtree *t,		/* k-d tree */
+               double *c,			/* coordinates */
+	       int **puid,			/* unique ids of the neighbors */
+	       double **pd,			/* squared distances to the neighbors */
+	       double maxdist,			/* radius to search around the given coordinates */
+	       int *skip);			/* unique id to skip */
+
+/* k-d tree optimization, only useful if the tree will be heavily used
+ * (more searches than items in the tree)
+ * level 0 = a bit, 1 = more, 2 = a lot */
+void kdtree_optimize(struct kdtree *t,		/* k-d tree */
+                     int level);		/* optimization level */



More information about the grass-commit mailing list