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

com.harium.etyl.storage.kdtree.KDTree Maven / Gradle / Ivy

package com.harium.etyl.storage.kdtree;

import java.io.Serializable;
import java.util.List;
import java.util.LinkedList;
import java.util.Stack;

/**
 * KDTree is a class supporting KD-tree insertion, deletion, equality
 * search, range search, and nearest neighbor(s) using double-precision
 * floating-point keys.  Splitting dimension is chosen naively, by
 * depth modulo K.  Semantics are as follows:
 *
 * 
    *
  • Two different keys containing identical numbers should retrieve the * same value from a given KD-tree. Therefore keys are cloned when a * node is inserted. *

    *
  • As with Hashtables, values inserted into a KD-tree are not * cloned. Modifying a value between insertion and retrieval will * therefore modify the value stored in the tree. *
* * Implements the Nearest Neighbor algorithm (Table 6.4) of * *
 * &*064;techreport{AndrewMooreNearestNeighbor,
 *   author  = {Andrew Moore},
 *   title   = {An introductory tutorial on kd-trees},
 *   institution = {Robotics Institute, Carnegie Mellon University},
 *   year    = {1991},
 *   number  = {Technical Report No. 209, Computer Laboratory, 
 *              University of Cambridge},
 *   address = {Pittsburgh, PA}
 * }
 * 
* * Copyright (C) Simon D. Levy and Bjoern Heckel 2014 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This code 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 Lesser General Public License * along with this code. If not, see . * You should also have received a copy of the Parrot Parrot AR.Drone * Development License and Parrot AR.Drone copyright notice and disclaimer * and If not, see * * and * . */ public class KDTree implements Serializable { // number of milliseconds final long m_timeout; // K = number of dimensions final private int m_K; // root of KD-tree private KDNode m_root; // count of nodes private int m_count; /** * Creates a KD-tree with specified number of dimensions. * * @param k number of dimensions */ public KDTree(int k) { this(k, 0); } public KDTree(int k, long timeout) { this.m_timeout = timeout; m_K = k; m_root = null; } /** * Insert a node in a KD-tree. Uses algorithm translated from 352.ins.c of * *
	 *   Book{GonnetBaezaYates1991,
	 *     author =    {G.H. Gonnet and R. Baeza-Yates},
	 *     title =     {Handbook of Algorithms and Data Structures},
	 *     publisher = {Addison-Wesley},
	 *     year =      {1991}
	 *   }
	 *   
* * @param key key for KD-tree node * @param value value at that key * * @throws KeySizeException if key.length mismatches K * @throws KeyDuplicateException if key already in tree */ public void insert(double [] key, T value) throws KeySizeException, KeyDuplicateException { this.edit(key, new Editor.Inserter(value)); } /** * Edit a node in a KD-tree * * @param key key for KD-tree node * @param editor object to edit the value at that key * * @throws KeySizeException if key.length mismatches K * @throws KeyDuplicateException if key already in tree */ public void edit(double [] key, Editor editor) throws KeySizeException, KeyDuplicateException { if (key.length != m_K) { throw new KeySizeException(); } synchronized (this) { // the first insert has to be synchronized if (null == m_root) { m_root = KDNode.create(new HPoint(key), editor); m_count = m_root.deleted ? 0 : 1; return; } } m_count += KDNode.edit(new HPoint(key), editor, m_root, 0, m_K); } /** * Find KD-tree node whose key is identical to key. Uses algorithm * translated from 352.srch.c of Gonnet & Baeza-Yates. * * @param key key for KD-tree node * * @return object at key, or null if not found * * @throws KeySizeException if key.length mismatches K */ public T search(double [] key) throws KeySizeException { if (key.length != m_K) { throw new KeySizeException(); } KDNode kd = KDNode.srch(new HPoint(key), m_root, m_K); return (kd == null ? null : kd.v); } public void delete(double [] key) throws KeySizeException, KeyMissingException { delete(key, false); } /** * Delete a node from a KD-tree. Instead of actually deleting node and * rebuilding tree, marks node as deleted. Hence, it is up to the caller * to rebuild the tree as needed for efficiency. * * @param key key for KD-tree node * @param optional if false and node not found, throw an exception * * @throws KeySizeException if key.length mismatches K * @throws KeyMissingException if no node in tree has key */ public void delete(double [] key, boolean optional) throws KeySizeException, KeyMissingException { if (key.length != m_K) { throw new KeySizeException(); } KDNode t = KDNode.srch(new HPoint(key), m_root, m_K); if (t == null) { if (optional == false) { throw new KeyMissingException(); } } else { if (KDNode.del(t)) { m_count--; } } } /** * Find KD-tree node whose key is nearest neighbor to * key. * * @param key key for KD-tree node * * @return object at node nearest to key, or null on failure * * @throws KeySizeException if key.length mismatches K */ public T nearest(double [] key) throws KeySizeException { List nbrs = nearest(key, 1, null); return nbrs.get(0); } /** * Find KD-tree nodes whose keys are n nearest neighbors to * key. * * @param key key for KD-tree node * @param n number of nodes to return * * @return objects at nodes nearest to key, or null on failure * * @throws KeySizeException if key.length mismatches K */ public List nearest(double [] key, int n) throws KeySizeException, IllegalArgumentException { return nearest(key, n, null); } /** * Find KD-tree nodes whose keys are within a given Euclidean distance of * a given key. * * @param key key for KD-tree node * @param d Euclidean distance * * @return objects at nodes with distance of key, or null on failure * * @throws KeySizeException if key.length mismatches K */ public List nearestEuclidean(double [] key, double dist) throws KeySizeException { return nearestDistance(key, dist, new EuclideanDistance()); } /** * Find KD-tree nodes whose keys are within a given Hamming distance of * a given key. * * @param key key for KD-tree node * @param d Hamming distance * * @return objects at nodes with distance of key, or null on failure * * @throws KeySizeException if key.length mismatches K */ public List nearestHamming(double [] key, double dist) throws KeySizeException { return nearestDistance(key, dist, new HammingDistance()); } /** * Find KD-tree nodes whose keys are n nearest neighbors to * key. Uses algorithm above. Neighbors are returned in ascending * order of distance to key. * * @param key key for KD-tree node * @param n how many neighbors to find * @param checker an optional object to filter matches * * @return objects at node nearest to key, or null on failure * * @throws KeySizeException if key.length mismatches K * @throws IllegalArgumentException if n is negative or * exceeds tree size */ public List nearest(double [] key, int n, Checker checker) throws KeySizeException, IllegalArgumentException { if (n <= 0) { return new LinkedList(); } NearestNeighborList> nnl = getnbrs(key, n, checker); n = nnl.getSize(); Stack nbrs = new Stack(); for (int i=0; i kd = nnl.removeHighest(); nbrs.push(kd.v); } return nbrs; } /** * Range search in a KD-tree. Uses algorithm translated from * 352.range.c of Gonnet & Baeza-Yates. * * @param lowk lower-bounds for key * @param uppk upper-bounds for key * * @return array of Objects whose keys fall in range [lowk,uppk] * * @throws KeySizeException on mismatch among lowk.length, uppk.length, or K */ public List range(double [] lowk, double [] uppk) throws KeySizeException { if (lowk.length != uppk.length) { throw new KeySizeException(); } else if (lowk.length != m_K) { throw new KeySizeException(); } else { List> found = new LinkedList>(); KDNode.rsearch(new HPoint(lowk), new HPoint(uppk), m_root, 0, m_K, found); List o = new LinkedList(); for (KDNode node : found) { o.add(node.v); } return o; } } public int size() { /* added by MSL */ return m_count; } public boolean isEmpty() { return size() == 0; } public String toString() { return m_root.toString(0); } private NearestNeighborList> getnbrs(double [] key) throws KeySizeException { return getnbrs(key, m_count, null); } private NearestNeighborList> getnbrs(double [] key, int n, Checker checker) throws KeySizeException { if (key.length != m_K) { throw new KeySizeException(); } NearestNeighborList> nnl = new NearestNeighborList>(n); // initial call is with infinite hyper-rectangle and max distance HRect hr = HRect.infiniteHRect(key.length); double max_dist_sqd = Double.MAX_VALUE; HPoint keyp = new HPoint(key); if (m_count > 0) { long timeout = (this.m_timeout > 0) ? (System.currentTimeMillis() + this.m_timeout) : 0; KDNode.nnbr(m_root, keyp, hr, max_dist_sqd, 0, m_K, nnl, checker, timeout); } return nnl; } private List nearestDistance(double [] key, double dist, DistanceMetric metric) throws KeySizeException { NearestNeighborList> nnl = getnbrs(key); int n = nnl.getSize(); Stack nbrs = new Stack(); for (int i=0; i kd = nnl.removeHighest(); HPoint p = kd.k; if (metric.distance(p.coord, key) < dist) { nbrs.push(kd.v); } } return nbrs; } public void clear() { m_root = null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy