All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.ethz.globis.phtree.v13.PhQueryKnnHS Maven / Gradle / Ivy

There is a newer version: 2.8.1
Show newest version
/*
 * 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