/** * GeDBIT.index.VPRangeCursor 2006.05.10 Copyright Information: Change Log: 2006.05.10: Created, by Rui Mao */ package GeDBIT.index; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.logging.Logger; import GeDBIT.dist.Metric; import GeDBIT.parallel.GlobalIndexPrintTask; import GeDBIT.parallel.GlobalIndexWorkTask; import GeDBIT.parallel.Task; import GeDBIT.parallel.WorkThread; import GeDBIT.parallel.WorkThreadUtil; import GeDBIT.type.DoubleIndexObjectPair; import GeDBIT.type.IndexObject; import GeDBIT.util.Debug; import GeDBIT.util.ObjectIOManager; /** * Implements an {@link Cursor}, for range search in vp tree * * @author Rui Mao, Willard * @version 2006.05.31 */ public class VPRangeCursor extends Cursor { IndexObject q; double r; int maxListLength; int id; private Logger logger; // statistics // global statistics int nodeVisited, internalVisited; // leafVisited; //number of index nodes // visited int distNum, pivotDistNum, dataDistNum; // number of distance calculation, // calculations with pivots, data // with points int resultNode, resultInternalNode, resultLeafNode; // number of nodes // containing at least // one query result. int internalPruned, leafPruned; // number of points pruned in internal // nodes, leaf nodes. int resultConfirmedByPathDist; // number of data points that are confirmed // to be results by // path distance list // without computing the distance to the query directly. it is a subset of // resultWithoutDist. int pointPrunedByPivotDist; // number of data points that are pruned by the // pivot distance int nodePrunedByPivotDist; // number of nodes that are pruned by the pivot // distance int pointPrunedByPathDist; // number of data points that are pruned by path // distance list int nodeWithoutDist; // number of nodes that are determined to be all // results without // computing distance directly. int resultWithoutDist; // number of results that are determined to be // results without // computing distance directly. int internalWithoutDist, leafWithoutDist; int pivotAsResult; // number of pivots that are also search results int pivotRowIDAsResult; // number of row IDs corresponding to those pivots // being search // results // layer statistics, computed in temp variables, and finally added to the // lists when values are fixed. ArrayList<double[]> queryPivotDistance; // distance between the query and // the pivots of each level, -1 // means no such value // each element is a 1-d array, is the average value of one level, for each // pivot (column) // computed during processing the layer, and value added to this list at the // end of the layer ArrayList<Integer> levelNodeNum, levelNodeVisited; // from the perspective // of last level, these // are the total // children node number, // number of children visited, and the ratio. thus, these values are // computed // during the processing of last // layer, and are added to the list at the begin // of the current layer. ArrayList<Integer> levelPointNum, levelPointVisited; // number of data // objects of leaf // nodes of each level, // number of data object directly compute distance on, ratio. //computed // during processing the layer, //and value // added to this list at the end of the layer // counters, indecies private int level; // the id of the current level, top-down from 0 private int insideLevel; // the id of the node on current level, left to // right from 0 int levelNodeToVisit; // number of nodes of current layer to be visited. The // value // is initially levelNodeVisited set during previous // layer boolean isLeftMost; // whether next node is the first node (left-most) of a // layer, i.e., start a new layer. // temp variable, for each layer, to be added to the ArrayLists double[] tempQueryPivotDistance; int[] childWithPivotCounter; // the ith element is the number of index nodes // of the layer // that has ith pivot. int tempLevelNode, tempLevelNodeVisited; int tempLevelPoint, tempLevelPointVisited; LinkedList<RangeQueryTask> task; public VPRangeCursor(RangeQuery query, ObjectIOManager oiom, Metric metric, long rootAddress) { super(oiom, metric, rootAddress); task = new LinkedList<RangeQueryTask>(); this.q = query.getQueryObject(); this.r = query.getRadius(); this.maxListLength = query.getMaxDistanceListSize(); // add the first task task.add(new RangeQueryTask(rootAddress, new double[0])); if (Debug.debug) { logger = Logger.getLogger("GeDBIT.index"); } if (Debug.statistics) { initializeStatistics(); } } public VPRangeCursor(RangeQuery query, ObjectIOManager[] oioms, Metric metric, long rootAddress) { super(oioms, metric, rootAddress); task = new LinkedList<RangeQueryTask>(); this.q = query.getQueryObject(); this.r = query.getRadius(); this.id = query.getId(); this.maxListLength = query.getMaxDistanceListSize(); // add the first task task.add(new RangeQueryTask(-1, rootAddress, new double[0])); if (Debug.debug) { logger = Logger.getLogger("GeDBIT.index"); } if (Debug.statistics) { initializeStatistics(); } } /** * initialize the statistics, not the temp variables. this method only be * called once at the begining, where the temp variables will be initialized * each time at the begining of a new layer. */ private void initializeStatistics() { level = -1; // before the root (0,0), the level should be -1 insideLevel = 0; levelNodeToVisit = 1; // node number of root level is 1 isLeftMost = true; // global statistics nodeVisited = 0; internalVisited = 0; resultNode = 0; resultInternalNode = 0; resultLeafNode = 0; distNum = 0; pivotDistNum = 0; this.resultConfirmedByPathDist = 0; this.pointPrunedByPathDist = 0; this.pivotAsResult = 0; this.pivotRowIDAsResult = 0; this.nodeWithoutDist = 0; this.resultWithoutDist = 0; this.internalWithoutDist = 0; this.leafWithoutDist = 0; this.nodePrunedByPivotDist = 0; this.pointPrunedByPivotDist = 0; this.dataDistNum = 0; this.internalPruned = 0; this.leafPruned = 0; // level statistics queryPivotDistance = new ArrayList<double[]>(); levelNodeNum = new ArrayList<Integer>(); levelNodeVisited = new ArrayList<Integer>(); levelPointNum = new ArrayList<Integer>(); levelPointVisited = new ArrayList<Integer>(); // temp variables, these variables are supposed to be computed during // the processing of last layer tempLevelNode = 1; tempLevelNodeVisited = 1; } /** * the method to be called at the begining of each layer. Actions includes: * 1. add the temp varaibles to the list, which should be add at the * begining of a layer 2. set related counters 3. initialize temp variables * for the new layer */ protected void layerBegining() { // step1. add temp variables to lists levelNodeNum.add(tempLevelNode); levelNodeVisited.add(tempLevelNodeVisited); // step2. set counters level++; insideLevel = 0; levelNodeToVisit = tempLevelNodeVisited; // step3. initialize temp variables tempQueryPivotDistance = new double[1]; tempQueryPivotDistance[0] = 0; childWithPivotCounter = new int[1]; childWithPivotCounter[0] = 0; tempLevelNode = 0; tempLevelNodeVisited = 0; tempLevelPoint = 0; tempLevelPointVisited = 0; } /** * to be called at the end of a layer. actions includes: 1. add temp * variables, which should be added to lists at the end of a layer, to lists * 2. set related counters */ protected void layerEnd() { // step1. add temp variables for (int i = 0; i < tempQueryPivotDistance.length; i++) tempQueryPivotDistance[i] /= childWithPivotCounter[i]; queryPivotDistance.add(tempQueryPivotDistance); levelPointNum.add(tempLevelPoint); levelPointVisited.add(tempLevelPointVisited); // step2. set related counters isLeftMost = true; } /** * to be call at the begining of visiting a node */ protected void nodeBegining() { if (isLeftMost) { isLeftMost = false; layerBegining(); } nodeVisited++; } /** * to be called at the end of visiting a node */ protected void nodeEnd() { insideLevel++; // System.out.println("level=" + level + ", layer = " + layer + // ", layersize=" + layerSize); if (insideLevel == levelNodeToVisit) layerEnd(); } // methods to return statistics /** * @return the height the of index tree */ public int getHeight() { return level + 1; } /** * Return the number of index node visited during the search * * @return an int array of length 2, the first element is the total number * of index nodes visited during the search, the second is the * number of internal nodes visited. */ public int[] getNodeVisitedNumber() { return new int[] { nodeVisited, internalVisited }; } public int getResultNodeNumber() { return resultNode; } public int getResultInternalNodeNumber() { return resultInternalNode; } public int getResultLeafNodeNumber() { return resultLeafNode; } public int getInternalPruned() { return this.internalPruned; } public int getLeafPruned() { return this.leafPruned; } public int getPivotDistNum() { return this.pivotDistNum; } public int getDistNum() { return this.distNum; } public int getDataDistNum() { return this.dataDistNum; } public int getResultWithoutDist() { return this.resultWithoutDist; } public int getInternalWithoutDist() { return this.internalWithoutDist; } public int getLeafWithoutDist() { return this.leafWithoutDist; } public int getPivotAsResult() { return this.pivotAsResult; } /** * return some counters * * @return an array of counters: 0: int resultConfirmedByPathDist; //number * of data points that are confirmed to be results by path distance * list //without computing the distance directly. it is a subset of * resultWithoutDist. 1: int pointPrunedByPivotDist; //number of * data points that are pruned by the pivot distance 2: int * nodePrunedByPivotDist; //number of nodes that are pruned by the * pivot distance 3: int pointPrunedByPathDist; //number of data * points that are pruned by path distance list 4: int * nodeWithoutDist; //number of nodes that are determined to be all * results without computing distance directly. 5: int * resultWithoutDist; //number of results that are determined to be * results without computing distance directly. 6: int * pivotAsResult; //number of pivots that are also search results 7: * int pivotRowIDAsResult; //number of row IDs corresponding to * those pivots being search results */ public int[] getCounters() { int result[] = new int[8]; result[0] = this.resultConfirmedByPathDist; result[1] = this.pointPrunedByPathDist; result[2] = this.nodePrunedByPivotDist; result[3] = this.pointPrunedByPivotDist; result[4] = this.nodeWithoutDist; result[5] = this.resultWithoutDist; result[6] = this.pivotAsResult; result[7] = this.pivotRowIDAsResult; return result; } /** * return the number of distance calculations during the search * * @return an int array of length 2, the first element is total number of * distance calculations during the search, the second is the number * of distance calculations between the query and the pivots */ public int[] getDistanceCalculationNumber() { return new int[] { distNum, pivotDistNum, dataDistNum }; } /** * return the average distance between the query and the pivots for each * layer. * * @return a 2-d double array, average distance between the query to the * pivots (column) on each level (row). the length of a row is the * max number of pivots in the level. */ public double[][] getQueryPivotDistance() { double[][] result = new double[level + 1][]; for (int i = 0; i <= level; i++) result[i] = (double[]) queryPivotDistance.get(i); return result; } /** * return the number of nodes visited in each level of the index tree. * * @return a 2-d int array, each row is the statistics of a level, starting * from level 0, the root. each row is an int array of length 2, the * first is the number of index nodes of the level that belong to * the parent nodes of the nodes counted; the second is the number * of nodes visited in the layer, */ public int[][] getLevelNodeVisited() { int[][] result = new int[level + 1][2]; for (int i = 0; i <= level; i++) { result[i][0] = levelNodeNum.get(i); result[i][1] = levelNodeVisited.get(i); } return result; } /** * return the number of data objects that the query directly compute * distance with in the leaf nodes of each level of the index tree. * * @return a 2-d int array, each row is the statistics of a level, starting * from level 0, the root. each row is an int array of length 2, the * first the total number of data objects in the leaf node visited * of the level, the second is the number of data object that the * query directly compute distance with. Note that the total number * of index nodes of a level can not be found during the search. * Also note that the first element can be 0. */ public int[][] getLevelPointVisited() { int[][] result = new int[level + 1][2]; for (int i = 0; i <= level; i++) { result[i][0] = levelPointNum.get(i); result[i][1] = levelPointVisited.get(i); } return result; } /** * @return a string representation of the Visitor */ public String toString() { StringBuffer result = new StringBuffer(); result.append("BFSProximityVisitor:\n" + "Query: metric=" + metric.toString() + ", center=" + q.toString() + ", radius=" + r + "\nOIOM: " + oiom.toString() + ", height = " + getHeight() + "\n"); int[] temp1 = getDistanceCalculationNumber(); result.append("#Distance calculation: " + temp1[0] + " (internal: " + temp1[1] + " , leaf: " + temp1[2] + " ), "); temp1 = getNodeVisitedNumber(); result.append("#Node visited: " + temp1[0] + " (center: " + temp1[1] + " , data object: " + temp1[2] + " )\n"); double[][] temp2 = getQueryPivotDistance(); int[][] temp3 = getLevelNodeVisited(); int[][] temp4 = getLevelPointVisited(); result.append("Layer statistics: layer id, node visited(total), data object visited(total), average query-center distances:\n"); for (int i = 0; i < getHeight(); i++) { result.append(i + ": " + temp3[i][1] + " ( " + temp3[i][0] + " ), " + temp4[i][1] + " ( " + temp4[i][0] + " ), ["); for (int j = 0; j < temp2[i].length; j++) result.append(temp2[i][j] + ", "); result.append("]\n"); } return result.toString(); } protected void visit(QueryTask t) { // TODO if (!(t instanceof VPRangeCursor.RangeQueryTask)) throw new UnsupportedOperationException( "Only RangeQueryTask is supported by VPRangeCursor, not " + t.getClass()); RangeQueryTask task = (RangeQueryTask) t; Object node = null; try { node = oiom.readObject(new Long(task.nodeAddress)); } catch (Exception e) { e.printStackTrace(); } if (node instanceof VPInternalNode) visitInternalNode((VPInternalNode) node, task.distList); else if (node instanceof VPLeafNode) visitLeafNode((VPLeafNode) node, task.distList); else throw new UnsupportedOperationException( "only VPInternalNode or VPLeafNode is supported by VPRangerCursor, not " + node.getClass()); } protected void visitRoot(QueryTask t, int root) { // TODO if (!(t instanceof VPRangeCursor.RangeQueryTask)) throw new UnsupportedOperationException( "Only RangeQueryTask is supported by VPRangeCursor, not " + t.getClass()); RangeQueryTask task = (RangeQueryTask) t; Object node = null; try { node = oioms[root].readObject(new Long(task.nodeAddress)); } catch (Exception e) { e.printStackTrace(); } if (node instanceof VPInternalNode) visitRootInternalNode((VPInternalNode) node, task.distList); else throw new UnsupportedOperationException( "only VPInternalNode or VPLeafNode is supported by VPRangerCursor, not " + node.getClass()); distributeTasks(); } boolean contains(ObjectIOManager oiom, long address, IndexObject query) { List<IndexObject> linearIndex = getAllPoints(oiom, address); for (IndexObject o : linearIndex) if (this.metric.getDistance(o, query) == 0) return true; return false; } private class RangeQueryTask extends QueryTask { RangeQueryTask(long address, double[] distList) { this(-1, address, distList); } RangeQueryTask(int childNum, long address, double[] distList) { this.childNum = childNum; this.nodeAddress = address; this.distList = distList; } // TODO long nodeAddress; double[] distList; int childNum; } private void visitInternalNode(VPInternalNode node, double[] distList) { /* * for (int i=0; i<node.numChildren(); i++) if (contains(this.oiom, * node.getChildAddress(i), this.q) ) System.out.println("chid " + i + * " contains the query"); */ // check whether this is the first node of a layer, and set some // statistics. if (Debug.debug) // operation of statistics { logger.finest("\n(" + level + ", " + insideLevel + "): [d(q,pivot) : "); } if (Debug.statistics) // operation of statistics { nodeBegining(); internalVisited++; } // ArrayList toSearch = new ArrayList(); //the children to further // search // calculate distances from the query to all pivots final int numPivot = node.numPivots(); if (Debug.statistics) // operation of statistics { // calculate statistics: queryCenterDistance if (tempQueryPivotDistance.length < numPivot) // check whether more // centers/vantage // points emerge. { double[] temp1 = tempQueryPivotDistance; int[] temp2 = childWithPivotCounter; tempQueryPivotDistance = new double[numPivot]; childWithPivotCounter = new int[numPivot]; System.arraycopy(temp1, 0, tempQueryPivotDistance, 0, temp1.length); System.arraycopy(temp2, 0, childWithPivotCounter, 0, temp2.length); for (int i = temp1.length; i < numPivot; i++) { tempQueryPivotDistance[i] = 0; childWithPivotCounter[i] = 0; } } // set the statistics for (int i = 0; i < numPivot; i++) childWithPivotCounter[i]++; pivotDistNum += numPivot; distNum += numPivot; } // search step1: compute distances between pivot and query double queryPivotDistance[] = new double[numPivot]; // add more distance to the distance list if necessary int oldDistListLength = distList.length; if (distList.length < this.maxListLength) { double[] temp = distList; distList = new double[((oldDistListLength + numPivot) <= this.maxListLength) ? (oldDistListLength + numPivot) : this.maxListLength]; System.arraycopy(temp, 0, distList, 0, oldDistListLength); } boolean gotResult = false; for (int pivot = 0; pivot < numPivot; ++pivot) { queryPivotDistance[pivot] = metric.getDistance(q, node.getPivot(pivot)); if (Debug.debug) // operation of statistics { logger.finest("Checking pivot " + pivot + ", d(query, pivot[" + pivot + "] ) =" + queryPivotDistance); } if (Debug.statistics) // operation of statistics { childWithPivotCounter[pivot]++; tempQueryPivotDistance[pivot] += queryPivotDistance[pivot]; } // return pivot as query result if it is satisfies the range query if (queryPivotDistance[pivot] <= r) { this.result.add(new DoubleIndexObjectPair( queryPivotDistance[pivot], node.getPivot(pivot))); // statistics, number of (internal) nodes containing at least // one query result if (!gotResult) { gotResult = true; this.resultNode++; this.resultInternalNode++; } if (Debug.statistics) // operation of statistics { this.pivotAsResult++; } } else { if (Debug.debug) // operation of statistics { logger.finest(", not satisfying the query."); } } // add query pivot distance to list if list is not long enough if (oldDistListLength + pivot < distList.length) distList[oldDistListLength + pivot] = queryPivotDistance[pivot]; } // hack: if node has cght partition, transform distances to d1+d2 an // d1-d2 if (node.GHTDegree < 0) { // for now, must be only two pivots if (numPivot != 2) throw new IllegalArgumentException( "for CGHT partition, there should be only two pivots!"); // transformation to d1+d2, d1-d2 queryPivotDistance[0] = queryPivotDistance[0] + queryPivotDistance[1]; queryPivotDistance[1] = queryPivotDistance[0] - queryPivotDistance[1] * 2; } // search step 2: check each child node. final int numChild = node.numChildren(); if (Debug.debug) // operation of statistics { logger.finest("], children : "); } if (Debug.statistics) // operation of statistics { tempLevelNode += numChild; } // check range for each child and prune if (node.GHTDegree != -1) { for (int i = 0; i < numChild; ++i) { boolean done = false; double[][] range = node.getChildPredicate(i); if (Debug.debug) // operation of statistics { logger.finest("[" + i + ":"); } for (int j = 0; j < numPivot && !done; ++j) { // 1st rule: if upper(child,p) + d(p,q) <= r, then all // points of child are results. // for cght, something similar can be done. for now, it is // omitted for simplicity if (!done && (node.GHTDegree >= 0) && (range[1][j] + queryPivotDistance[j] <= r)) { done = true; List<IndexObject> allResult = getAllPoints(this.oiom, node.getChildAddress(i)); resultNode += getNodeNumber(this.oiom, node.getChildAddress(i)); resultInternalNode += getInternalNodeNumber(this.oiom, node.getChildAddress(i)); resultLeafNode += getLeafNodeNumber(this.oiom, node.getChildAddress(i)); if (Debug.statistics) // operation of statistics { this.nodeWithoutDist++; int temp = allResult.size(); this.resultWithoutDist += temp; this.internalWithoutDist += temp; } if (Debug.debug) // operation of statistics { logger.finest("(" + queryPivotDistance[j] + "+" + range[1][j] + ">" + r + ") : all are results], "); } for (IndexObject o : allResult) result.add(new DoubleIndexObjectPair(-1, o)); } // 2nd rule: // for cght, should use 2r if (!done && ((queryPivotDistance[j] + ((node.GHTDegree < 0) ? 2 * r : r) < range[0][j]) || (queryPivotDistance[j] - ((node.GHTDegree < 0) ? 2 * r : r) > range[1][j]))) { done = true; if (Debug.statistics) // operation of statistics { this.nodePrunedByPivotDist++; int temp = getAllPoints(this.oiom, node.getChildAddress(i)).size(); this.pointPrunedByPivotDist += temp; this.internalPruned += temp; } if (Debug.debug) // operation of statistics { logger.finest("(" + queryPivotDistance[j] + "+" + r + "<" + range[0][j] + ") : pruned], "); } } } // add to task list to be searched if the child has not been // pruned if (!done) { // TODO if (oldDistListLength < distList.length) this.task.add(new RangeQueryTask(node .getChildAddress(i), (double[]) distList .clone())); else task.add(new RangeQueryTask(node.getChildAddress(i), distList)); if (Debug.debug) // operation of statistics { logger.finest("to search], "); } if (Debug.statistics) // operation of statistics { tempLevelNodeVisited += 1; } } } } else // ght search { // prune right: d1 > d2, can be pruned if d(q,p1)+2r <= d(q,p2) if (queryPivotDistance[1] <= -2 * r) { if (Debug.statistics) // operation of statistics { this.nodePrunedByPivotDist++; int temp = getAllPoints(this.oiom, node.getChildAddress(1)) .size(); this.pointPrunedByPivotDist += temp; this.internalPruned += temp; } if (Debug.debug) // operation of statistics { logger.finest("(" + queryPivotDistance[1] + "<= -2*" + r + ") : pruned], "); } } else { // TODO if (oldDistListLength < distList.length) this.task.add(new RangeQueryTask(node.getChildAddress(1), (double[]) distList.clone())); else task.add(new RangeQueryTask(node.getChildAddress(1), distList)); if (Debug.debug) // operation of statistics { logger.finest("to search], "); } if (Debug.statistics) // operation of statistics { tempLevelNodeVisited += 1; } } // prune left: d1 <= d2, can be pruned if d(q,p1) > d(q,p2) + 2r if (queryPivotDistance[1] > 2 * r) { if (Debug.statistics) // operation of statistics { this.nodePrunedByPivotDist++; int temp = getAllPoints(this.oiom, node.getChildAddress(0)) .size(); this.pointPrunedByPivotDist += temp; this.internalPruned += temp; } if (Debug.debug) // operation of statistics { logger.finest("(" + queryPivotDistance[0] + "> 2*" + r + ") : pruned], "); } } else { // TODO if (oldDistListLength < distList.length) this.task.add(new RangeQueryTask(node.getChildAddress(0), (double[]) distList.clone())); else task.add(new RangeQueryTask(node.getChildAddress(0), distList)); if (Debug.debug) // operation of statistics { logger.finest("to search], "); } if (Debug.statistics) // operation of statistics { tempLevelNodeVisited += 1; } } } if (Debug.debug) // operation of statistics { logger.finest(""); } if (Debug.statistics) // operation of statistics { // check whether this is the last node of the layer, and set // statistics nodeEnd(); } } private void visitRootInternalNode(VPInternalNode node, double[] distList) { // calculate distances from the query to all pivots final int numPivot = node.numPivots(); // search step1: compute distances between pivot and query double queryPivotDistance[] = new double[numPivot]; // add more distance to the distance list if necessary int oldDistListLength = distList.length; if (distList.length < this.maxListLength) { double[] temp = distList; distList = new double[((oldDistListLength + numPivot) <= this.maxListLength) ? (oldDistListLength + numPivot) : this.maxListLength]; System.arraycopy(temp, 0, distList, 0, oldDistListLength); } boolean gotResult = false; for (int pivot = 0; pivot < numPivot; ++pivot) { queryPivotDistance[pivot] = metric.getDistance(q, node.getPivot(pivot)); // return pivot as query result if it is satisfies the range query if (queryPivotDistance[pivot] <= r) { this.result.add(new DoubleIndexObjectPair( queryPivotDistance[pivot], node.getPivot(pivot))); // statistics, number of (internal) nodes containing at least // one query result if (!gotResult) { gotResult = true; this.resultNode++; this.resultInternalNode++; } } // add query pivot distance to list if list is not long enough if (oldDistListLength + pivot < distList.length) distList[oldDistListLength + pivot] = queryPivotDistance[pivot]; } if (node.GHTDegree < 0) { // for now, must be only two pivots if (numPivot != 2) throw new IllegalArgumentException( "for CGHT partition, there should be only two pivots!"); // transformation to d1+d2, d1-d2 queryPivotDistance[0] = queryPivotDistance[0] + queryPivotDistance[1]; queryPivotDistance[1] = queryPivotDistance[0] - queryPivotDistance[1] * 2; } // search step 2: check each child node. final int numChild = node.numChildren(); // check range for each child and prune if (node.GHTDegree != -1) { for (int i = 0; i < numChild; ++i) { boolean done = false; double[][] range = node.getChildPredicate(i); for (int j = 0; j < numPivot && !done; ++j) { // 1st rule: if upper(child,p) + d(p,q) <= r, then all // points of child are results. // for cght, something similar can be done. for now, it is // omitted for simplicity if (!done && (node.GHTDegree >= 0) && (range[1][j] + queryPivotDistance[j] <= r)) { done = true; List<IndexObject> allResult = getAllPoints( this.oioms[i + 1], node.getChildAddress(i)); for (IndexObject o : allResult) this.result.add(new DoubleIndexObjectPair(-1, o)); } // 2nd rule: // for cght, should use 2r if (!done && ((queryPivotDistance[j] + ((node.GHTDegree < 0) ? 2 * r : r) < range[0][j]) || (queryPivotDistance[j] - ((node.GHTDegree < 0) ? 2 * r : r) > range[1][j]))) { done = true; } } // add to task list to be searched if the child has not been // pruned if (!done) { if (oldDistListLength < distList.length) this.task.add(new RangeQueryTask(i, node .getChildAddress(i), (double[]) distList .clone())); else this.task.add(new RangeQueryTask(i, node .getChildAddress(i), distList)); } } } else // ght search { // prune right: d1 > d2, can be pruned if d(q,p1)+2r <= d(q,p2) if (queryPivotDistance[1] > -2 * r) { if (oldDistListLength < distList.length) this.task.add(new RangeQueryTask(1, node.getChildAddress(1), (double[]) distList .clone())); else this.task.add(new RangeQueryTask(1, node.getChildAddress(1), distList)); } // prune left: d1 <= d2, can be pruned if d(q,p1) > d(q,p2) + 2r if (queryPivotDistance[1] <= 2 * r) { if (oldDistListLength < distList.length) this.task.add(new RangeQueryTask(0, node.getChildAddress(0), (double[]) distList .clone())); else this.task.add(new RangeQueryTask(0, node.getChildAddress(0), distList)); } } } private void visitLeafNode(VPLeafNode node, double[] distList) { boolean gotResult = false; if (Debug.debug) // operation of statistics { logger.finest("entering a vp leaf node (" + level + ", " + insideLevel + ")\n" + node); } if (Debug.statistics) // operation of statistics { // check whether this is the first node of a layer, and set some // statistics. nodeBegining(); // set statistics tempLevelPoint += node.size(); // final int size = node.size(); if (tempQueryPivotDistance.length < node.numPivots()) // check // whether // more // centers/vantage // points // emerge. { double[] temp1 = tempQueryPivotDistance; int[] temp2 = childWithPivotCounter; tempQueryPivotDistance = new double[node.numPivots()]; childWithPivotCounter = new int[node.numPivots()]; System.arraycopy(temp1, 0, tempQueryPivotDistance, 0, temp1.length); System.arraycopy(temp2, 0, childWithPivotCounter, 0, temp2.length); for (int i = temp1.length; i < node.numPivots(); i++) { tempQueryPivotDistance[i] = 0; childWithPivotCounter[i] = 0; } } } final int distinctSize = node.numChildren(); // check the pivots double[] queryPivotDistance = new double[node.numPivots()]; if (Debug.statistics) // operation of statistics { pivotDistNum += node.numPivots(); distNum += node.numPivots(); } for (int pivot = 0; pivot < node.numPivots(); ++pivot) { queryPivotDistance[pivot] = metric.getDistance(q, node.getPivot(pivot)); if (Debug.statistics) // operation of statistics { childWithPivotCounter[pivot]++; tempQueryPivotDistance[pivot] += queryPivotDistance[pivot]; } if (Debug.debug) // operation of statistics { logger.finest("Checking pivot " + pivot + ", d(query, pivot[" + pivot + "] ) =" + queryPivotDistance); } // return pivot as query result if it is satisfies the range query if (queryPivotDistance[pivot] <= r) { gotResult = true; this.result.add(new DoubleIndexObjectPair( queryPivotDistance[pivot], node.getPivot(pivot))); if (Debug.statistics) // operation of statistics { pivotAsResult++; } } else { if (Debug.debug) // operation of statistics { logger.finest(", not satisfying the query."); } } } // int doneNum = 0; //number of distinct data points that are already // pruned. for (int child = 0; child < distinctSize; child++) { boolean done = false; // 1. try to prune by the path distances double[] pathDist = node.getDataPointPathDistance(child); for (int pivot = 0; pivot < distList.length && pivot < pathDist.length; pivot++) { // if | d(p,c) - d(p,q) | > r, then c can be pruned. if (Math.abs(pathDist[pivot] - distList[pivot]) > r) { if (Debug.statistics) // operation of statistics { this.pointPrunedByPathDist++; } if (Debug.debug) // operation of statistics { logger.finest("child: " + child + ", path distance: " + pivot + ": | " + pathDist[pivot] + " - " + distList[pivot] + " | > " + r + ", pruned!"); } done = true; break; } // if d(p,c) + d(p,q) <= r, then c is a result; if (pathDist[pivot] + distList[pivot] <= r) { if (Debug.statistics) // operation of statistics { this.resultConfirmedByPathDist++; this.resultWithoutDist++; this.leafWithoutDist++; } if (Debug.debug) // operation of statistics { logger.finest("child: " + child + ", path distance: " + pivot + ": " + pathDist[pivot] + " + " + distList[pivot] + " <= " + r + ", is a result!"); } done = true; gotResult = true; this.result.add(new DoubleIndexObjectPair(-1, node .getChild(child))); break; } } if (done) continue; // 2, try to prune by each point's distance to the node pivot, check // in the order of pivot // compute distance with each pivot, then try to prune for (int pivot = 0; pivot < node.numPivots(); pivot++) { double[] dataPivotDistance = node .getDataPointPivotDistance(child); // if | d(p,c) - d(p,q) | > r, then c can be pruned. if (Math.abs(dataPivotDistance[pivot] - queryPivotDistance[pivot]) > r) { if (Debug.statistics) // operation of statistics { this.pointPrunedByPivotDist++; this.leafPruned++; } if (Debug.debug) // operation of statistics { logger.finest("child: " + child + ", pivot distance: " + pivot + ": | " + dataPivotDistance[pivot] + " - " + queryPivotDistance[pivot] + " | > " + r + ", pruned!"); } done = true; break; } // if d(p,c) + d(p,q) <= r, then c is a result; if (dataPivotDistance[pivot] + queryPivotDistance[pivot] <= r) { if (Debug.statistics) // operation of statistics { this.resultWithoutDist++; this.leafWithoutDist++; } if (Debug.debug) // operation of statistics { logger.finest("child: " + child + ", pivot distance: " + pivot + ": " + dataPivotDistance[pivot] + " + " + queryPivotDistance[pivot] + " <= " + r + ", is a result!"); } done = true; gotResult = true; this.result.add(new DoubleIndexObjectPair(-1, node .getChild(child))); break; } } if (done) continue; // 3 the data can not be pruned or sure to be a result, so, compute // distance directly. double distance = metric.getDistance(q, node.getChild(child)); if (Debug.statistics) // operation of statistics { distNum++; this.dataDistNum++; tempLevelPointVisited++; } if (distance <= r) { if (Debug.debug) // operation of statistics { logger.finest("Point: " + child + ": " + distance + " <= " + r + " ), is a result. "); } gotResult = true; this.result.add(new DoubleIndexObjectPair(distance, node .getChild(child))); } else { if (Debug.debug) // operation of statistics { logger.finest("Point: " + child + ": " + distance + " > " + r + " ), is not a result."); } } } if (gotResult) { resultNode++; resultLeafNode++; } if (Debug.statistics) // operation of statistics { // check whether this is the last node of a layer, and set // statistics nodeEnd(); } } public List<IndexObject> searchRangeQuery(double[] distList) { // add the first task this.task.clear(); this.task.add(new RangeQueryTask(this.rootAddress, distList)); List<IndexObject> resultList = new ArrayList<IndexObject>(); while (this.hasNext()) { IndexObject iobject = ((DoubleIndexObjectPair) this.next()) .getObject(); resultList.add(iobject); } return resultList; } void continueSearch() { while ((task.size() > 0) && (result.size() == 0)) { visit((QueryTask) task.remove()); } } // start from root node public void searchResults() { visitRoot((QueryTask) task.removeFirst(), 0); } // distribute task from tasklist to subtrees void distributeTasks() { Map<Integer, Runnable> threadspool = WorkThreadUtil.getThreadsPool(); CountDownLatch latch = null; RangeQueryTask queryTask = null; StringBuffer forprint = new StringBuffer(""); if (WorkThreadUtil.isWaitEachQueryFinished()) { int numLatch; if (this.task.isEmpty()) { numLatch = this.result.isEmpty() ? 0 : 1; } else { numLatch = this.task.size(); } latch = new CountDownLatch(numLatch); } if (!this.result.isEmpty()) { while (!this.result.isEmpty()) { DoubleIndexObjectPair pair = this.result.removeFirst(); forprint.append(pair.getObject()); } if (this.task.isEmpty()) { WorkThread thread = (WorkThread) threadspool.get(0); Task newTask = new GlobalIndexPrintTask(0, forprint.toString(), latch); LinkedList<Task> queue = thread.getQueue(); synchronized (queue) { queue.addLast(newTask); queue.notify(); } } else { queryTask = this.task.removeFirst(); WorkThread thread = (WorkThread) threadspool .get(queryTask.childNum % threadspool.size()); Task newTask = new GlobalIndexWorkTask(thread.getThreadID(), this.metric, queryTask.nodeAddress, queryTask.distList, this.id, this.r, forprint.toString(), latch); LinkedList<Task> queue = thread.getQueue(); synchronized (queue) { queue.addLast(newTask); queue.notify(); } } } while (!this.task.isEmpty()) { queryTask = this.task.removeFirst(); WorkThread thread = (WorkThread) threadspool.get(queryTask.childNum % threadspool.size()); Task newTask = new GlobalIndexWorkTask(thread.getThreadID(), this.metric, queryTask.nodeAddress, queryTask.distList, this.id, this.r, "", latch); LinkedList<Task> queue = thread.getQueue(); synchronized (queue) { queue.addLast(newTask); queue.notify(); } } if (WorkThreadUtil.isWaitEachQueryFinished()) { try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } }