/** * {@link VpTree Vantage point} tree implementation for nearest neighbor search. Slower than KD-Tree on * random data, but faster than it for some pathological cases. * * @see VpTree * * @param randSeed Random seed * @return {@link NearestNeighbor} implementation */ public static NearestNeighbor<double[]> vptree( long randSeed ) { return new VpTree(randSeed); } }
/** * Builds the tree from a set of points by recursively partitioning * them according to a random pivot. * @param lower start of range * @param upper end of range (exclusive) * @return root of the tree or null if lower == upper */ private Node buildFromPoints(int lower, int upper) { if (upper == lower) { return null; } final Node node = new Node(); node.index = lower; if (upper - lower > 1) { // choose an arbitrary vantage point and move it to the start int i = random.nextInt(upper - lower - 1) + lower; listSwap(items, lower, i); listSwap(indexes, lower, i); int median = (upper + lower + 1) / 2; // partition around the median distance // TODO: use the QuickSelect class? nthElement(lower + 1, upper, median, items[lower]); // what was the median? node.threshold = distance(items[lower], items[median]); node.index = lower; node.left = buildFromPoints(lower + 1, median); node.right = buildFromPoints(median, upper); } return node; }
/** * Ensures that the n-th element is in a correct position in the list based on * the distance from origin. * @param left start of range * @param right end of range (exclusive) * @param n element to put in the right position * @param origin origin to compute the distance to */ private void nthElement(int left, int right, int n, double[] origin) { int npos = partitionItems(left, right, n, origin); if (npos < n) nthElement(npos + 1, right, n, origin); if (npos > n) nthElement(left, npos, n, origin); }
/** * Partition the points based on their distance to origin around the selected pivot. * @param left range start * @param right range end (exclusive) * @param pivot pivot for the partition * @param origin origin to compute the distance to * @return index of the pivot */ private int partitionItems(int left, int right, int pivot, double[] origin) { double pivotDistance = distance(origin, items[pivot]); listSwap(items, pivot, right - 1); listSwap(indexes, pivot, right - 1); int storeIndex = left; for (int i = left; i < right - 1; i++) { if (distance(origin, items[i]) <= pivotDistance) { listSwap(items, i, storeIndex); listSwap(indexes, i, storeIndex); storeIndex++; } } listSwap(items, storeIndex, right - 1); listSwap(indexes, storeIndex, right - 1); return storeIndex; }
final double dist = distance(items[node.index], target);
@SuppressWarnings("unchecked") @Override public void setPoints(List<double[]> points, boolean trackIndicies) { // Make a copy because we mutate the lists this.items = points.toArray(new double[0][]); this.indexes = new GrowQueue_I32(); indexes.resize(points.size()); for (int i = 0; i < points.size(); i++) { indexes.data[i] = i; } this.root = buildFromPoints(0, items.length); }
@Override public boolean findNearest(double[] point, double maxDistance, NnData<double[]> result) { boolean r = searchNearest(point, maxDistance < 0 ? Double.POSITIVE_INFINITY : Math.sqrt(maxDistance), result); result.distance *= result.distance; // Callee expects squared distance return r; }
@Override public void findNearest(double[] target, double maxDistance, int numNeighbors, FastQueue<NnData<double[]>> results) { results.reset(); PriorityQueue<HeapItem> heap = search(target, maxDistance < 0 ? Double.POSITIVE_INFINITY : Math.sqrt(maxDistance), numNeighbors); while (!heap.isEmpty()) { final HeapItem heapItem = heap.poll(); NnData<double[]> objects = new NnData<>(); objects.index = indexes.get(heapItem.index); objects.point = items[heapItem.index]; objects.distance = heapItem.dist * heapItem.dist; // squared distance is expected results.add(objects); } results.reverse(); }
/** * Partition the points based on their distance to origin around the selected pivot. * @param left range start * @param right range end (exclusive) * @param pivot pivot for the partition * @param origin origin to compute the distance to * @return index of the pivot */ private int partitionItems(int left, int right, int pivot, double[] origin) { double pivotDistance = distance(origin, items[pivot]); listSwap(items, pivot, right - 1); listSwap(indexes, pivot, right - 1); int storeIndex = left; for (int i = left; i < right - 1; i++) { if (distance(origin, items[i]) <= pivotDistance) { listSwap(items, i, storeIndex); listSwap(indexes, i, storeIndex); storeIndex++; } } listSwap(items, storeIndex, right - 1); listSwap(indexes, storeIndex, right - 1); return storeIndex; }
final double dist = distance(items[node.index], target);
@SuppressWarnings("unchecked") @Override public void setPoints(List<double[]> points, boolean trackIndicies) { // Make a copy because we mutate the lists this.items = points.toArray(new double[0][]); this.indexes = new GrowQueue_I32(); indexes.resize(points.size()); for (int i = 0; i < points.size(); i++) { indexes.data[i] = i; } this.root = buildFromPoints(0, items.length); }
@Override public boolean findNearest(double[] point, double maxDistance, NnData<double[]> result) { boolean r = searchNearest(point, maxDistance < 0 ? Double.POSITIVE_INFINITY : Math.sqrt(maxDistance), result); result.distance *= result.distance; // Callee expects squared distance return r; }
@Override public void findNearest(double[] target, double maxDistance, int numNeighbors, FastQueue<NnData<double[]>> results) { results.reset(); PriorityQueue<HeapItem> heap = search(target, maxDistance < 0 ? Double.POSITIVE_INFINITY : Math.sqrt(maxDistance), numNeighbors); while (!heap.isEmpty()) { final HeapItem heapItem = heap.poll(); NnData<double[]> objects = new NnData<>(); objects.index = indexes.get(heapItem.index); objects.point = items[heapItem.index]; objects.distance = heapItem.dist * heapItem.dist; // squared distance is expected results.add(objects); } results.reverse(); }
/** * Builds the tree from a set of points by recursively partitioning * them according to a random pivot. * @param lower start of range * @param upper end of range (exclusive) * @return root of the tree or null if lower == upper */ private Node buildFromPoints(int lower, int upper) { if (upper == lower) { return null; } final Node node = new Node(); node.index = lower; if (upper - lower > 1) { // choose an arbitrary vantage point and move it to the start int i = random.nextInt(upper - lower - 1) + lower; listSwap(items, lower, i); listSwap(indexes, lower, i); int median = (upper + lower + 1) / 2; // partition around the median distance // TODO: use the QuickSelect class? nthElement(lower + 1, upper, median, items[lower]); // what was the median? node.threshold = distance(items[lower], items[median]); node.index = lower; node.left = buildFromPoints(lower + 1, median); node.right = buildFromPoints(median, upper); } return node; }
/** * Ensures that the n-th element is in a correct position in the list based on * the distance from origin. * @param left start of range * @param right end of range (exclusive) * @param n element to put in the right position * @param origin origin to compute the distance to */ private void nthElement(int left, int right, int n, double[] origin) { int npos = partitionItems(left, right, n, origin); if (npos < n) nthElement(npos + 1, right, n, origin); if (npos > n) nthElement(left, npos, n, origin); }
final Node node = nodes.getTail(); nodes.removeTail(); final double dist = distance(items[node.index], target);
/** * {@link VpTree Vantage point} tree implementation for nearest neighbor search. Slower than KD-Tree on * random data, but faster than it for some pathological cases. * * @see VpTree * * @param randSeed Random seed * @return {@link NearestNeighbor} implementation */ public static NearestNeighbor<double[]> vptree( long randSeed ) { return new VpTree(randSeed); } }
final Node node = nodes.getTail(); nodes.removeTail(); final double dist = distance(items[node.index], target);