jidt/java/source/infodynamics/utils/KdTree.java

3608 lines
141 KiB
Java

/*
* Java Information Dynamics Toolkit (JIDT)
* Copyright (C) 2012, Joseph T. Lizier
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package infodynamics.utils;
import java.util.Collection;
import java.util.PriorityQueue;
import java.util.Vector;
/**
* K-d tree implementation to be used for fast neighbour searching
* across several (multi-dimensional) variables.
* Norms for the nearest neighbour searches are the max norm between
* the (multi-dimensional) variables, and either max norm or Euclidean
* norm (squared) within each variable.
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*
* @see <a href="http://en.wikipedia.org/wiki/K-d_tree">K-d tree page on wikipedia</a>
*/
public class KdTree extends NearestNeighbourSearcher {
/**
* Cached reference to the data the tree is constructed from.
* We have an array of double[][] 2D arrays -- each 2D array
* originalDataSets[i] is
* considered as a separate (multivariate) variable (indexed by sample
* number then dimension number), whilst the set of all such
* arrays is considered a joint variable of multivariates.
*/
protected double[][][] originalDataSets;
/**
* For each dimension dim, sortedArrayIndices[dim] is an array
* of indices to the data in sourceObservations and destObservations,
* sorted in order (min to max) for the dimension dim;
* plus we have a spare dimension for a temporary array.
* This is only used in the construction of the kd tree.
*/
protected int[][] masterSortedArrayIndices = null;
/**
* Maps dimension number (first index) to which
* double[][] array holds the data for this dimension,
* and which index we use in that array (dimensionToArrayIndex).
*
* I.e. for a dimension number d (out of the joint variables
* across all the multivariates in originalDataSets),
* dimensionToArray[d] points to the relevant originalDataSets[i]
* multivariate, while dimensionToArrayIndex[d] tells us which
* variable within originalDataSets[i] to use, i.e. the time series
* originalDataSets[i][t][dimensionToArrayIndex[d]] for time variable t
* is the relevant time-series for dimension number d.
*/
protected double[][][] dimensionToArray = null;
protected int[] dimensionToArrayIndex = null;
/**
* Variable at index i in the full joint set
* corresponds to high level variable
* dimensionToVariableNumber[i]; i.e. the
* originalDataSets[dimensionToVariableNumber[i]] 2D array.
*/
protected int[] dimensionToVariableNumber = null;
protected int totalDimensions = 0;
/**
* The root node for this k-d tree
*/
protected KdTreeNode rootNode = null;
/**
* Protected class to implement nodes of a k-d tree
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
protected class KdTreeNode {
protected int indexOfThisPoint;
protected KdTreeNode leftTree;
protected KdTreeNode rightTree;
protected KdTreeNode(int indexOfThisPoint, KdTreeNode leftTree,
KdTreeNode rightTree) {
this.indexOfThisPoint = indexOfThisPoint;
this.leftTree = leftTree;
this.rightTree = rightTree;
}
}
/**
* Construct the k-d tree from a set of double[][] data.
*
* @param data a double[][] 2D data set, first indexed
* by time, second index by variable number.
*/
public KdTree(double[][] data) {
this(new int[] {data[0].length}, new double[][][] {data});
}
/**
* Construct the k-d tree from a <b>set</b> of double[][] data,
* considered jointly.
*
* @param dimensions an array of dimensions for each
* of the 2D data sets.
* @param data an array of double[][] 2D data sets
* for each data[i]
* (where i is the main variable number within data, then
* after that the first index is sample number, second is dimension
* within this data set)
*/
public KdTree(int[] dimensions, double[][][] data) {
this.originalDataSets = data;
int numObservations = data[0].length;
// First work out how many dimensions we have in total:
totalDimensions = 0;
for (int i = 0; i < dimensions.length; i++) {
totalDimensions += dimensions[i];
}
// Cache which array and the index within that array
// are used for each dimension of the data
dimensionToArray = new double[totalDimensions][][];
dimensionToArrayIndex = new int[totalDimensions];
dimensionToVariableNumber = new int[totalDimensions];
int cumulativeDimension = 0;
int cumulativeDimsionsForPreviousArray = 0;
for (int i = 0; i < dimensions.length; i++) {
int dimensionsForThisVariable = dimensions[i];
for (int j = 0; j < dimensionsForThisVariable; j++) {
dimensionToArray[cumulativeDimension] = data[i];
dimensionToVariableNumber[cumulativeDimension] = i;
dimensionToArrayIndex[cumulativeDimension]
= cumulativeDimension - cumulativeDimsionsForPreviousArray;
cumulativeDimension++;
}
cumulativeDimsionsForPreviousArray = cumulativeDimension;
}
// Sort the original data sets in each dimension:
double[][] thisDimensionsData = new double[numObservations][2];
// Create storage for sorted arrays, plus a shared spare temporary
// array
masterSortedArrayIndices = new int[totalDimensions+1][numObservations];
// Sort all the data first
for (int i = 0; i < totalDimensions; i++) {
// Extract the data for this dimension:
double[][] fullData = dimensionToArray[i];
MatrixUtils.arrayCopy(fullData, 0, dimensionToArrayIndex[i],
thisDimensionsData, 0, 0, numObservations, 1);
// Record original time indices:
for (int t = 0; t < numObservations; t++) {
thisDimensionsData[t][1] = t;
}
// Sort the data:
java.util.Arrays.sort(thisDimensionsData, FirstIndexComparatorDouble.getInstance());
// And extract the sorted indices:
for (int t = 0; t < numObservations; t++) {
masterSortedArrayIndices[i][t] = (int) thisDimensionsData[t][1];
}
}
// Construct the k-d tree:masterSortedArrayIndices
rootNode = constructKdTree(0, 0, numObservations, masterSortedArrayIndices);
// And destroy the temporary storage of sorted array indices:
masterSortedArrayIndices = null;
}
/**
*
* @param currentDim the dimension that we're currently working with
* @param startPoint the index of the first point for us to add here,
* in the sorted array of points for currentDim
* @param numPoints the number of points for us to add here,
* in the sorted array of points for currentDim
* @param sortedArrayIndices for each dimension dim, sortedArrayIndices[dim] is an array
* of indices to the data in sourceObservations and destObservations,
* sorted in order (min to max) for the dimension dim. This is only valid
* between startPoint and startPoint + numPoints-1 however; nothing outside
* this should be touched. There is one extra dimension here, which may be used
* as a temporary array (though again, only between startPoint and
* startPoint + numPoints-1 should be touched).
* @return
*/
protected KdTreeNode constructKdTree(int currentDim, int startPoint, int numPoints,
int[][] sortedArrayIndices) {
// Precondition: sortedArrayIndices[][] are currently sorted for all
// dimensions
// Handle non-recursive solutions:
if (numPoints == 0) {
return null;
}
if (numPoints == 1) {
return new KdTreeNode(sortedArrayIndices[currentDim][startPoint], null, null);
}
if (numPoints == 2) {
// Make the first point the splitting point, in case
// the two are equal in this dimension, then the left side definitely
// contains a strictly less than branch.
return new KdTreeNode(sortedArrayIndices[currentDim][startPoint], null,
new KdTreeNode(sortedArrayIndices[currentDim][startPoint+1], null, null));
}
// Point to the correct array for the data
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
// Identify the point on which to split here
int candidateSplitPoint = startPoint + numPoints/2;
while ((candidateSplitPoint > startPoint) &&
(data[sortedArrayIndices[currentDim][candidateSplitPoint-1]][actualDim] ==
data[sortedArrayIndices[currentDim][candidateSplitPoint]][actualDim])) {
// The adjoining data points are equal in this dimension, so continue searching
// for the median with no left points less than it
candidateSplitPoint--;
}
// Postcondition: candidateSplitPoint holds our new median point
double medianInActualDim = data[sortedArrayIndices[currentDim][candidateSplitPoint]][actualDim];
int sampleNumberForSplitPoint = sortedArrayIndices[currentDim][candidateSplitPoint];
int leftStart = startPoint;
int leftNumPoints = candidateSplitPoint-startPoint;
int rightStart = candidateSplitPoint+1;
int rightNumPoints = startPoint+numPoints-1-candidateSplitPoint;
// Partition the other dimensions properly.
int[][] newSortedArrayIndices = new int[totalDimensions+1][];
// Grab the temporary array for us to use:
int[] tempSortedIndices = sortedArrayIndices[totalDimensions];
for (int dim = 0; dim < totalDimensions; dim++) {
if (dim == currentDim) {
newSortedArrayIndices[dim] = sortedArrayIndices[dim];
continue;
}
int leftIndex = leftStart, rightIndex = rightStart;
for (int i = startPoint; i < startPoint + numPoints; i++) {
int sampleNumberInData = sortedArrayIndices[dim][i];
if (sampleNumberInData == sampleNumberForSplitPoint) {
// This is the split point
continue;
}
// Check if this data point
// was going to the left or right tree
if (data[sampleNumberInData][actualDim] < medianInActualDim) {
// This point will be in the left tree
tempSortedIndices[leftIndex++] = sampleNumberInData;
} else {
// This point will be in the right tree
tempSortedIndices[rightIndex++] = sampleNumberInData;
}
}
// Check that we have not exceeded boundaries for either
// left or right sets:
// Could remove these since the code is now functional,
// but may be better to leave them in just in case the code breaks:
if (leftIndex > leftStart + leftNumPoints) {
throw new RuntimeException("Exceeded expected number of points on left - likely had an NaN in the data");
}
if (rightIndex > rightStart + rightNumPoints) {
throw new RuntimeException("Exceeded expected number of points on right - likely had an NaN in the data");
}
// Update the pointer for the sorted indices for this dimension,
// and keep the new temporary array
int[] temp = sortedArrayIndices[dim]; // Old array to become tempSortedIndices
newSortedArrayIndices[dim] = tempSortedIndices;
tempSortedIndices = temp;
}
newSortedArrayIndices[totalDimensions] = tempSortedIndices;
int newDim = (currentDim + 1) % totalDimensions;
return new KdTreeNode(sampleNumberForSplitPoint,
constructKdTree(newDim, leftStart, leftNumPoints, newSortedArrayIndices),
constructKdTree(newDim, rightStart, rightNumPoints, newSortedArrayIndices));
}
@Override
public void setNormType(int normType) {
super.setNormType(normType);
}
@Override
public void setNormType(String normTypeString) {
super.setNormType(normTypeString);
}
/**
* Computing the configured norm between vectors x1 and x2.
* Adding here instead of using {@link EuclideanUtils#norm(double[], double[])}
* to attempt speed-up.
* Also hoping this method is inlined by the JVM, but haven't checked this.
*
* @param x1 vector of doubles
* @param x2 vector of doubles
* @return the selected norm
*/
public final static double norm(double[] x1, double[] x2, int normToUse) {
double distance = 0.0;
switch (normToUse) {
case EuclideanUtils.NORM_MAX_NORM:
// Inlined from {@link EuclideanUtils}:
for (int d = 0; d < x1.length; d++) {
double difference = x1[d] - x2[d];
// Take the abs
if (difference < 0) {
difference = -difference;
}
if (difference > distance) {
distance = difference;
}
}
return distance;
// case EuclideanUtils.NORM_EUCLIDEAN_SQUARED:
default:
// Inlined from {@link EuclideanUtils}:
for (int d = 0; d < x1.length; d++) {
double difference = x1[d] - x2[d];
distance += difference * difference;
}
return distance;
}
}
/**
* Computing the configured norm between vectors x1 and x2; if
* it becomes clear that norm will be larger than limit,
* then return Double.POSITIVE_INFINITY immediately.
*
* <p>Adding here instead of using {@link EuclideanUtils#normWithAbort(double[], double[], double)}
* to attempt speed-up.
* Also hoping this method is inlined by the JVM, but haven't checked this.</p>
*
* @param x1 vector 1 of doubles
* @param x2 vector 2 of doubles
* @param limit if it becomes clear that norm will be larger than limit,
* then return Double.POSITIVE_INFINITY immediately.
* @param normToUse which norm to use, as defined by {@link #setNormType(int)}
* @return the selected norm
*/
public final static double normWithAbort(double[] x1, double[] x2,
double limit, int normToUse) {
double distance = 0.0;
switch (normToUse) {
case EuclideanUtils.NORM_MAX_NORM:
// Inlined from {@link EuclideanUtils}:
for (int d = 0; d < x1.length; d++) {
double difference = x1[d] - x2[d];
// Take the abs
if (difference < 0) {
difference = -difference;
}
if (difference > distance) {
if (difference > limit) {
return Double.POSITIVE_INFINITY;
}
distance = difference;
}
}
return distance;
// case EuclideanUtils.NORM_EUCLIDEAN_SQUARED:
default:
// Inlined from {@link EuclideanUtils}:
for (int d = 0; d < x1.length; d++) {
double difference = x1[d] - x2[d];
distance += difference * difference;
if (distance > limit) {
return Double.POSITIVE_INFINITY;
}
}
return distance;
}
}
@Override
public NeighbourNodeData findNearestNeighbour(int sampleIndex) {
if (rootNode == null) {
return null;
}
return findNearestNeighbour(sampleIndex, rootNode, 0, null);
}
/**
* Find the nearest neighbour to a given sample (sampleIndex), in the tree
* rooted at node (which is at the specified level in the tree), or
* return currentBest if no better match is found.
* Nearest neighbour is a max norm between the high-level variables,
* with norm for each variable being the specified norm.
*
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param currentBest a NeighbourNodeData structure capturing the current
* closest neighbour and its distance
* @return the node data for the nearest neighbour.
*/
protected NeighbourNodeData findNearestNeighbour(int sampleIndex,
KdTreeNode node, int level, NeighbourNodeData currentBest) {
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance (this saves taking square roots anywhere)
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if ((node.indexOfThisPoint != sampleIndex) &&
((currentBest == null) || (absDistOnThisDim < currentBest.distance))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should become our
// currentBest properly.
double maxNorm = 0;
double[] norms = new double[originalDataSets.length];
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
if (currentBest == null) {
norms[v] = norm(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint], normTypeToUse);
} else {
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than currentBest.distance:
norms[v] = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
currentBest.distance, normTypeToUse);
}
if (norms[v] > maxNorm) {
maxNorm = norms[v];
if (Double.isInfinite(maxNorm)) {
// we've aborted the norm check early;
// no point checking the other variables.
break;
}
}
}
if ((currentBest == null) ||
(maxNorm < currentBest.distance)) {
// We set this as the current nearest neighbour:
currentBest = new NeighbourNodeData(node.indexOfThisPoint,
norms, maxNorm);
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
currentBest = findNearestNeighbour(sampleIndex, closestSubTree,
level + 1, currentBest);
}
if ((currentBest == null) || (absDistOnThisDim < currentBest.distance)) {
// It's possible we could have a closer node than the current best
// in the other branch as well, so search there too.
// (It's highly unlikely we would still have (currentBest == null)
// here but remains possible, so we check that)
if (furthestSubTree != null) {
currentBest = findNearestNeighbour(sampleIndex, furthestSubTree,
level + 1, currentBest);
}
}
return currentBest;
}
@Override
public PriorityQueue<NeighbourNodeData>
findKNearestNeighbours(int K, int sampleIndex) throws Exception {
if (originalDataSets[0].length <= K) {
throw new Exception("Not enough data points for a K nearest neighbours search");
}
PriorityQueue<NeighbourNodeData> pq = new PriorityQueue<NeighbourNodeData>(K);
if (rootNode == null) {
return pq;
}
findKNearestNeighbours(K, sampleIndex, rootNode, 0, pq);
return pq;
}
@Override
public PriorityQueue<NeighbourNodeData>
findKNearestNeighbours(int K, int sampleIndex,
int dynCorrExclTime) throws Exception {
if (originalDataSets[0].length <= K + 2*dynCorrExclTime) {
throw new Exception("Not enough data points for a K nearest neighbours search" +
" with dynamic exclusion window of " + dynCorrExclTime + " points either side");
}
PriorityQueue<NeighbourNodeData> pq = new PriorityQueue<NeighbourNodeData>(K);
if (rootNode == null) {
return pq;
}
findKNearestNeighbours(K, sampleIndex, dynCorrExclTime, rootNode, 0, pq);
return pq;
}
/**
* Protected method to Update the k nearest neighbours to a given sample (sampleIndex), from the tree
* rooted at node (which is at the specified level in the tree).
* Incorporate neighbours found in this sub-tree into the PriorityQueue
* of the current K closest.
* @param K the number of nearest neighbour
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param currentKBest a PriorityQueue of NeighbourNodeData objects
* capturing the current K closest neighbours and their distances.
* Assumed not to be null, but may be empty or with less than K elements
* so far. It must be sorted from furthest away first to nearest last.
*/
protected void findKNearestNeighbours(int K,
int sampleIndex, KdTreeNode node, int level,
PriorityQueue<NeighbourNodeData> currentKBest) {
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance (this saves taking square roots anywhere)
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
// Grab the current furthest nearest neighbour in our cached list
// (will not throw an Exception if the PQ is empty)
NeighbourNodeData furthestCached = currentKBest.peek();
if ((node.indexOfThisPoint != sampleIndex) &&
((currentKBest.size() < K) || (absDistOnThisDim < furthestCached.distance))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be
// added to our currentKBest properly.
double maxNorm = 0;
double[] norms = new double[originalDataSets.length];
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
if (currentKBest.size() < K) {
norms[v] = norm(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint], normTypeToUse);
} else {
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than currentBest.distance:
norms[v] = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
furthestCached.distance, normTypeToUse);
}
if (norms[v] > maxNorm) {
maxNorm = norms[v];
if (Double.isInfinite(maxNorm)) {
// we've aborted the norm check early;
// no point checking the other variables.
break;
}
}
}
if ((currentKBest.size() < K) ||
(maxNorm < furthestCached.distance)) {
// We add this to our cache of K nearest neighbours:
if (currentKBest.size() == K) {
// Remove the current Kth nearest neighbour
// as it is about to be replaced.
currentKBest.poll();
}
currentKBest.add(new NeighbourNodeData(node.indexOfThisPoint,
norms, maxNorm));
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
findKNearestNeighbours(K, sampleIndex,
closestSubTree, level + 1, currentKBest);
}
// Grab the current furthest nearest neighbour in our cached list again
// (will not throw an Exception if the PQ is empty)
// as it may have been changed above:
furthestCached = currentKBest.peek();
if ((currentKBest.size() < K) || (absDistOnThisDim < furthestCached.distance)) {
// It's possible we could have a closer node than the current best
// in the other branch as well, so search there too:
if (furthestSubTree != null) {
findKNearestNeighbours(K, sampleIndex,
furthestSubTree, level + 1, currentKBest);
}
}
}
/**
* Protected method to Update the k nearest neighbours to a given sample (sampleIndex), from the tree
* rooted at node (which is at the specified level in the tree).
* Incorporate neighbours found in this sub-tree into the PriorityQueue
* of the current K closest.
* Make sure to exclude any points within dynCorrExclTime of sampleIndex
* from being included in the k NNs.
*
* @param K the number of nearest neighbour
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param dynCorrExclTime size of dynamic correlation exclusion time window
* on either side of sampleIndex. 0 means exclude only sampleIndex itself.
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param currentKBest a PriorityQueue of NeighbourNodeData objects
* capturing the current K closest neighbours and their distances.
* Assumed not to be null, but may be empty or with less than K elements
* so far. It must be sorted from furthest away first to nearest last.
*/
protected void findKNearestNeighbours(int K,
int sampleIndex, int dynCorrExclTime, KdTreeNode node, int level,
PriorityQueue<NeighbourNodeData> currentKBest) {
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance (this saves taking square roots anywhere)
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
// Grab the current furthest nearest neighbour in our cached list
// (will not throw an Exception if the PQ is empty)
NeighbourNodeData furthestCached = currentKBest.peek();
if (((node.indexOfThisPoint - sampleIndex > dynCorrExclTime)
|| (node.indexOfThisPoint - sampleIndex < -dynCorrExclTime)) &&
((currentKBest.size() < K) || (absDistOnThisDim < furthestCached.distance))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be
// added to our currentKBest properly.
double maxNorm = 0;
double[] norms = new double[originalDataSets.length];
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
if (currentKBest.size() < K) {
norms[v] = norm(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint], normTypeToUse);
} else {
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than currentBest.distance:
norms[v] = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
furthestCached.distance, normTypeToUse);
}
if (norms[v] > maxNorm) {
maxNorm = norms[v];
if (Double.isInfinite(maxNorm)) {
// we've aborted the norm check early;
// no point checking the other variables.
break;
}
}
}
if ((currentKBest.size() < K) ||
(maxNorm < furthestCached.distance)) {
// We add this to our cache of K nearest neighbours:
if (currentKBest.size() == K) {
// Remove the current Kth nearest neighbour
// as it is about to be replaced.
currentKBest.poll();
}
currentKBest.add(new NeighbourNodeData(node.indexOfThisPoint,
norms, maxNorm));
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
findKNearestNeighbours(K, sampleIndex, dynCorrExclTime,
closestSubTree, level + 1, currentKBest);
}
// Grab the current furthest nearest neighbour in our cached list again
// (will not throw an Exception if the PQ is empty)
// as it may have been changed above:
furthestCached = currentKBest.peek();
if ((currentKBest.size() < K) || (absDistOnThisDim < furthestCached.distance)) {
// It's possible we could have a closer node than the current best
// in the other branch as well, so search there too:
if (furthestSubTree != null) {
findKNearestNeighbours(K, sampleIndex, dynCorrExclTime,
furthestSubTree, level + 1, currentKBest);
}
}
}
public PriorityQueue<NeighbourNodeData>
findKNearestNeighbours(int K, double[][] sampleVectors) throws Exception {
if (originalDataSets[0].length <= K) {
throw new Exception("Not enough data points for a K nearest neighbours search");
}
PriorityQueue<NeighbourNodeData> pq = new PriorityQueue<NeighbourNodeData>(K);
if (rootNode == null) {
return pq;
}
findKNearestNeighbours(K, sampleVectors, rootNode, 0, pq);
return pq;
}
/**
* Protected method to Update the k nearest neighbours to a given sample (samplePoint), from the tree
* rooted at node (which is at the specified level in the tree).
* Incorporate neighbours found in this sub-tree into the PriorityQueue
* of the current K closest.
* @param K the number of nearest neighbour
* @param sampleVectors sample vectors (not necessarily in the tree) to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param currentKBest a PriorityQueue of NeighbourNodeData objects
* capturing the current K closest neighbours and their distances.
* Assumed not to be null, but may be empty or with less than K elements
* so far. It must be sorted from furthest away first to nearest last.
*/
protected void findKNearestNeighbours(int K,
double[][] sampleVectors,
KdTreeNode node, int level,
PriorityQueue<NeighbourNodeData> currentKBest) {
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
int variableNumber = dimensionToVariableNumber[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = sampleVectors[variableNumber][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance (this saves taking square roots anywhere)
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
// Grab the current furthest nearest neighbour in our cached list
// (will not throw an Exception if the PQ is empty)
NeighbourNodeData furthestCached = currentKBest.peek();
if ((currentKBest.size() < K) || (absDistOnThisDim < furthestCached.distance)) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be
// added to our currentKBest properly.
double maxNorm = 0;
double[] norms = new double[originalDataSets.length];
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
if (currentKBest.size() < K) {
norms[v] = norm(
sampleVectors[v],
originalDataSets[v][node.indexOfThisPoint], normTypeToUse);
} else {
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than currentBest.distance:
norms[v] = normWithAbort(
sampleVectors[v],
originalDataSets[v][node.indexOfThisPoint],
furthestCached.distance, normTypeToUse);
}
if (norms[v] > maxNorm) {
maxNorm = norms[v];
if (Double.isInfinite(maxNorm)) {
// we've aborted the norm check early;
// no point checking the other variables.
break;
}
}
}
if ((currentKBest.size() < K) ||
(maxNorm < furthestCached.distance)) {
// We add this to our cache of K nearest neighbours:
if (currentKBest.size() == K) {
// Remove the current Kth nearest neighbour
// as it is about to be replaced.
currentKBest.poll();
}
currentKBest.add(new NeighbourNodeData(node.indexOfThisPoint,
norms, maxNorm));
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
findKNearestNeighbours(K, sampleVectors,
closestSubTree, level + 1, currentKBest);
}
// Grab the current furthest nearest neighbour in our cached list again
// (will not throw an Exception if the PQ is empty)
// as it may have been changed above:
furthestCached = currentKBest.peek();
if ((currentKBest.size() < K) || (absDistOnThisDim < furthestCached.distance)) {
// It's possible we could have a closer node than the current best
// in the other branch as well, so search there too:
if (furthestSubTree != null) {
findKNearestNeighbours(K, sampleVectors,
furthestSubTree, level + 1, currentKBest);
}
}
}
@Override
public int countPointsWithinR(int sampleIndex, double r, boolean allowEqualToR) {
if (rootNode == null) {
return 0;
}
return countPointsWithinR(sampleIndex, rootNode, 0, r, allowEqualToR);
}
/**
* Count the number of points within radius r of a given sample (sampleIndex),
* in the tree rooted at node (which is at the specified level in the tree).
* Nearest neighbour function to compare to r is a max norm between the
* high-level variables, with norm for each variable being the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @return count of points within r
*/
protected int countPointsWithinR(int sampleIndex,
KdTreeNode node, int level, double r, boolean allowEqualToR) {
int count = 0;
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if ((node.indexOfThisPoint != sampleIndex) &&
((absDistOnThisDim < r) ||
( allowEqualToR && (absDistOnThisDim == r)))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this dimension, no point
// checking the others:
withinBounds = false;
break;
}
}
if (withinBounds) {
// This node gets counted
count++;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
count += countPointsWithinR(sampleIndex, closestSubTree,
level + 1, r, allowEqualToR);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
count += countPointsWithinR(sampleIndex, furthestSubTree,
level + 1, r, allowEqualToR);
}
}
return count;
}
@Override
public int countPointsWithinR(int sampleIndex, double r,
int dynCorrExclTime, boolean allowEqualToR) {
if (rootNode == null) {
return 0;
}
return countPointsWithinR(sampleIndex, rootNode, 0, r,
dynCorrExclTime, allowEqualToR);
}
/**
* As per {@link #countPointsWithinR(int, KdTreeNode, int, double, boolean)}
* however excludes points within dynCorrExclTime of node
* from being counted.
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param dynCorrExclTime time window within which to exclude
* points to be counted. 0 means only exclude sampleIndex.
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @return count of points within r
*/
protected int countPointsWithinR(int sampleIndex,
KdTreeNode node, int level, double r,
int dynCorrExclTime, boolean allowEqualToR) {
int count = 0;
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if ((Math.abs(node.indexOfThisPoint - sampleIndex) > dynCorrExclTime) &&
((absDistOnThisDim < r) ||
( allowEqualToR && (absDistOnThisDim == r)))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this dimension, no point
// checking the others:
withinBounds = false;
break;
}
}
if (withinBounds) {
// This node gets counted
count++;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
count += countPointsWithinR(sampleIndex, closestSubTree,
level + 1, r, dynCorrExclTime, allowEqualToR);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
count += countPointsWithinR(sampleIndex, furthestSubTree,
level + 1, r, dynCorrExclTime, allowEqualToR);
}
}
return count;
}
@Override
public int countPointsWithinR(double[][] sampleVectors, double r, boolean allowEqualToR) {
if (rootNode == null) {
return 0;
}
return countPointsWithinR(sampleVectors, rootNode, 0, r, allowEqualToR);
}
/**
* Count the number of points within radius r of a given sample (sampleVector),
* in the tree rooted at node (which is at the specified level in the tree).
* Nearest neighbour function to compare to r is a max norm between the
* high-level variables, with norm for each variable being the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* @param sampleVectors sample vectors to find the neighbours within r
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @return count of points within r
*/
protected int countPointsWithinR(double[][] sampleVectors,
KdTreeNode node, int level, double r, boolean allowEqualToR) {
int count = 0;
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
int variableNumber = dimensionToVariableNumber[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = sampleVectors[variableNumber][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if (((absDistOnThisDim < r) ||
( allowEqualToR && (absDistOnThisDim == r)))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
sampleVectors[v],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this dimension, no point
// checking the others:
withinBounds = false;
break;
}
}
if (withinBounds) {
// This node gets counted
count++;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
count += countPointsWithinR(sampleVectors, closestSubTree,
level + 1, r, allowEqualToR);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
count += countPointsWithinR(sampleVectors, furthestSubTree,
level + 1, r, allowEqualToR);
}
}
return count;
}
@Override
public Collection<NeighbourNodeData> findPointsWithinR(int sampleIndex,
double r, boolean allowEqualToR) {
Vector<NeighbourNodeData> pointsWithinR = new Vector<NeighbourNodeData>();
if (rootNode == null) {
return pointsWithinR;
}
findPointsWithinR(sampleIndex,
rootNode, 0, r, allowEqualToR, pointsWithinR);
return pointsWithinR;
}
/**
* Add to the collection of points within radius r of a given sample (sampleIndex),
* in the tree rooted at node (which is at the specified level in the tree).
* Nearest neighbour function to compare to r is a max norm between the
* high-level variables, with norm for each variable being the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param pointsWithinR the collection of points to add to
*/
protected void findPointsWithinR(int sampleIndex,
KdTreeNode node, int level, double r, boolean allowEqualToR,
Collection<NeighbourNodeData> pointsWithinR) {
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if ((node.indexOfThisPoint != sampleIndex) &&
((absDistOnThisDim < r) ||
( allowEqualToR && (absDistOnThisDim == r)))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
double[] norms = new double[originalDataSets.length];
double maxNorm = 0;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this variable, no point
// checking the others:
withinBounds = false;
break;
}
norms[v] = distForVariableV;
if (distForVariableV > maxNorm) {
maxNorm = distForVariableV;
}
}
if (withinBounds) {
// This node gets counted
pointsWithinR.add(
new NeighbourNodeData(node.indexOfThisPoint,
norms, maxNorm));
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
findPointsWithinR(sampleIndex, closestSubTree,
level + 1, r, allowEqualToR, pointsWithinR);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
findPointsWithinR(sampleIndex, furthestSubTree,
level + 1, r, allowEqualToR, pointsWithinR);
}
}
}
@Override
public int findPointsWithinR(int sampleIndex,
double r, boolean allowEqualToR, boolean[] isWithinR,
int[] indicesWithinR) {
if (rootNode == null) {
indicesWithinR[0] = -1;
return 0;
}
int currentIndexInIndicesWithinR =
findPointsWithinR(sampleIndex,
rootNode, 0, r, allowEqualToR, isWithinR,
indicesWithinR, 0);
indicesWithinR[currentIndexInIndicesWithinR] = -1;
return currentIndexInIndicesWithinR;
}
/**
* Record the collection of points within radius r of a given sample (sampleIndex),
* in the tree rooted at node (which is at the specified level in the tree).
* Nearest neighbour function to compare to r is a max norm between the
* high-level variables, with norm for each variable being the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* <p>The recording of nearest neighbours is made within the isWithinR
* and indicesWithinR arrays, which must be constructed before
* calling this method, with length at or exceeding the total
* number of data points. indicesWithinR is
* </p>
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param isWithinR the array should be passed in with all points set to
* false initially, and is return indicating whether each sample was
* found to be within r of that at sampleIndex.
* @param indicesWithinR a list of array indices
* for points marked as true in isWithinR, terminated with a -1 value.
* @param nextIndexInIndicesWithinR the next available index in indicesWithinR
* before the method.
* @return the next available index in indicesWithinR after the method is complete
*/
protected int findPointsWithinR(int sampleIndex,
KdTreeNode node, int level, double r, boolean allowEqualToR,
boolean[] isWithinR, int[] indicesWithinR,
int nextIndexInIndicesWithinR) {
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if ((node.indexOfThisPoint != sampleIndex) &&
((absDistOnThisDim < r) ||
( allowEqualToR && (absDistOnThisDim == r)))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
double[] norms = new double[originalDataSets.length];
double maxNorm = 0;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this variable, no point
// checking the others:
withinBounds = false;
break;
}
norms[v] = distForVariableV;
if (distForVariableV > maxNorm) {
maxNorm = distForVariableV;
}
}
if (withinBounds) {
// This node gets counted
isWithinR[node.indexOfThisPoint] = true;
indicesWithinR[nextIndexInIndicesWithinR++] =
node.indexOfThisPoint;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinR(sampleIndex, closestSubTree,
level + 1, r, allowEqualToR, isWithinR, indicesWithinR,
nextIndexInIndicesWithinR);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinR(sampleIndex, furthestSubTree,
level + 1, r, allowEqualToR, isWithinR, indicesWithinR,
nextIndexInIndicesWithinR);
}
}
return nextIndexInIndicesWithinR;
}
@Override
public void findPointsWithinR(int sampleIndex,
double r, int dynCorrExclTime, boolean allowEqualToR,
boolean[] isWithinR, int[] indicesWithinR) {
if (rootNode == null) {
indicesWithinR[0] = -1;
return;
}
int currentIndexInIndicesWithinR =
findPointsWithinR(sampleIndex,
rootNode, 0, r, dynCorrExclTime, allowEqualToR, isWithinR,
indicesWithinR, 0);
indicesWithinR[currentIndexInIndicesWithinR] = -1;
return;
}
/**
* As per {@link #findPointsWithinR(int, KdTreeNode, int, double, boolean, boolean[], int[], int)}
* however with dynamic correlation time specified
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param dynCorrExclTime time window within which to exclude
* points to be counted. Is >= 0. 0 means only exclude sampleIndex.
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param isWithinR the array should be passed in with all points set to
* false initially, and is return indicating whether each sample was
* found to be within r of that at sampleIndex.
* @param indicesWithinR a list of array indices
* for points marked as true in isWithinR, terminated with a -1 value.
* @param nextIndexInIndicesWithinR the next available index in indicesWithinR
* before the method.
* @return the next available index in indicesWithinR after the method is complete
*/
protected int findPointsWithinR(int sampleIndex,
KdTreeNode node, int level, double r,
int dynCorrExclTime, boolean allowEqualToR,
boolean[] isWithinR, int[] indicesWithinR,
int nextIndexInIndicesWithinR) {
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if ((Math.abs(node.indexOfThisPoint - sampleIndex) > dynCorrExclTime) &&
((absDistOnThisDim < r) ||
( allowEqualToR && (absDistOnThisDim == r)))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
double[] norms = new double[originalDataSets.length];
double maxNorm = 0;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this variable, no point
// checking the others:
withinBounds = false;
break;
}
norms[v] = distForVariableV;
if (distForVariableV > maxNorm) {
maxNorm = distForVariableV;
}
}
if (withinBounds) {
// This node gets counted
isWithinR[node.indexOfThisPoint] = true;
indicesWithinR[nextIndexInIndicesWithinR++] =
node.indexOfThisPoint;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinR(sampleIndex, closestSubTree,
level + 1, r, dynCorrExclTime, allowEqualToR, isWithinR,
indicesWithinR, nextIndexInIndicesWithinR);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinR(sampleIndex, furthestSubTree,
level + 1, r, dynCorrExclTime, allowEqualToR, isWithinR,
indicesWithinR, nextIndexInIndicesWithinR);
}
}
return nextIndexInIndicesWithinR;
}
@Override
public int findPointsWithinR(int sampleIndex, double r,
int dynCorrExclTime, boolean allowEqualToR,
boolean[] isWithinR,
double[] distancesWithinRInOrder,
double[][] distancesAndIndicesWithinR) {
if (rootNode == null) {
distancesAndIndicesWithinR[0][1] = -1;
return 0;
}
int currentIndexInIndicesWithinR =
findPointsWithinR(sampleIndex,
rootNode, 0, r, dynCorrExclTime, allowEqualToR, isWithinR,
distancesWithinRInOrder, distancesAndIndicesWithinR, 0);
distancesAndIndicesWithinR[currentIndexInIndicesWithinR][1] = -1;
return currentIndexInIndicesWithinR;
}
/**
* As per {@link #findPointsWithinR(int, KdTreeNode, int, double, boolean, boolean[], int[], int)}
* however with dynamic correlation time specified, and
* returning distances to neighbours as well as neighbour indices.
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param dynCorrExclTime time window within which to exclude
* points to be counted. Is >= 0. 0 means only exclude sampleIndex.
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param isWithinR the array should be passed in with all points set to
* false initially, and is return indicating whether each sample was
* found to be within r of that at sampleIndex.
* @param distancesWithinRInOrder the array must be passed in and is
* returned with distances for each point found to be within r. Values
* at other indices are not defined.
* @param distancesAndIndicesWithinR a list of distances (in column index 0)
* and array indices (in column index 1)
* for points marked as true in isWithinR, terminated with a -1 value on the index.
* @param nextIndexInIndicesWithinR the next available index in indicesWithinR
* before the method.
* @return the next available index in distancesAndIndicesWithinR after the method is complete
*/
protected int findPointsWithinR(int sampleIndex,
KdTreeNode node, int level, double r,
int dynCorrExclTime, boolean allowEqualToR,
boolean[] isWithinR,
double[] distancesWithinRInOrder,
double[][] distancesAndIndicesWithinR,
int nextIndexInIndicesWithinR) {
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if ((Math.abs(node.indexOfThisPoint - sampleIndex) > dynCorrExclTime) &&
((absDistOnThisDim < r) ||
( allowEqualToR && (absDistOnThisDim == r)))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
double[] norms = new double[originalDataSets.length];
double maxNorm = 0;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this variable, no point
// checking the others:
withinBounds = false;
break;
}
norms[v] = distForVariableV;
if (distForVariableV > maxNorm) {
maxNorm = distForVariableV;
}
}
if (withinBounds) {
// This node gets counted
isWithinR[node.indexOfThisPoint] = true;
distancesWithinRInOrder[node.indexOfThisPoint] = maxNorm;
distancesAndIndicesWithinR[nextIndexInIndicesWithinR][0] =
maxNorm;
distancesAndIndicesWithinR[nextIndexInIndicesWithinR++][1] =
node.indexOfThisPoint;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinR(sampleIndex, closestSubTree,
level + 1, r, dynCorrExclTime, allowEqualToR, isWithinR,
distancesWithinRInOrder,
distancesAndIndicesWithinR, nextIndexInIndicesWithinR);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinR(sampleIndex, furthestSubTree,
level + 1, r, dynCorrExclTime, allowEqualToR, isWithinR,
distancesWithinRInOrder,
distancesAndIndicesWithinR, nextIndexInIndicesWithinR);
}
}
return nextIndexInIndicesWithinR;
}
/**
* As per {@link #findPointsWithinR(int, double, int, boolean, boolean[], double[], double[][])}
* only we have a sub-variable already tested, and we incorporate its results here.
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param r radius within which to count points
* @param dynCorrExclTime time window within which to exclude
* points to be counted. Is >= 0. 0 means only exclude sampleIndex.
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param variableAlreadyTested
* @param testResultsForGivenVariable an array indicating whether each sample for
* variableAlreadyTested was within radius for this search point
* @param distancesWithinRForGivenVariable an array of distances for
* variableAlreadyTested for each sample
* found to be within r. Values at other indices are not defined.
* @param distancesAndIndicesWithinR a list of distances (in column index 0)
* and array indices (in column index 1)
* for points found to be within r, terminated with a -1 value on the index.
* @return the next available index in distancesAndIndicesWithinR after the method is complete
*/
public int findPointsWithinRAndTakeRadiusMax(int sampleIndex, double r,
int dynCorrExclTime, boolean allowEqualToR,
int variableAlreadyTested, boolean[] testResultsForGivenVariable,
double[] distancesWithinRForGivenVariable,
double[][] distancesAndIndicesWithinR) {
if (rootNode == null) {
distancesAndIndicesWithinR[0][1] = -1;
return 0;
}
int currentIndexInIndicesWithinR =
findPointsWithinRAndTakeRadiusMax(sampleIndex,
rootNode, 0, r, dynCorrExclTime, allowEqualToR,
variableAlreadyTested, testResultsForGivenVariable,
distancesWithinRForGivenVariable, distancesAndIndicesWithinR, 0);
distancesAndIndicesWithinR[currentIndexInIndicesWithinR][1] = -1;
return currentIndexInIndicesWithinR;
}
/**
* As per {@link #findPointsWithinR(int, KdTreeNode, int, double, int, boolean, boolean[], double[], double[][], int)}
* only we have a sub-variable already tested, and we incorporate its results here.
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param dynCorrExclTime time window within which to exclude
* points to be counted. Is >= 0. 0 means only exclude sampleIndex.
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param variableAlreadyTested
* @param testResultsForGivenVariable an array indicating whether each sample for
* variableAlreadyTested was within radius for this search point
* @param distancesWithinRForGivenVariable an array of distances for
* variableAlreadyTested for each sample
* found to be within r. Values at other indices are not defined.
* @param distancesAndIndicesWithinR a list of distances (in column index 0)
* and array indices (in column index 1)
* for points found to be within r, terminated with a -1 value on the index.
* @param nextIndexInIndicesWithinR the next available index in indicesWithinR
* before the method.
* @return the next available index in distancesAndIndicesWithinR after the method is complete
*/
protected int findPointsWithinRAndTakeRadiusMax(int sampleIndex,
KdTreeNode node, int level, double r,
int dynCorrExclTime, boolean allowEqualToR,
int variableAlreadyTested, boolean[] testResultsForGivenVariable,
double[] distancesWithinRForGivenVariable,
double[][] distancesAndIndicesWithinR,
int nextIndexInIndicesWithinR) {
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if (testResultsForGivenVariable[node.indexOfThisPoint] &&
(Math.abs(node.indexOfThisPoint - sampleIndex) > dynCorrExclTime) &&
((absDistOnThisDim < r) ||
( allowEqualToR && (absDistOnThisDim == r)))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
double[] norms = new double[originalDataSets.length];
double maxNorm = 0;
for (int v = 0; v < originalDataSets.length; v++) {
if (v == variableAlreadyTested) {
// This variable has already been checked to be ok above
continue;
}
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this variable, no point
// checking the others:
withinBounds = false;
break;
}
norms[v] = distForVariableV;
if (distForVariableV > maxNorm) {
maxNorm = distForVariableV;
}
}
if (withinBounds) {
// This node gets counted
if (maxNorm < distancesWithinRForGivenVariable[node.indexOfThisPoint]) {
// The max norm actually comes from the pre-tested variable:
maxNorm = distancesWithinRForGivenVariable[node.indexOfThisPoint];
}
distancesAndIndicesWithinR[nextIndexInIndicesWithinR][0] =
maxNorm;
distancesAndIndicesWithinR[nextIndexInIndicesWithinR++][1] =
node.indexOfThisPoint;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinRAndTakeRadiusMax(sampleIndex, closestSubTree,
level + 1, r, dynCorrExclTime, allowEqualToR,
variableAlreadyTested, testResultsForGivenVariable,
distancesWithinRForGivenVariable,
distancesAndIndicesWithinR, nextIndexInIndicesWithinR);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinRAndTakeRadiusMax(sampleIndex, furthestSubTree,
level + 1, r, dynCorrExclTime, allowEqualToR,
variableAlreadyTested, testResultsForGivenVariable,
distancesWithinRForGivenVariable,
distancesAndIndicesWithinR, nextIndexInIndicesWithinR);
}
}
return nextIndexInIndicesWithinR;
}
@Override
public void findPointsWithinR(
double r, double[][] samplePoint,
boolean allowEqualToR, boolean[] isWithinR,
int[] indicesWithinR) {
if (rootNode == null) {
indicesWithinR[0] = -1;
return;
}
int currentIndexInIndicesWithinR =
findPointsWithinR(samplePoint,
rootNode, 0, r, allowEqualToR, isWithinR,
indicesWithinR, 0);
indicesWithinR[currentIndexInIndicesWithinR] = -1;
return;
}
/**
* Record the collection of points within radius r of a given sample (sampleVectors),
* in the tree rooted at node (which is at the specified level in the tree).
* Nearest neighbour function to compare to r is a max norm between the
* high-level variables, with norm for each variable being the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* <p>The recording of nearest neighbours is made within the isWithinR
* and indicesWithinR arrays, which must be constructed before
* calling this method, with length at or exceeding the total
* number of data points. indicesWithinR is
* </p>
*
* @param sampleVectors sample vectors to find the neighbours within r
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param isWithinR the array should be passed in with all points set to
* false initially, and is return indicating whether each sample was
* found to be within r of that at sampleIndex.
* @param indicesWithinR a list of array indices
* for points marked as true in isWithinR, terminated with a -1 value.
* @param nextIndexInIndicesWithinR the next available index in indicesWithinR
* before the method.
* @return the next available index in indicesWithinR after the method is complete
*/
protected int findPointsWithinR(double[][] sampleVectors,
KdTreeNode node, int level, double r, boolean allowEqualToR,
boolean[] isWithinR, int[] indicesWithinR,
int nextIndexInIndicesWithinR) {
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
int variableNumber = dimensionToVariableNumber[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = sampleVectors[variableNumber][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (absDistOnThisDim == r))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
double[] norms = new double[originalDataSets.length];
double maxNorm = 0;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
sampleVectors[v],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this variable, no point
// checking the others:
withinBounds = false;
break;
}
norms[v] = distForVariableV;
if (distForVariableV > maxNorm) {
maxNorm = distForVariableV;
}
}
if (withinBounds) {
// This node gets counted
isWithinR[node.indexOfThisPoint] = true;
indicesWithinR[nextIndexInIndicesWithinR++] =
node.indexOfThisPoint;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinR(sampleVectors, closestSubTree,
level + 1, r, allowEqualToR, isWithinR, indicesWithinR,
nextIndexInIndicesWithinR);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinR(sampleVectors, furthestSubTree,
level + 1, r, allowEqualToR, isWithinR, indicesWithinR,
nextIndexInIndicesWithinR);
}
}
return nextIndexInIndicesWithinR;
}
/**
* As per {@link #countPointsWithinR(int, double, boolean)}
* with an additional factor as follows.
*
* <p>For this method, one of the high-level variables has already had their
* norms tested against r; this variable is indicated by variableAlreadyTested
* and the results are contained in the testResultsForGivenVariable array.
* </p>
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param r radius within which to count points
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param variableAlreadyTested the variable for which the test results
* are provided in testResultsForGivenVariable
* @param testResultsForGivenVariable array of booleans indicating whether
* the variable identified by variableAlreadyTested was within r for each
* sample index.
* @return the count of points within r.
*/
public int countPointsWithinR(int sampleIndex, double r, boolean allowEqualToR,
int variableAlreadyTested, boolean[] testResultsForGivenVariable) {
if (rootNode == null) {
return 0;
}
return countPointsWithinR(sampleIndex, rootNode, 0, r, allowEqualToR,
variableAlreadyTested, testResultsForGivenVariable);
}
/**
* As per {@link #countPointsWithinR(int, double, boolean, int, boolean[])}
* with allowEqualToR == false
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param r radius within which to count points
* @param variableAlreadyTested the variable for which the test results
* are provided in testResultsForGivenVariable
* @param testResultsForGivenVariable array of booleans indicating whether
* the variable identified by variableAlreadyTested was within r for each
* sample index.
* @return the count of points within r.
*/
public int countPointsStrictlyWithinR(int sampleIndex, double r,
int variableAlreadyTested, boolean[] testResultsForGivenVariable) {
return countPointsWithinR(sampleIndex, r, false,
variableAlreadyTested, testResultsForGivenVariable);
}
/**
* As per {@link #countPointsWithinR(int, double, boolean, int, boolean[])}
* with allowEqualToR == true
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param r radius within which to count points
* @param variableAlreadyTested the variable for which the test results
* are provided in testResultsForGivenVariable
* @param testResultsForGivenVariable array of booleans indicating whether
* the variable identified by variableAlreadyTested was within r for each
* sample index.
* @return the count of points within r.
*/
public int countPointsWithinOrOnR(int sampleIndex, double r,
int variableAlreadyTested, boolean[] testResultsForGivenVariable) {
return countPointsWithinR(sampleIndex, r, true,
variableAlreadyTested, testResultsForGivenVariable);
}
/**
* Count the number of points within radius r of a given sample (sampleIndex),
* in the tree rooted at node (which is at the specified level in the tree),
* when one of the variables has already been tested.
* Nearest neighbour function to compare to r is a max norm between the
* high-level variables, with norm for each variable being the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param variableAlreadyTested the variable for which the test results
* are provided in testResultsForGivenVariable
* @param testResultsForGivenVariable array of booleans indicating whether
* the variable identified by variableAlreadyTested was within r for each
* sample index.
* @return count of points within r
*/
protected int countPointsWithinR(int sampleIndex,
KdTreeNode node, int level, double r, boolean allowEqualToR,
int variableAlreadyTested, boolean[] testResultsForGivenVariable) {
int count = 0;
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
int variableNumber = dimensionToVariableNumber[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if (testResultsForGivenVariable[node.indexOfThisPoint] &&
(node.indexOfThisPoint != sampleIndex) &&
((variableNumber == variableAlreadyTested) ||
(absDistOnThisDim < r) ||
(allowEqualToR && (absDistOnThisDim == r)))) {
// Preliminary check (including that the dimension already tested was ok)
// says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
for (int v = 0; v < originalDataSets.length; v++) {
if (v == variableAlreadyTested) {
// This variable has already been checked to be ok above
continue;
}
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this dimension, no point
// checking the others:
withinBounds = false;
break;
}
}
if (withinBounds) {
// This node gets counted
count++;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
count += countPointsWithinR(sampleIndex, closestSubTree,
level + 1, r, allowEqualToR,
variableAlreadyTested, testResultsForGivenVariable);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
count += countPointsWithinR(sampleIndex, furthestSubTree,
level + 1, r, allowEqualToR,
variableAlreadyTested, testResultsForGivenVariable);
}
}
return count;
}
/**
* As per {@link #countPointsWithinR(int, double, boolean)}, except searches for a specified
* sample point rather than a point associated with an index in the existing sample set, and
* with an additional factor as follows.
*
* <p>For this method, one of the high-level variables has already had their
* norms tested against r; this variable is indicated by variableAlreadyTested
* and the results are contained in the testResultsForGivenVariable array.
* </p>
*
* @param r radius within which to count points
* @param sampleVectors sample vectors (not necessarily in the tree) to find a nearest neighbour
* for
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param variableAlreadyTested the variable for which the test results
* are provided in testResultsForGivenVariable
* @param testResultsForGivenVariable array of booleans indicating whether
* the variable identified by variableAlreadyTested was within r for each
* sample index.
* @return the count of points within r.
*/
public int countPointsWithinR(double r, double[][] sampleVectors, boolean allowEqualToR,
int variableAlreadyTested, boolean[] testResultsForGivenVariable) {
if (rootNode == null) {
return 0;
}
return countPointsWithinR(sampleVectors, rootNode, 0, r, allowEqualToR,
variableAlreadyTested, testResultsForGivenVariable);
}
/**
* Count the number of points within radius r of a given sample (sampleVectors),
* in the tree rooted at node (which is at the specified level in the tree),
* when one of the variables has already been tested.
* Nearest neighbour function to compare to r is a max norm between the
* high-level variables, with norm for each variable being the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* @param sampleVectors sample vectors (not necessarily in the tree) to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param variableAlreadyTested the variable for which the test results
* are provided in testResultsForGivenVariable
* @param testResultsForGivenVariable array of booleans indicating whether
* the variable identified by variableAlreadyTested was within r for each
* sample index.
* @return count of points within r
*/
protected int countPointsWithinR(double[][] sampleVectors,
KdTreeNode node, int level, double r, boolean allowEqualToR,
int variableAlreadyTested, boolean[] testResultsForGivenVariable) {
int count = 0;
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
int variableNumber = dimensionToVariableNumber[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = sampleVectors[variableNumber][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if (testResultsForGivenVariable[node.indexOfThisPoint] &&
((variableNumber == variableAlreadyTested) ||
(absDistOnThisDim < r) ||
(allowEqualToR && (absDistOnThisDim == r)))) {
// Preliminary check (including that the dimension already tested was ok)
// says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
for (int v = 0; v < originalDataSets.length; v++) {
if (v == variableAlreadyTested) {
// This variable has already been checked to be ok above
continue;
}
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
sampleVectors[v],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this dimension, no point
// checking the others:
withinBounds = false;
break;
}
}
if (withinBounds) {
// This node gets counted
count++;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
count += countPointsWithinR(sampleVectors, closestSubTree,
level + 1, r, allowEqualToR,
variableAlreadyTested, testResultsForGivenVariable);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
count += countPointsWithinR(sampleVectors, furthestSubTree,
level + 1, r, allowEqualToR,
variableAlreadyTested, testResultsForGivenVariable);
}
}
return count;
}
/**
* As per {@link #findPointsWithinR(int, double, int, boolean, boolean[], int[])}
* however using multiple radii for each variable.
*
* @param sampleIndex sample index in the data to find a nearest neighbour for
* @param rs radii within which to count points (one for each variable)
* @param dynCorrExclTime time window within which to exclude points to be counted. Is >= 0. 0 means only exclude sampleIndex.
* @param allowEqualToR if true, then count points at radius r also, otherwise only those strictly within r
* @param isWithinR the array MUST be passed in with all points set to false initially, and is returned indicating whether each sample was found to be within r of that at sampleIndex.
* @param indicesWithinR a list of array indices for points marked as true in isWithinR, terminated with a -1 value.
* @return the number of points found to match
*/
public int findPointsWithinRs(int sampleIndex,
double[] rs, int dynCorrExclTime, boolean allowEqualToR,
boolean[] isWithinR, int[] indicesWithinR) {
if (rootNode == null) {
indicesWithinR[0] = -1;
return 0;
}
int currentIndexInIndicesWithinR =
findPointsWithinRs(sampleIndex,
rootNode, 0, rs, dynCorrExclTime, allowEqualToR, isWithinR,
indicesWithinR, 0);
indicesWithinR[currentIndexInIndicesWithinR] = -1;
return currentIndexInIndicesWithinR;
}
/**
* As per {@link #findPointsWithinR(int, KdTreeNode, int, double, int, boolean, boolean[], int[], int)}
* however with dynamic correlation time specified
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param rs radii within which to count points (one for each variable)
* @param dynCorrExclTime time window within which to exclude
* points to be counted. Is >= 0. 0 means only exclude sampleIndex.
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param isWithinR the array should be passed in with all points set to
* false initially, and is return indicating whether each sample was
* found to be within r of that at sampleIndex.
* @param indicesWithinR a list of array indices
* for points marked as true in isWithinR, terminated with a -1 value.
* @param nextIndexInIndicesWithinR the next available index in indicesWithinR
* before the method.
* @return the next available index in indicesWithinR after the method is complete
*/
protected int findPointsWithinRs(int sampleIndex,
KdTreeNode node, int level, double[] rs,
int dynCorrExclTime, boolean allowEqualToR,
boolean[] isWithinR, int[] indicesWithinR,
int nextIndexInIndicesWithinR) {
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
int variableNumber = dimensionToVariableNumber[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if ((Math.abs(node.indexOfThisPoint - sampleIndex) > dynCorrExclTime) &&
((absDistOnThisDim < rs[variableNumber]) ||
( allowEqualToR && (absDistOnThisDim == rs[variableNumber])))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
double[] norms = new double[originalDataSets.length];
double maxNorm = 0;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
rs[v], normTypeToUse);
if ((distForVariableV >= rs[v]) &&
!(allowEqualToR && (distForVariableV == rs[v]))) {
// We don't fit on this variable, no point
// checking the others:
withinBounds = false;
break;
}
norms[v] = distForVariableV;
if (distForVariableV > maxNorm) {
maxNorm = distForVariableV;
}
}
if (withinBounds) {
// This node gets counted
isWithinR[node.indexOfThisPoint] = true;
indicesWithinR[nextIndexInIndicesWithinR++] =
node.indexOfThisPoint;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinRs(sampleIndex, closestSubTree,
level + 1, rs, dynCorrExclTime, allowEqualToR, isWithinR,
indicesWithinR, nextIndexInIndicesWithinR);
}
if ((absDistOnThisDim < rs[variableNumber]) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == rs[variableNumber]))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinRs(sampleIndex, furthestSubTree,
level + 1, rs, dynCorrExclTime, allowEqualToR, isWithinR,
indicesWithinR, nextIndexInIndicesWithinR);
}
}
return nextIndexInIndicesWithinR;
}
/**
* As per {@link #findPointsWithinR(double[], double[][], boolean, boolean[], int[]))}
* however using multiple radii for each variable.
*
* @param rs radii within which to count points (one for each variable)
* @param samplePoint sample vectors to find the neighbours within rs for
* @param allowEqualToR if true, then count points at radius r also, otherwise only those strictly within r
* @param isWithinR the array MUST be passed in with all points set to false initially, and is returned indicating whether each sample was found to be within r of that at sampleIndex.
* @param indicesWithinR a list of array indices for points marked as true in isWithinR, terminated with a -1 value.
*/
public void findPointsWithinRs(
double[] rs, double[][] samplePoint,
boolean allowEqualToR, boolean[] isWithinR,
int[] indicesWithinR) {
if (rootNode == null) {
indicesWithinR[0] = -1;
return;
}
int currentIndexInIndicesWithinR =
findPointsWithinRs(samplePoint,
rootNode, 0, rs, allowEqualToR, isWithinR,
indicesWithinR, 0);
indicesWithinR[currentIndexInIndicesWithinR] = -1;
return;
}
/**
* Record the collection of points within radii rs of a given sample (sampleVectors),
* in the tree rooted at node (which is at the specified level in the tree).
* Nearest neighbour function to compare to rs is a max norm between the
* high-level variables, with norm for each variable being the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* <p>The recording of nearest neighbours is made within the isWithinR
* and indicesWithinR arrays, which must be constructed before
* calling this method, with length at or exceeding the total
* number of data points. indicesWithinR is
* </p>
*
* @param sampleVectors sample vectors to find the neighbours within r
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param rs radii within which to count points (one for each variable)
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param isWithinR the array should be passed in with all points set to
* false initially, and is return indicating whether each sample was
* found to be within r of that at sampleIndex.
* @param indicesWithinR a list of array indices
* for points marked as true in isWithinR, terminated with a -1 value.
* @param nextIndexInIndicesWithinR the next available index in indicesWithinR
* before the method.
* @return the next available index in indicesWithinR after the method is complete
*/
protected int findPointsWithinRs(double[][] sampleVectors,
KdTreeNode node, int level, double[] rs, boolean allowEqualToR,
boolean[] isWithinR, int[] indicesWithinR,
int nextIndexInIndicesWithinR) {
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
int variableNumber = dimensionToVariableNumber[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = sampleVectors[variableNumber][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if ((absDistOnThisDim < rs[variableNumber]) ||
( allowEqualToR && (absDistOnThisDim == rs[variableNumber]))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
double[] norms = new double[originalDataSets.length];
double maxNorm = 0;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
sampleVectors[v],
originalDataSets[v][node.indexOfThisPoint],
rs[v], normTypeToUse);
if ((distForVariableV >= rs[v]) &&
!(allowEqualToR && (distForVariableV == rs[v]))) {
// We don't fit on this variable, no point
// checking the others:
withinBounds = false;
break;
}
norms[v] = distForVariableV;
if (distForVariableV > maxNorm) {
maxNorm = distForVariableV;
}
}
if (withinBounds) {
// This node gets counted
isWithinR[node.indexOfThisPoint] = true;
indicesWithinR[nextIndexInIndicesWithinR++] =
node.indexOfThisPoint;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinRs(sampleVectors, closestSubTree,
level + 1, rs, allowEqualToR, isWithinR, indicesWithinR,
nextIndexInIndicesWithinR);
}
if ((absDistOnThisDim < rs[variableNumber]) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == rs[variableNumber]))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
nextIndexInIndicesWithinR = findPointsWithinRs(sampleVectors, furthestSubTree,
level + 1, rs, allowEqualToR, isWithinR, indicesWithinR,
nextIndexInIndicesWithinR);
}
}
return nextIndexInIndicesWithinR;
}
/**
* As per {@link #countPointsWithinRs(int, double[], boolean)}
* with an additional factor as follows.
*
* <p>For this method, one of the high-level variables has already had their
* norms tested against the relevant r; this variable is indicated by variableAlreadyTested
* and the results are contained in the testResultsForGivenVariable array.
* </p>
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param rs radii for each variable within which to count points
* @param allowEqualToR if true, then count points at radii rs also,
* otherwise only those strictly within rs
* @param variableAlreadyTested the variable for which the test results
* are provided in testResultsForGivenVariable
* @param testResultsForGivenVariable array of booleans indicating whether
* the variable identified by variableAlreadyTested was within rs[variableAlreadyTested]
* for each sample index.
* @return the count of points within rs.
*/
public int countPointsWithinRs(int sampleIndex, double[] rs, boolean allowEqualToR,
int variableAlreadyTested, boolean[] testResultsForGivenVariable) {
if (rootNode == null) {
return 0;
}
return countPointsWithinRs(sampleIndex, rootNode, 0, rs, allowEqualToR,
variableAlreadyTested, testResultsForGivenVariable);
}
/**
* As per {@link #countPointsWithinRs(int, double[], boolean, int, boolean[])}
* with allowEqualToR == false
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param rs radii for each variable within which to count points
* @param variableAlreadyTested the variable for which the test results
* are provided in testResultsForGivenVariable
* @param testResultsForGivenVariable array of booleans indicating whether
* the variable identified by variableAlreadyTested was within rs[variableAlreadyTested]
* for each sample index.
* @return the count of points within rs.
*/
public int countPointsStrictlyWithinRs(int sampleIndex, double[] rs,
int variableAlreadyTested, boolean[] testResultsForGivenVariable) {
return countPointsWithinRs(sampleIndex, rs, false,
variableAlreadyTested, testResultsForGivenVariable);
}
/**
* As per {@link #countPointsWithinRs(int, double[], boolean, int, boolean[])}
* with allowEqualToR == true
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param rs radii for each variable within which to count points
* @param variableAlreadyTested the variable for which the test results
* are provided in testResultsForGivenVariable
* @param testResultsForGivenVariable array of booleans indicating whether
* the variable identified by variableAlreadyTested was within rs[variableAlreadyTested]
* for each sample index.
* @return the count of points within rs.
*/
public int countPointsWithinOrOnRs(int sampleIndex, double[] rs,
int variableAlreadyTested, boolean[] testResultsForGivenVariable) {
return countPointsWithinRs(sampleIndex, rs, true,
variableAlreadyTested, testResultsForGivenVariable);
}
/**
* Count the number of points within norms {r1,r2,etc} for each high-level
* variable, for a given sample (sampleIndex),
* in the tree rooted at node (which is at the specified level in the tree),
* when one of the variables has already been tested.
* The node itself is excluded from the search.
* Nearest neighbour function to compare to {r1,r2,etc}
* for each variable is the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param rs radii for each variable within which to count points
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param variableAlreadyTested the variable for which the test results
* are provided in testResultsForGivenVariable
* @param testResultsForGivenVariable array of booleans indicating whether
* the variable identified by variableAlreadyTested was within rs[variableAlreadyTested]
* for each sample index.
* @return count of points within rs
*/
protected int countPointsWithinRs(int sampleIndex,
KdTreeNode node, int level, double[] rs, boolean allowEqualToR,
int variableAlreadyTested, boolean[] testResultsForGivenVariable) {
int count = 0;
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
int variableNumber = dimensionToVariableNumber[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if (testResultsForGivenVariable[node.indexOfThisPoint] &&
(node.indexOfThisPoint != sampleIndex) &&
((variableNumber == variableAlreadyTested) ||
(absDistOnThisDim < rs[variableNumber]) ||
(allowEqualToR && (absDistOnThisDim == rs[variableNumber])))) {
// Preliminary check (including that the dimension already tested was ok)
// says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
for (int v = 0; v < originalDataSets.length; v++) {
if (v == variableAlreadyTested) {
// This variable has already been checked to be ok above
continue;
}
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
rs[v], normTypeToUse);
if ((distForVariableV >= rs[v]) &&
!(allowEqualToR && (distForVariableV == rs[v]))) {
// We don't fit on this dimension, no point
// checking the others:
withinBounds = false;
break;
}
}
if (withinBounds) {
// This node gets counted
count++;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
count += countPointsWithinRs(sampleIndex, closestSubTree,
level + 1, rs, allowEqualToR,
variableAlreadyTested, testResultsForGivenVariable);
}
if ((absDistOnThisDim < rs[variableNumber]) ||
( allowEqualToR && (distOnThisDim < 0) &&
(absDistOnThisDim == rs[variableNumber]))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
count += countPointsWithinRs(sampleIndex, furthestSubTree,
level + 1, rs, allowEqualToR,
variableAlreadyTested, testResultsForGivenVariable);
}
}
return count;
}
@Override
public int countPointsWithinR(int sampleIndex, double r,
boolean allowEqualToR, boolean[] additionalCriteria) {
if (rootNode == null) {
return 0;
}
return countPointsWithinR(sampleIndex, rootNode, 0, r, allowEqualToR,
additionalCriteria);
}
/**
* Count the number of points within radius r of a given sample (sampleIndex),
* in the tree rooted at node (which is at the specified level in the tree),
* subject to an additional criteria.
* Nearest neighbour function to compare to r is a max norm between the
* high-level variables, with norm for each variable being the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param additionalCriteria array of booleans. Only count a point if it
* is within r and is true in additionalCrtieria.
* @return count of points within r
*/
protected int countPointsWithinR(int sampleIndex,
KdTreeNode node, int level, double r, boolean allowEqualToR,
boolean[] additionalCriteria) {
int count = 0;
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if ((node.indexOfThisPoint != sampleIndex) &&
((absDistOnThisDim < r) ||
( allowEqualToR && (absDistOnThisDim == r)))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be counted.
if (additionalCriteria[node.indexOfThisPoint]) {
// The additional criteria was ok, so continue:
boolean withinBounds = true;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this dimension, no point
// checking the others:
withinBounds = false;
break;
}
}
if (withinBounds) {
// This node gets counted
count++;
}
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
count += countPointsWithinR(sampleIndex, closestSubTree,
level + 1, r, allowEqualToR, additionalCriteria);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
count += countPointsWithinR(sampleIndex, furthestSubTree,
level + 1, r, allowEqualToR, additionalCriteria);
}
}
return count;
}
@Override
public int countPointsWithinR(int sampleIndex, double r,
boolean allowEqualToR, boolean[] additionalCriteria, int[] remapping) {
if (rootNode == null) {
return 0;
}
return countPointsWithinR(sampleIndex, rootNode, 0, r, allowEqualToR,
additionalCriteria, remapping);
}
/**
* Count the number of points within radius r of a given sample (sampleIndex),
* in the tree rooted at node (which is at the specified level in the tree),
* subject to an additional criteria,
* and the search points are reindexed according to the remapping specified in remapping.
*
* Nearest neighbour function to compare to r is a max norm between the
* high-level variables, with norm for each variable being the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param additionalCriteria array of booleans. Only count a point if it
* is within r and is true in additionalCrtieria.
* @param remapping array of time indices with which to remap the search points
* onto the same time index space as the additionalCriteria (this will
* apply to the supplied sampleIndex as well as other search points)
* @return count of points within r
*/
protected int countPointsWithinR(int sampleIndex,
KdTreeNode node, int level, double r, boolean allowEqualToR,
boolean[] additionalCriteria, int[] remapping) {
int count = 0;
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if ((node.indexOfThisPoint != sampleIndex) &&
((absDistOnThisDim < r) ||
( allowEqualToR && (absDistOnThisDim == r)))) {
// Preliminary check says we need to compute the full distance
// to use or at least to check if it should be counted.
if (additionalCriteria[remapping[node.indexOfThisPoint]]) {
// The additional criteria was ok, so continue:
boolean withinBounds = true;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this dimension, no point
// checking the others:
withinBounds = false;
break;
}
}
if (withinBounds) {
// This node gets counted
count++;
}
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
count += countPointsWithinR(sampleIndex, closestSubTree,
level + 1, r, allowEqualToR, additionalCriteria, remapping);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
count += countPointsWithinR(sampleIndex, furthestSubTree,
level + 1, r, allowEqualToR, additionalCriteria, remapping);
}
}
return count;
}
@Override
public int countPointsWithinR(double[][] sampleVectors, double r,
boolean allowEqualToR, boolean[] additionalCriteria) {
if (rootNode == null) {
return 0;
}
return countPointsWithinR(sampleVectors, rootNode, 0, r, allowEqualToR,
additionalCriteria);
}
/**
* Count the number of points within radius r of a given sample (sampleVectors -- from outside the tree),
* in the tree rooted at node (which is at the specified level in the tree),
* subject to an additional criteria.
* Nearest neighbour function to compare to r is a max norm between the
* high-level variables, with norm for each variable being the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* @param sampleVectors sample vectors to find the neighbours within r
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param r radius within which to count points
* @param allowEqualToR if true, then count points at radius r also,
* otherwise only those strictly within r
* @param additionalCriteria array of booleans. Only count a point if it
* is within r and is true in additionalCrtieria.
* @return count of points within r
*/
protected int countPointsWithinR(double[][] sampleVectors,
KdTreeNode node, int level, double r, boolean allowEqualToR,
boolean[] additionalCriteria) {
int count = 0;
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
int variableNumber = dimensionToVariableNumber[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = sampleVectors[variableNumber][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if (additionalCriteria[node.indexOfThisPoint] &&
((absDistOnThisDim < r) ||
( allowEqualToR && (absDistOnThisDim == r)))) {
// Preliminary check (including additional criteria) says we need to compute the full distance
// to use or at least to check if it should be counted.
boolean withinBounds = true;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than r:
distForVariableV = normWithAbort(
sampleVectors[v],
originalDataSets[v][node.indexOfThisPoint],
r, normTypeToUse);
if ((distForVariableV >= r) &&
!(allowEqualToR && (distForVariableV == r))) {
// We don't fit on this dimension, no point
// checking the others:
withinBounds = false;
break;
}
}
if (withinBounds) {
// This node gets counted
count++;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
count += countPointsWithinR(sampleVectors, closestSubTree,
level + 1, r, allowEqualToR, additionalCriteria);
}
if ((absDistOnThisDim < r) ||
( allowEqualToR && (distOnThisDim < 0) && (absDistOnThisDim == r))) {
// It's possible we could have a node within (or on) r
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
count += countPointsWithinR(sampleVectors, furthestSubTree,
level + 1, r, allowEqualToR, additionalCriteria);
}
}
return count;
}
/**
* Count the number of points within norms {r1,r2,etc} for each high-level
* variable, for a given
* sample index in the data set. The node itself is
* excluded from the search.
* Nearest neighbour function to compare to {r1,r2,etc}
* for each variable is the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param rs radii for each variable within which to count points
* @param allowEqualToR if true, then count points at radii rs also,
* otherwise only those strictly within rs
* @return the count of points within rs.
*/
public int countPointsWithinRs(int sampleIndex, double[] rs, boolean allowEqualToR) {
if (allowEqualToR) {
return countPointsWithinOrOnRs(sampleIndex, rs);
} else {
return countPointsStrictlyWithinRs(sampleIndex, rs);
}
}
/**
* Count the number of points strictly within norms {r1,r2,etc} for each high-level
* variable, for a given
* sample index in the data set. The node itself is
* excluded from the search.
* Nearest neighbour function to compare to {r1,r2,etc}
* for each variable is the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param rs radii for each variable within which to count points
* @return the count of points within r.
*/
public int countPointsStrictlyWithinRs(int sampleIndex, double[] rs) {
if (rootNode == null) {
return 0;
}
return countPointsWithinRs(sampleIndex, rootNode, 0, rs, false);
}
/**
* Count the number of points within or at norms {r1,r2,etc} for each high-level
* variable, for a given
* sample index in the data set. The node itself is
* excluded from the search.
* Nearest neighbour function to compare to {r1,r2,etc}
* for each variable is the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param rs radii for each variable within which to count points
* @return the count of points within or on r.
*/
public int countPointsWithinOrOnRs(int sampleIndex, double[] rs) {
if (rootNode == null) {
return 0;
}
return countPointsWithinRs(sampleIndex, rootNode, 0, rs, true);
}
/**
* Count the number of points within norms {r1,r2,etc} for each high-level
* variable, for a given
* sample (sampleIndex),
* in the tree rooted at node (which is at the specified level in the tree).
* The node itself is excluded from the search.
* Nearest neighbour function to compare to {r1,r2,etc}
* for each variable is the specified norm.
* (If {@link EuclideanUtils#NORM_EUCLIDEAN} was selected, then the supplied
* r should be the required Euclidean norm <b>squared</b>, since we switch it
* to {@link EuclideanUtils#NORM_EUCLIDEAN_SQUARED} internally).
*
* @param sampleIndex sample index in the data to find a nearest neighbour
* for
* @param node node to start searching from in the kd-tree. Cannot be null
* @param level which level we're currently at in the tree
* @param rs radii for each variable within which to count points
* @param allowEqualToR if true, then count points at radii rs also,
* otherwise only those strictly within r
* @return count of points within r
*/
protected int countPointsWithinRs(int sampleIndex,
KdTreeNode node, int level, double[] rs, boolean allowEqualToR) {
int count = 0;
// Point to the correct array for the data at this level
int currentDim = level % totalDimensions;
double[][] data = dimensionToArray[currentDim];
int actualDim = dimensionToArrayIndex[currentDim];
int variableNumber = dimensionToVariableNumber[currentDim];
// Check the distance on this particular dimension
double distOnThisDim = data[sampleIndex][actualDim] -
data[node.indexOfThisPoint][actualDim];
double absDistOnThisDim;
if (normTypeToUse == EuclideanUtils.NORM_MAX_NORM) {
absDistOnThisDim = (distOnThisDim > 0) ? distOnThisDim : - distOnThisDim;
} else {
// norm type is EuclideanUtils#NORM_EUCLIDEAN_SQUARED
// Track the square distance
absDistOnThisDim = distOnThisDim * distOnThisDim;
}
if ((node.indexOfThisPoint != sampleIndex) &&
((absDistOnThisDim < rs[variableNumber]) ||
( allowEqualToR && (absDistOnThisDim == rs[variableNumber])))) {
// Preliminary check says we need to compute the full distances
// to use or at least to check if it should be counted.
boolean withinBounds = true;
for (int v = 0; v < originalDataSets.length; v++) {
// For each of our separate (multivariate) variables,
// compute the (specified) norm in that variable's space:
double distForVariableV;
// Distance calculation terminates early with Double.POSITIVE_INFINITY
// if it is clearly larger than rs[v]:
distForVariableV = normWithAbort(
originalDataSets[v][sampleIndex],
originalDataSets[v][node.indexOfThisPoint],
rs[v], normTypeToUse);
if ((distForVariableV >= rs[v]) &&
!(allowEqualToR && (distForVariableV == rs[v]))) {
// We don't fit on this dimension, no point
// checking the others:
withinBounds = false;
break;
}
}
if (withinBounds) {
// This node gets counted
count++;
}
}
KdTreeNode closestSubTree = null;
KdTreeNode furthestSubTree = null;
// And translate this to which subtree is closer
if (distOnThisDim < 0) {
// We need to search the left tree
closestSubTree = node.leftTree;
furthestSubTree = node.rightTree;
} else {
// We need to search the right tree
closestSubTree = node.rightTree;
furthestSubTree = node.leftTree;
}
// Update the search on that subtree
if (closestSubTree != null) {
count += countPointsWithinRs(sampleIndex, closestSubTree,
level + 1, rs, allowEqualToR);
}
if ((absDistOnThisDim < rs[variableNumber]) ||
( allowEqualToR && (distOnThisDim < 0) &&
(absDistOnThisDim == rs[variableNumber]))) {
// It's possible we could have a node within (or on) rs[variableNumber]
// in the other branch as well, so search there too.
// (Note: we only check furthest subtree in the == case
// when it's allowed
// *if* it's the right subtree, as only the right sub-tree
// can have node with distance in this coordinate *equal* to
// that of the current node -- left subtree must be strictly
// less than the coordinate of the current node, so
// distance to any of those points could not be equal.)
if (furthestSubTree != null) {
count += countPointsWithinRs(sampleIndex, furthestSubTree,
level + 1, rs, allowEqualToR);
}
}
return count;
}
/**
* Internal utility function for debug printing of a tree
*
*/
public void print() {
print(rootNode, 0);
}
/**
* Internal utility function for debug printing of a node and
* all of its descendants
*
* @param node current node
* @param level which level we're at in the tree
*/
protected void print(KdTreeNode node, int level) {
if (node == null) {
System.out.print("null");
return;
}
if (level > 0) {
System.out.println();
}
for (int i = 0; i < level; i++) {
System.out.print("\t");
}
System.out.print("((");
for (int i = 0; i < totalDimensions; i++) {
System.out.printf("%.3f,",
dimensionToArray[i][node.indexOfThisPoint][dimensionToArrayIndex[i]]);
}
System.out.print("),");
print(node.leftTree, level+1);
System.out.print(", ");
print(node.rightTree, level+1);
System.out.println(")");
}
}