diff --git a/src/main/java/org/tinspin/index/qthypercube/QuadTreeKD.java b/src/main/java/org/tinspin/index/qthypercube/QuadTreeKD.java index cbe1993..cee93a1 100644 --- a/src/main/java/org/tinspin/index/qthypercube/QuadTreeKD.java +++ b/src/main/java/org/tinspin/index/qthypercube/QuadTreeKD.java @@ -21,6 +21,7 @@ import java.util.function.Predicate; import org.tinspin.index.*; +import org.tinspin.index.util.MathTools; import org.tinspin.index.util.StringBuilderLn; /** @@ -94,7 +95,9 @@ public void insert(double[] key, T value) { PointEntry e = new PointEntry<>(key, value); if (root == null) { // We calculate a better radius when adding a second point. - root = new QNode<>(key.clone(), INITIAL_RADIUS); + // We align the center to a power of two. That reduces precision problems when + // creating subnode centers. + root = new QNode<>(MathTools.floorPowerOfTwoCopy(key), INITIAL_RADIUS); } if (root.getRadius() == INITIAL_RADIUS) { adjustRootSize(key); @@ -113,10 +116,17 @@ private void adjustRootSize(double[] key) { return; } if (root.getRadius() == INITIAL_RADIUS) { - // We just use Euclidean here, that should be good enough in all cases. - double dist = PointDistance.L2.dist(key, root.getCenter()); - if (dist > 0) { - root.adjustRadius(2 * dist); + // Root size has not been initialized yet. + // We start by getting the maximum horizontal distance between the node center and any point in the node + double dMax = MathTools.maxDelta(key, root.getCenter()); + for (int i = 0; i < root.getEntries().size(); i++) { + dMax = Math.max(dMax, MathTools.maxDelta(root.getEntries().get(i).point(), root.getCenter())); + } + // We calculate the minimum required radius that is also a power of two. + // This radius can be divided by 2 many times without precision problems. + double radius = MathTools.ceilPowerOfTwo(dMax + QUtil.EPS_MUL); + if (radius > 0) { + root.adjustRadius(radius); } else if (root.getEntries().size() >= maxNodeSize - 1) { // we just set an arbitrary radius here root.adjustRadius(1000); diff --git a/src/main/java/org/tinspin/index/qthypercube2/QuadTreeKD2.java b/src/main/java/org/tinspin/index/qthypercube2/QuadTreeKD2.java index 01d50d4..364cb4f 100644 --- a/src/main/java/org/tinspin/index/qthypercube2/QuadTreeKD2.java +++ b/src/main/java/org/tinspin/index/qthypercube2/QuadTreeKD2.java @@ -109,6 +109,8 @@ public void insert(double[] key, T value) { PointEntry e = new PointEntry<>(key, value); if (root == null) { // We calculate a better radius when adding a second point. + // We align the center to a power of two. That reduces precision problems when + // creating subnode centers. root = new QNode<>(MathTools.floorPowerOfTwoCopy(key), INITIAL_RADIUS); } if (root.getRadius() == INITIAL_RADIUS) { @@ -128,10 +130,14 @@ private void adjustRootSize(double[] key) { return; } if (root.getRadius() == INITIAL_RADIUS) { + // Root size has not been initialized yet. + // We start by getting the maximum horizontal distance between the node center and any point in the node double dMax = MathTools.maxDelta(key, root.getCenter()); for (int i = 0; i < root.getValueCount(); i++) { dMax = Math.max(dMax, MathTools.maxDelta(root.getValues()[i].point(), root.getCenter())); } + // We calculate the minimum required radius that is also a power of two. + // This radius can be divided by 2 many times without precision problems. double radius = MathTools.ceilPowerOfTwo(dMax + QUtil.EPS_MUL); if (radius > 0) { root.adjustRadius(radius);