[GRASS-SVN] r71600 - grass/trunk/lib/btree2
svn_grass at osgeo.org
svn_grass at osgeo.org
Fri Oct 27 13:49:08 PDT 2017
Author: mmetz
Date: 2017-10-27 13:49:08 -0700 (Fri, 27 Oct 2017)
New Revision: 71600
Modified:
grass/trunk/lib/btree2/kdtree.c
grass/trunk/lib/btree2/kdtree.h
Log:
btree2 lib: improved k-d tree balancing
Modified: grass/trunk/lib/btree2/kdtree.c
===================================================================
--- grass/trunk/lib/btree2/kdtree.c 2017-10-27 19:56:40 UTC (rev 71599)
+++ grass/trunk/lib/btree2/kdtree.c 2017-10-27 20:49:08 UTC (rev 71600)
@@ -36,7 +36,7 @@
static struct kdnode *kdtree_insert2(struct kdtree *, struct kdnode *,
struct kdnode *, int, int);
-static int kdtree_replace(struct kdtree *, struct kdnode *, int);
+static int kdtree_replace(struct kdtree *, struct kdnode *);
static int kdtree_balance(struct kdtree *, struct kdnode *, int);
static int kdtree_first(struct kdtrav *, double *, int *);
static int kdtree_next(struct kdtrav *, double *, int *);
@@ -70,6 +70,7 @@
n->c = G_malloc(t->ndims * sizeof(double));
n->dim = 0;
n->depth = 0;
+ n->balance = 0;
n->uid = 0;
n->child[0] = NULL;
n->child[1] = NULL;
@@ -83,6 +84,32 @@
G_free(n);
}
+static void kdtree_update_node(struct kdtree *t, struct kdnode *n)
+{
+ int ld, rd, btol;
+
+ ld = (!n->child[0] ? -1 : n->child[0]->depth);
+ rd = (!n->child[1] ? -1 : n->child[1]->depth);
+ n->depth = MAX(ld, rd) + 1;
+
+ n->balance = 0;
+ /* set balance flag if any of the node's subtrees needs balancing
+ * or if the node itself needs balancing */
+ if ((n->child[0] && n->child[0]->balance) ||
+ (n->child[1] && n->child[1]->balance)) {
+ n->balance = 1;
+
+ return;
+ }
+
+ btol = t->btol;
+ if (!n->child[0] || !n->child[1])
+ btol = 2;
+
+ if (ld > rd + btol || rd > ld + btol)
+ n->balance = 1;
+}
+
/* create a new k-d tree with ndims dimensions,
* optionally set balancing tolerance */
struct kdtree *kdtree_create(char ndims, int *btol)
@@ -185,7 +212,7 @@
} s[256];
int top;
int dir, found;
- int ld, rd;
+ int balance, bmode;
sn.c = c;
sn.uid = uid;
@@ -221,8 +248,8 @@
dir = s[top].dir;
n->child[dir] = NULL;
- /* update node depth */
- n->depth = (!n->child[!dir] ? 0 : n->child[!dir]->depth + 1);
+ /* update node */
+ kdtree_update_node(t, n);
}
else {
t->root = NULL;
@@ -231,30 +258,73 @@
}
}
else
- kdtree_replace(t, s[top].n, 1);
+ kdtree_replace(t, s[top].n);
- if (top) {
+ while (top) {
top--;
- dir = s[top].dir;
n = s[top].n;
- while (kdtree_balance(t, n->child[dir], 0));
-
- /* update node depth */
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
+ /* update node */
+ kdtree_update_node(t, n);
}
- while (top) {
- top--;
- n = s[top].n;
- /* update node depth */
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
+ balance = 1;
+ bmode = 1;
+ if (balance) {
+ struct kdnode *r;
+ int iter, bmode2;
- while (kdtree_balance(t, n, 0));
+ /* fix any inconsistencies in the (sub-)tree */
+ iter = 0;
+ bmode2 = 0;
+ top = 0;
+ r = t->root;
+ s[top].n = r;
+ while (top >= 0) {
+
+ n = s[top].n;
+
+ /* top-down balancing
+ * slower but more compact */
+ if (!bmode2) {
+ while (kdtree_balance(t, n, bmode));
+ }
+
+ /* go down */
+ if (n->child[0] && n->child[0]->balance) {
+ dir = 0;
+ top++;
+ s[top].n = n->child[dir];
+ }
+ else if (n->child[1] && n->child[1]->balance) {
+ dir = 1;
+ top++;
+ s[top].n = n->child[dir];
+ }
+ /* go back up */
+ else {
+
+ /* bottom-up balancing
+ * faster but less compact */
+ kdtree_update_node(t, n);
+ if (bmode2) {
+ while (kdtree_balance(t, n, bmode));
+ }
+ top--;
+ if (top >= 0) {
+ kdtree_update_node(t, s[top].n);
+ }
+ if (!bmode2 && top == 0) {
+ iter++;
+ if (iter == 2) {
+ /* the top node has been visited twice,
+ * switch from top-down to bottom-up balancing */
+ iter = 0;
+ bmode2 = 1;
+ }
+ }
+ }
+ }
}
return 1;
@@ -291,7 +361,16 @@
ld = (!n->child[0] ? -1 : n->child[0]->depth);
rd = (!n->child[1] ? -1 : n->child[1]->depth);
+
+ if (ld < rd)
+ while (kdtree_balance(t, n->child[0], level));
+ else if (ld > rd)
+ while (kdtree_balance(t, n->child[1], level));
+ ld = (!n->child[0] ? -1 : n->child[0]->depth);
+ rd = (!n->child[1] ? -1 : n->child[1]->depth);
+ n->depth = MAX(ld, rd) + 1;
+
dir = (rd > ld);
top++;
@@ -303,18 +382,17 @@
n = s[top].n;
/* balance node */
+ while (kdtree_balance(t, n, level)) {
+ nbal++;
+ }
while (kdtree_balance(t, n->child[0], level));
while (kdtree_balance(t, n->child[1], level));
+
ld = (!n->child[0] ? -1 : n->child[0]->depth);
rd = (!n->child[1] ? -1 : n->child[1]->depth);
n->depth = MAX(ld, rd) + 1;
- while (kdtree_balance(t, n, level)) {
- while (kdtree_balance(t, n->child[0], level));
- while (kdtree_balance(t, n->child[1], level));
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
+ while (kdtree_balance(t, n, level)) {
nbal++;
}
}
@@ -323,18 +401,17 @@
n = s[top].n;
/* balance node */
+ while (kdtree_balance(t, n, level)) {
+ nbal++;
+ }
while (kdtree_balance(t, n->child[0], level));
while (kdtree_balance(t, n->child[1], level));
+
ld = (!n->child[0] ? -1 : n->child[0]->depth);
rd = (!n->child[1] ? -1 : n->child[1]->depth);
n->depth = MAX(ld, rd) + 1;
- while (kdtree_balance(t, n, level)) {
- while (kdtree_balance(t, n->child[0], level));
- while (kdtree_balance(t, n->child[1], level));
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
+ while (kdtree_balance(t, n, level)) {
nbal++;
}
@@ -364,19 +441,17 @@
n = s[top].n;
/* balance node */
+ while (kdtree_balance(t, n, level)) {
+ nbal++;
+ }
while (kdtree_balance(t, n->child[0], level));
while (kdtree_balance(t, n->child[1], level));
ld = (!n->child[0] ? -1 : n->child[0]->depth);
rd = (!n->child[1] ? -1 : n->child[1]->depth);
n->depth = MAX(ld, rd) + 1;
- while (kdtree_balance(t, n, level)) {
- while (kdtree_balance(t, n->child[0], level));
- while (kdtree_balance(t, n->child[1], level));
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
+ while (kdtree_balance(t, n, level)) {
nbal++;
}
@@ -417,7 +492,7 @@
}
}
- G_debug(1, "k-d tree optimization: %d times balanced, new depth %d",
+ G_debug(0, "k-d tree optimization: %d times balanced, new depth %d",
nbal, t->root->depth);
return;
@@ -801,11 +876,11 @@
/* internal functions */
/**********************************************/
-static int kdtree_replace(struct kdtree *t, struct kdnode *r, int bmode)
+static int kdtree_replace(struct kdtree *t, struct kdnode *r)
{
double mindist;
int rdir, ordir, dir;
- int ld, rd, old_depth;
+ int ld, rd;
struct kdnode *n, *rn, *or;
struct kdstack {
struct kdnode *n;
@@ -1013,25 +1088,9 @@
n->child[dir] = NULL;
t->count--;
- old_depth = n->depth;
+ kdtree_update_node(t, n);
+ top++;
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
-
-#if 0
- /* this can cause stack overflow because of too high levels of recursion */
- if (bmode > 1)
- while (kdtree_balance(t, n, bmode));
-#endif
-
- if (n->depth == old_depth)
- top = 0;
-
-#ifdef KD_DEBUG
- top = top2 - 1;
-#endif
-
/* go back up */
while (top) {
top--;
@@ -1049,10 +1108,8 @@
}
#endif
- /* update depth */
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
+ /* update node */
+ kdtree_update_node(t, n);
}
return nr;
@@ -1076,7 +1133,7 @@
if (old_depth != r->depth) {
G_warning("balancing: depth is wrong: %d != %d", r->depth, old_depth);
- r->depth = old_depth;
+ kdtree_update_node(t, r);
}
/* subtree difference */
@@ -1101,7 +1158,7 @@
or->uid = r->uid;
or->dim = t->nextdim[r->dim];
- if (!kdtree_replace(t, r, bmode))
+ if (!kdtree_replace(t, r))
G_fatal_error("kdtree_balance: nothing replaced");
#ifdef KD_DEBUG
@@ -1113,12 +1170,10 @@
}
#endif
- r->child[!dir] = kdtree_insert2(t, r->child[!dir], or, bmode, 1);
+ r->child[!dir] = kdtree_insert2(t, r->child[!dir], or, bmode, 1); /* bmode */
- /* update node depth */
- ld = (!r->child[0] ? -1 : r->child[0]->depth);
- rd = (!r->child[1] ? -1 : r->child[1]->depth);
- r->depth = MAX(ld, rd) + 1;
+ /* update node */
+ kdtree_update_node(t, r);
if (r->depth == old_depth) {
G_debug(4, "balancing had no effect");
@@ -1142,9 +1197,6 @@
} s[256];
int top;
int dir;
- int ld, rd;
- int old_depth;
- int go_back;
int bmode;
if (!r) {
@@ -1160,46 +1212,20 @@
rcallsmax = rcalls;
/* balancing modes
- * bmode = 0: a posteriori, top-down and bottom-up balancing, no recursion
- * slow, high tree depth (sub-optimal tree)
- * bmode = 1: top-down and bottom-up balancing, recursion
- * faster, lower tree depth (more optimal tree)
- * bmode = 2: bottom-up balancing
- * fastest, tree depth similar to bmode = 1
- * most optimal, but slowest: recursion, bottom-up, a posteriori
+ * bmode = 0: no recursion (only insert -> balance -> insert)
+ * slower, higher tree depth
+ * bmode = 1: recursion (insert -> balance -> insert -> balance ...)
+ * faster, more compact tree
* */
- bmode = 2;
+ bmode = 1;
/* find node with free child */
top = 0;
- go_back = 0;
s[top].n = r;
while (s[top].n) {
n = s[top].n;
- if (balance && bmode < 2) {
- /* balance node */
- old_depth = n->depth;
-
- while (kdtree_balance(t, n->child[0], bmode));
- while (kdtree_balance(t, n->child[1], bmode));
-
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
- while (kdtree_balance(t, n, bmode)) {
- while (kdtree_balance(t, n->child[0], bmode));
- while (kdtree_balance(t, n->child[1], bmode));
-
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
- }
- if (old_depth != n->depth)
- go_back = top;
- }
-
if (!cmpc(nnew, n, t) && (!dc || nnew->uid == n->uid)) {
G_debug(1, "KD node exists already, nothing to do");
@@ -1230,30 +1256,7 @@
nnew->dim = t->nextdim[n->dim];
t->count++;
-
- old_depth = n->depth;
- n->depth = (!n->child[!dir] ? 1 : n->child[!dir]->depth + 1);
-
- if (balance) {
- /* balance parent */
- while (kdtree_balance(t, n->child[0], bmode));
- while (kdtree_balance(t, n->child[1], bmode));
-
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
- while (kdtree_balance(t, n, bmode)) {
- while (kdtree_balance(t, n->child[0], bmode));
- while (kdtree_balance(t, n->child[1], bmode));
-
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
- }
- }
-
- if (old_depth != n->depth)
- go_back = top;
+ top++;
}
/* go back up */
@@ -1261,32 +1264,11 @@
top--;
n = s[top].n;
- /* update node depth */
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
+ /* update node */
+ kdtree_update_node(t, n);
- if (balance) {
- /* balance node */
- /* slightly reduced tree depth, a bit slower: */
- /*
- while (kdtree_balance(t, n->child[0], bmode));
- while (kdtree_balance(t, n->child[1], bmode));
- */
+ /* do not balance on the way back up */
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
- while (kdtree_balance(t, n, bmode)) {
- while (kdtree_balance(t, n->child[0], bmode));
- while (kdtree_balance(t, n->child[1], bmode));
-
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
- }
- }
-
#ifdef KD_DEBUG
/* debug directions */
if (n->child[0]) {
@@ -1300,53 +1282,58 @@
#endif
}
- if (balance && bmode == 0) {
+ if (balance) {
+ int iter, bmode2;
+
+ /* fix any inconsistencies in the (sub-)tree */
+ iter = 0;
+ bmode2 = 0;
top = 0;
- go_back = 0;
s[top].n = r;
- while (s[top].n) {
+ while (top >= 0) {
+
n = s[top].n;
- old_depth = n->depth;
- /* balance node */
- while (kdtree_balance(t, n->child[0], bmode));
- while (kdtree_balance(t, n->child[1], bmode));
+ /* top-down balancing
+ * slower but more compact */
+ if (!bmode2) {
+ while (kdtree_balance(t, n, bmode));
+ }
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
- while (kdtree_balance(t, n, bmode)) {
- /*
- while (kdtree_balance(t, n->child[0], bmode));
- while (kdtree_balance(t, n->child[1], bmode));
- */
-
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
+ /* go down */
+ if (n->child[0] && n->child[0]->balance) {
+ dir = 0;
+ top++;
+ s[top].n = n->child[dir];
}
- if (old_depth != n->depth)
- go_back = top;
+ else if (n->child[1] && n->child[1]->balance) {
+ dir = 1;
+ top++;
+ s[top].n = n->child[dir];
+ }
+ /* go back up */
+ else {
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
-
- dir = (ld < rd);
-
- top++;
- s[top].n = n->child[dir];
+ /* bottom-up balancing
+ * faster but less compact */
+ if (bmode2) {
+ while (kdtree_balance(t, n, bmode));
+ }
+ top--;
+ if (top >= 0) {
+ kdtree_update_node(t, s[top].n);
+ }
+ if (!bmode2 && top == 0) {
+ iter++;
+ if (iter == 2) {
+ /* the top node has been visited twice,
+ * switch from top-down to bottom-up balancing */
+ iter = 0;
+ bmode2 = 1;
+ }
+ }
+ }
}
-
- top = go_back;
- while (top) {
- top--;
- n = s[top].n;
-
- /* update node depth */
- ld = (!n->child[0] ? -1 : n->child[0]->depth);
- rd = (!n->child[1] ? -1 : n->child[1]->depth);
- n->depth = MAX(ld, rd) + 1;
- }
}
rcalls--;
Modified: grass/trunk/lib/btree2/kdtree.h
===================================================================
--- grass/trunk/lib/btree2/kdtree.h 2017-10-27 19:56:40 UTC (rev 71599)
+++ grass/trunk/lib/btree2/kdtree.h 2017-10-27 20:49:08 UTC (rev 71600)
@@ -68,6 +68,7 @@
{
unsigned char dim; /*!< split dimension of this node */
unsigned char depth; /*!< depth at this node */
+ unsigned char balance; /*!< flag to indicate if balancing is needed */
double *c; /*!< coordinates */
int uid; /*!< unique id of this node */
struct kdnode *child[2]; /*!< link to children: `[0]` for smaller, `[1]` for larger */
More information about the grass-commit
mailing list