ch.ethz.globis.phtree.v13.PhQueryKnnHS Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of phtree Show documentation
Show all versions of phtree Show documentation
The PH-Tree is a multi-dimensional index
/*
* Copyright 2011-2016 ETH Zurich. All Rights Reserved.
* Copyright 2016-2018 Tilmann Zäschke. All Rights Reserved.
*
* This software is the proprietary information of ETH Zurich
* and Tilmann Zäschke.
* Use is subject to license terms.
*/
package ch.ethz.globis.phtree.v13;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.PriorityQueue;
import ch.ethz.globis.phtree.PhDistance;
import ch.ethz.globis.phtree.PhEntry;
import ch.ethz.globis.phtree.PhEntryDist;
import ch.ethz.globis.phtree.PhTree.PhKnnQuery;
/**
* kNN query implementation that uses preprocessors and distance functions.
*
* Implementation after Hjaltason and Samet (with some deviations: no MinDist or MaxDist used).
* G. R. Hjaltason and H. Samet., "Distance browsing in spatial databases.", ACM TODS 24(2):265--318. 1999
*
* @param value type
*/
public class PhQueryKnnHS implements PhKnnQuery {
private static final PhDEComp COMP = new PhDEComp();
private final int dims;
private PhTree13 pht;
private PhDistance distance;
private long[] center;
private final ArrayList> results = new ArrayList<>();
private final ArrayList> pool = new ArrayList<>();
private final PriorityQueue> queue = new PriorityQueue<>(COMP);
private final NodeIteratorFullToList iterNode;
private Iterator> iterResult;
private final KnnResultList candidateBuffer;
/**
* Create a new kNN/NNS search instance.
* @param pht the parent tree
*/
public PhQueryKnnHS(PhTree13 pht) {
this.dims = pht.getDim();
this.pht = pht;
//this.iterNode = new NodeIteratorFullNoGC<>(dims, new long[dims]);
this.candidateBuffer = new KnnResultList<>(dims, pool);
this.iterNode = new NodeIteratorFullToList<>(dims);
}
@Override
public long[] nextKey() {
return nextEntryReuse().getKey();
}
@Override
public T nextValue() {
return nextEntryReuse().getValue();
}
@Override
public PhEntryDist nextEntry() {
return iterResult.next();
}
@Override
public PhEntryDist nextEntryReuse() {
//Reusing happens only via pooling
return iterResult.next();
}
@Override
public boolean hasNext() {
return iterResult.hasNext();
}
@Override
public T next() {
return nextValue();
}
@Override
public PhKnnQuery reset(int nMin, PhDistance dist, long... center) {
this.distance = dist == null ? this.distance : dist;
this.center = center;
//TODO pool entries??/
this.queue.clear();
this.results.clear();
if (nMin <= 0 || pht.size() == 0) {
iterResult = Collections.>emptyList().iterator();
return this;
}
//Initialize queue
//d=0 (lies in Node!!!)
PhEntryDist rootE = createEntry(pool, new long[dims], null, 0);
rootE.setNodeInternal(pht.getRoot());
this.queue.add(rootE);
search(nMin);
iterResult = results.iterator();
return this;
}
private void search(int k) {
while (!queue.isEmpty()) {
PhEntryDist candidate = queue.poll();
if (!candidate.hasNodeInternal()) {
//data entry
results.add(candidate);
if (results.size() >= k) {
return;
}
} else {
//inner node
Node node = (Node) candidate.getNodeInternal();
candidateBuffer.clear();
iterNode.init(node, candidateBuffer, candidate.getKey());
for (int i = 0; i < candidateBuffer.size(); i++) {
PhEntryDist e2 = candidateBuffer.get(i);
if (e2.hasNodeInternal()) {
Node sub = (Node) e2.getNodeInternal();
double d = distToNode(e2.getKey(), sub.getPostLen() + 1);
e2.setDist(d);
} else {
double d = distance.dist(center, e2.getKey());
e2.setDist(d);
}
queue.add(e2);
}
pool.add(candidate);
}
}
}
private static PhEntryDist createEntry(ArrayList> pool,
long[] key, T val, double dist) {
if (pool.isEmpty()) {
return new PhEntryDist(key, val, dist);
}
PhEntryDist e = pool.remove(pool.size() - 1);
e.setKeyInternal(key);
e.set(val, dist);
return e;
}
private double distToNode(long[] prefix, int bitsToIgnore) {
long maskMin = (-1L) << bitsToIgnore;
long maskMax = ~maskMin;
long[] buf = new long[prefix.length];
for (int i = 0; i < buf.length; i++) {
//if v is outside the node, return distance to closest edge,
//otherwise return v itself (assume possible distance=0)
long min = prefix[i] & maskMin;
long max = prefix[i] | maskMax;
buf[i] = min > center[i] ? min : (max < center[i] ? max : center[i]);
}
return distance.dist(center, buf);
}
private static class PhDEComp implements Comparator> {
@Override
public int compare(PhEntryDist> a, PhEntryDist> b) {
double d1 = a.dist();
double d2 = b.dist();
return d1 < d2 ? -1 : d1 > d2 ? 1 : 0;
}
}
static class KnnResultList extends PhResultList> {
private final ArrayList> list;
private PhEntryDist free;
private final ArrayList> pool;
public KnnResultList(int dims, ArrayList> pool) {
this.list = new ArrayList<>();
this.pool = pool;
this.free = createEntry(pool, new long[dims], null, 0);
}
@Override
public int size() {
return list.size();
}
@Override
public void clear() {
list.clear();
}
@Override
public PhEntryDist get(int index) {
return list.get(index);
}
@Override
PhEntryDist phGetTempEntry() {
PhEntryDist ret = free;
free = null;
return ret;
}
@Override
void phReturnTemp(PhEntry entry) {
if (free == null) {
free = (PhEntryDist) entry;
}
}
@Override
void phOffer(PhEntry e) {
list.add((PhEntryDist) e);
free = createEntry(pool, new long[e.getKey().length], null, 0);
}
@Override
boolean phIsPrefixValid(long[] prefix, int bitsToIgnore) {
return true;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy