Permalink
Browse files

Added implementation of QuadraticPickSeeds; QuadraticPickNext

  • Loading branch information...
1 parent 34e9ce0 commit 25ebc2e62065a8049ae4c2200079653ea3f6ca30 Russ Weeks committed Aug 28, 2012
Showing with 128 additions and 46 deletions.
  1. +123 −41 src/com/newbrightidea/util/RTree.java
  2. +5 −5 test/com/newbrightidea/util/TestRTree.java
@@ -21,13 +21,16 @@
*/
public class RTree<T>
{
+ public enum SeedPicker { LINEAR, QUADRATIC }
private final int maxEntries;
private final int minEntries;
private final int numDims;
private final float[] pointDims;
+ private final SeedPicker seedPicker;
+
private Node root;
private volatile int size;
@@ -42,16 +45,22 @@
* @param numDims
* the number of dimensions of the RTree.
*/
- public RTree(int maxEntries, int minEntries, int numDims)
+ public RTree(int maxEntries, int minEntries, int numDims, SeedPicker seedPicker)
{
assert (minEntries <= (maxEntries / 2));
this.numDims = numDims;
this.maxEntries = maxEntries;
this.minEntries = minEntries;
+ this.seedPicker = seedPicker;
pointDims = new float[numDims];
root = buildRoot(true);
}
+ public RTree(int maxEntries, int minEntries, int numDims)
+ {
+ this(maxEntries, minEntries, numDims, SeedPicker.LINEAR);
+ }
+
private Node buildRoot(boolean asLeaf)
{
float[] initCoords = new float[numDims];
@@ -70,7 +79,7 @@ private Node buildRoot(boolean asLeaf)
*/
public RTree()
{
- this(50, 2, 2);
+ this(50, 2, 2, SeedPicker.LINEAR);
}
/**
@@ -375,6 +384,10 @@ private void adjustTree(Node n, Node nn)
private Node[] splitNode(Node n)
{
+ // TODO: this class probably calls "tighten" a little too often.
+ // For instance the call at the end of the "while (!cc.isEmpty())" loop
+ // could be modified and inlined because it's only adjusting for the addition
+ // of a single node. Left as-is for now for readability.
@SuppressWarnings("unchecked")
Node[] nn = new RTree.Node[]
{ n, new Node(n.coords, n.dimensions, n.leaf) };
@@ -385,32 +398,30 @@ private void adjustTree(Node n, Node nn)
}
LinkedList<Node> cc = new LinkedList<Node>(n.children);
n.children.clear();
- Node[] ss = pickSeeds(cc);
+ Node[] ss = seedPicker == SeedPicker.LINEAR ? lPickSeeds(cc) : qPickSeeds(cc);
nn[0].children.add(ss[0]);
nn[1].children.add(ss[1]);
+ tighten(nn);
while (!cc.isEmpty())
{
if ((nn[0].children.size() >= minEntries)
&& (nn[1].children.size() + cc.size() == minEntries))
{
nn[1].children.addAll(cc);
cc.clear();
- tighten(nn[0]); // Not sure this is required.
- tighten(nn[1]);
+ tighten(nn); // Not sure this is required.
return nn;
}
else if ((nn[1].children.size() >= minEntries)
&& (nn[0].children.size() + cc.size() == minEntries))
{
nn[0].children.addAll(cc);
cc.clear();
- tighten(nn[0]); // Not sure this is required.
- tighten(nn[1]);
+ tighten(nn); // Not sure this is required.
return nn;
}
- Node c = cc.pop();
+ Node c = seedPicker == SeedPicker.LINEAR ? lPickNext(cc) : qPickNext(cc, nn);
Node preferred;
- // Implementation of linear PickNext
float e0 = getRequiredExpansion(nn[0].coords, nn[0].dimensions, c);
float e1 = getRequiredExpansion(nn[1].coords, nn[1].dimensions, c);
if (e0 < e1)
@@ -450,16 +461,75 @@ else if (nn[0].children.size() > nn[1].children.size())
}
}
preferred.children.add(c);
+ tighten(preferred);
}
- tighten(nn[0]);
- tighten(nn[1]);
return nn;
}
+ // Implementation of Quadratic PickSeeds
+ private RTree<T>.Node[] qPickSeeds(LinkedList<Node> nn)
+ {
+ @SuppressWarnings("unchecked")
+ RTree<T>.Node[] bestPair = new RTree.Node[2];
+ float maxWaste = -1.0f * Float.MAX_VALUE;
+ for (Node n1: nn)
+ {
+ for (Node n2: nn)
+ {
+ if (n1 == n2) continue;
+ float n1a = getArea(n1.dimensions);
+ float n2a = getArea(n2.dimensions);
+ float ja = 1.0f;
+ for ( int i = 0; i < numDims; i++ )
+ {
+ float jc0 = Math.min(n1.coords[i], n2.coords[i]);
+ float jc1 = Math.max(n1.coords[i] + n1.dimensions[i], n2.coords[i] + n2.dimensions[i]);
+ ja *= (jc1 - jc0);
+ }
+ float waste = ja - n1a - n2a;
+ if ( waste > maxWaste )
+ {
+ maxWaste = waste;
+ bestPair[0] = n1;
+ bestPair[1] = n2;
+ }
+ }
+ }
+ nn.remove(bestPair[0]);
+ nn.remove(bestPair[1]);
+ return bestPair;
+ }
+
+ /**
+ * Implementation of QuadraticPickNext
+ * @param cc the children to be divided between the new nodes, one item will be removed from this list.
+ * @param nn the candidate nodes for the children to be added to.
+ */
+ private Node qPickNext(LinkedList<Node> cc, Node[] nn)
+ {
+ float maxDiff = -1.0f * Float.MAX_VALUE;
+ Node nextC = null;
+ for ( Node c: cc )
+ {
+ float n0Exp = getRequiredExpansion(nn[0].coords, nn[0].dimensions, c);
+ float n1Exp = getRequiredExpansion(nn[1].coords, nn[1].dimensions, c);
+ float diff = Math.abs(n1Exp - n0Exp);
+ if (diff > maxDiff)
+ {
+ maxDiff = diff;
+ nextC = c;
+ }
+ }
+ assert (nextC != null) : "No node selected from qPickNext";
+ cc.remove(nextC);
+ return nextC;
+ }
+
// Implementation of LinearPickSeeds
- private RTree<T>.Node[] pickSeeds(LinkedList<Node> nn)
+ private RTree<T>.Node[] lPickSeeds(LinkedList<Node> nn)
{
- RTree<T>.Node[] bestPair = null;
+ @SuppressWarnings("unchecked")
+ RTree<T>.Node[] bestPair = new RTree.Node[2];
float bestSep = 0.0f;
for (int i = 0; i < numDims; i++)
{
@@ -491,8 +561,8 @@ else if (nn[0].children.size() > nn[1].children.size())
Math.abs((dimMinUb - dimMaxLb) / (dimUb - dimLb));
if (sep >= bestSep)
{
- bestPair = new RTree.Node[]
- { nMaxLb, nMinUb };
+ bestPair[0] = nMaxLb;
+ bestPair[1] = nMinUb;
bestSep = sep;
}
}
@@ -508,39 +578,51 @@ else if (nn[0].children.size() > nn[1].children.size())
return bestPair;
}
- private void tighten(Node n)
+ /**
+ * Implementation of LinearPickNext
+ * @param cc the children to be divided between the new nodes, one item will be removed from this list.
+ */
+ private Node lPickNext(LinkedList<Node> cc)
{
- assert(n.children.size() > 0) : "tighten() called on empty node!";
- float[] minCoords = new float[numDims];
- float[] maxCoords = new float[numDims];
- for (int i = 0; i < numDims; i++)
- {
- minCoords[i] = Float.MAX_VALUE;
- maxCoords[i] = Float.MIN_VALUE;
+ return cc.pop();
+ }
- for (Node c : n.children)
+ private void tighten(Node... nodes)
+ {
+ assert(nodes.length >= 1): "Pass some nodes to tighten!";
+ for (Node n: nodes) {
+ assert(n.children.size() > 0) : "tighten() called on empty node!";
+ float[] minCoords = new float[numDims];
+ float[] maxCoords = new float[numDims];
+ for (int i = 0; i < numDims; i++)
{
- // we may have bulk-added a bunch of children to a node (eg. in
- // splitNode)
- // so here we just enforce the child->parent relationship.
- c.parent = n;
- if (c.coords[i] < minCoords[i])
- {
- minCoords[i] = c.coords[i];
- }
- if ((c.coords[i] + c.dimensions[i]) > maxCoords[i])
+ minCoords[i] = Float.MAX_VALUE;
+ maxCoords[i] = Float.MIN_VALUE;
+
+ for (Node c : n.children)
{
- maxCoords[i] = (c.coords[i] + c.dimensions[i]);
+ // we may have bulk-added a bunch of children to a node (eg. in
+ // splitNode)
+ // so here we just enforce the child->parent relationship.
+ c.parent = n;
+ if (c.coords[i] < minCoords[i])
+ {
+ minCoords[i] = c.coords[i];
+ }
+ if ((c.coords[i] + c.dimensions[i]) > maxCoords[i])
+ {
+ maxCoords[i] = (c.coords[i] + c.dimensions[i]);
+ }
}
}
+ for (int i = 0; i < numDims; i++)
+ {
+ // Convert max coords to dimensions
+ maxCoords[i] -= minCoords[i];
+ }
+ System.arraycopy(minCoords, 0, n.coords, 0, numDims);
+ System.arraycopy(maxCoords, 0, n.dimensions, 0, numDims);
}
- for (int i = 0; i < numDims; i++)
- {
- // Convert max coords to dimensions
- maxCoords[i] -= minCoords[i];
- }
- System.arraycopy(minCoords, 0, n.coords, 0, numDims);
- System.arraycopy(maxCoords, 0, n.dimensions, 0, numDims);
}
private RTree<T>.Node chooseLeaf(RTree<T>.Node n, RTree<T>.Entry e)
@@ -93,10 +93,10 @@ public void testEmptyResults()
@Test
public void testSplitNodesSmall()
{
- RTree<Object> rt = new RTree(2,1,2);
+ RTree<Object> rt = new RTree(2,1,2, RTree.SeedPicker.QUADRATIC);
float[][] coords = new float[][] { {0.0f, 0.0f}, {1.0f, 1.0f}, {2.0f, 2.0f}, {3.0f, 3.0f} };
float[] dims = new float[]{0.5f, 0.5f};
- Object[] entries = new Object[] { new Object(), new Object(), new Object(), new Object() };
+ Object[] entries = new Object[] { 0, 1, 2, 3 };
for (int i = 0; i < entries.length; i++ )
{
rt.insert(coords[i], dims, entries[i]);
@@ -264,10 +264,10 @@ public void testInsertDelete() {
for ( int j = 0; j < 500; j++ )
{
- RTree<Integer> tree = new RTree<Integer>(10, 2, 3);
+ RTree<Integer> tree = new RTree<Integer>(10, 2, 3, RTree.SeedPicker.LINEAR);
List<DataObject> rects = new ArrayList<DataObject>();
- for (int i = 0; i < 100; i++) {
+ for (int i = 0; i < 150; i++) {
rects.add(new DataObject(
new float[]{i, i * 2, i * 3},
new float[]{0, 0, 0},
@@ -276,7 +276,7 @@ public void testInsertDelete() {
tree.insert(dataObject.val, dataObject.dim, dataObject.id);
}
- for (int i = 0; i < 100; i++) {
+ for (int i = 0; i < 150; i++) {
DataObject dataObject = rects.get(i);
boolean deleted = tree.delete(dataObject.val, dataObject.dim, dataObject.id);
assert deleted;

0 comments on commit 25ebc2e

Please sign in to comment.