001/* 002 AUTOMATICALLY GENERATED BY jTemp FROM 003 /Users/jsh2/Work/openimaj/target/checkout/machine-learning/nearest-neighbour/src/main/jtemp/org/openimaj/knn/pq/#T#ADCNearestNeighbours.jtemp 004*/ 005/** 006 * Copyright (c) 2011, The University of Southampton and the individual contributors. 007 * All rights reserved. 008 * 009 * Redistribution and use in source and binary forms, with or without modification, 010 * are permitted provided that the following conditions are met: 011 * 012 * * Redistributions of source code must retain the above copyright notice, 013 * this list of conditions and the following disclaimer. 014 * 015 * * Redistributions in binary form must reproduce the above copyright notice, 016 * this list of conditions and the following disclaimer in the documentation 017 * and/or other materials provided with the distribution. 018 * 019 * * Neither the name of the University of Southampton nor the names of its 020 * contributors may be used to endorse or promote products derived from this 021 * software without specific prior written permission. 022 * 023 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 024 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 025 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 026 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 027 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 028 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 029 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 030 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 031 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 032 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 033 */ 034 035 package org.openimaj.knn.pq; 036 037import java.util.ArrayList; 038import java.util.Arrays; 039import java.util.List; 040 041import org.openimaj.citation.annotation.Reference; 042import org.openimaj.citation.annotation.ReferenceType; 043import org.openimaj.knn.ByteNearestNeighbours; 044import org.openimaj.util.pair.IntFloatPair; 045import org.openimaj.util.queue.BoundedPriorityQueue; 046/** 047 * Nearest-neighbours using Asymmetric Distance Computation (ADC) on Product 048 * Quantised vectors. In ADC, only the database points are quantised. The 049 * queries themselves are not quantised. The overall distance is computed as the 050 * summed distance of each subvector of the query to each corresponding 051 * centroids of each database vector. 052 * <p> 053 * For efficiency, the distance of each sub-vector of a query is computed to 054 * every centroid (for the sub-vector under consideration) only once, and is 055 * then cached for the lookup during the computation of the distance to each 056 * database vector. 057 * 058 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 059 */ 060@Reference( 061 type = ReferenceType.Article, 062 author = { "Jegou, Herve", "Douze, Matthijs", "Schmid, Cordelia" }, 063 title = "Product Quantization for Nearest Neighbor Search", 064 year = "2011", 065 journal = "IEEE Trans. Pattern Anal. Mach. Intell.", 066 pages = { "117", "", "128" }, 067 url = "http://dx.doi.org/10.1109/TPAMI.2010.57", 068 month = "January", 069 number = "1", 070 publisher = "IEEE Computer Society", 071 volume = "33", 072 customData = { 073 "issn", "0162-8828", 074 "numpages", "12", 075 "doi", "10.1109/TPAMI.2010.57", 076 "acmid", "1916695", 077 "address", "Washington, DC, USA", 078 "keywords", "High-dimensional indexing, High-dimensional indexing, image indexing, very large databases, approximate search., approximate search., image indexing, very large databases" 079 }) 080public class ByteADCNearestNeighbours extends ByteNearestNeighbours { 081 protected final ByteProductQuantiser pq; 082 protected final int ndims; 083 protected final byte[][] data; 084 085 /** 086 * Construct the ADC with the given quantiser and data points. 087 * 088 * @param pq 089 * the Product Quantiser 090 * @param dataPoints 091 * the data points to index 092 */ 093 public ByteADCNearestNeighbours(ByteProductQuantiser pq, byte[][] dataPoints) { 094 this.pq = pq; 095 this.ndims = dataPoints[0].length; 096 097 this.data = new byte[dataPoints.length][]; 098 for (int i = 0; i < dataPoints.length; i++) { 099 data[i] = pq.quantise(dataPoints[i]); 100 } 101 } 102 103 /** 104 * Construct the ADC with the given quantiser and pre-quantised data . 105 * 106 * @param pq 107 * the Product Quantiser 108 * @param pqData 109 * the pre-quantised data (i.e. vectors already quantised with 110 * the given pq) 111 * @param ndims 112 * the dimensionality of the indexed data 113 */ 114 public ByteADCNearestNeighbours(ByteProductQuantiser pq, byte[][] pqData, int ndims) { 115 this.ndims = ndims; 116 this.pq = pq; 117 this.data = pqData; 118 } 119 120 @Override 121 public void searchNN(final byte [][] qus, int [] indices, float [] distances) { 122 final int N = qus.length; 123 124 final BoundedPriorityQueue<IntFloatPair> queue = 125 new BoundedPriorityQueue<IntFloatPair>(1, IntFloatPair.SECOND_ITEM_ASCENDING_COMPARATOR); 126 127 //prepare working data 128 List<IntFloatPair> list = new ArrayList<IntFloatPair>(2); 129 list.add(new IntFloatPair()); 130 list.add(new IntFloatPair()); 131 132 for (int n=0; n < N; ++n) { 133 List<IntFloatPair> result = search(qus[n], queue, list); 134 135 final IntFloatPair p = result.get(0); 136 indices[n] = p.first; 137 distances[n] = p.second; 138 } 139 } 140 141 @Override 142 public void searchKNN(final byte [][] qus, int K, int [][] indices, float [][] distances) { 143 // Fix for when the user asks for too many points. 144 K = Math.min(K, data.length); 145 146 final int N = qus.length; 147 148 final BoundedPriorityQueue<IntFloatPair> queue = 149 new BoundedPriorityQueue<IntFloatPair>(K, IntFloatPair.SECOND_ITEM_ASCENDING_COMPARATOR); 150 151 //prepare working data 152 List<IntFloatPair> list = new ArrayList<IntFloatPair>(K + 1); 153 for (int i = 0; i < K + 1; i++) { 154 list.add(new IntFloatPair()); 155 } 156 157 // search on each query 158 for (int n = 0; n < N; ++n) { 159 List<IntFloatPair> result = search(qus[n], queue, list); 160 161 for (int k = 0; k < K; ++k) { 162 final IntFloatPair p = result.get(k); 163 indices[n][k] = p.first; 164 distances[n][k] = p.second; 165 } 166 } 167 } 168 169 @Override 170 public void searchNN(final List<byte[]> qus, int [] indices, float [] distances) { 171 final int N = qus.size(); 172 173 final BoundedPriorityQueue<IntFloatPair> queue = 174 new BoundedPriorityQueue<IntFloatPair>(1, IntFloatPair.SECOND_ITEM_ASCENDING_COMPARATOR); 175 176 //prepare working data 177 List<IntFloatPair> list = new ArrayList<IntFloatPair>(2); 178 list.add(new IntFloatPair()); 179 list.add(new IntFloatPair()); 180 181 for (int n=0; n < N; ++n) { 182 List<IntFloatPair> result = search(qus.get(n), queue, list); 183 184 final IntFloatPair p = result.get(0); 185 indices[n] = p.first; 186 distances[n] = p.second; 187 } 188 } 189 190 @Override 191 public void searchKNN(final List<byte[]> qus, int K, int [][] indices, float [][] distances) { 192 // Fix for when the user asks for too many points. 193 K = Math.min(K, data.length); 194 195 final int N = qus.size(); 196 197 final BoundedPriorityQueue<IntFloatPair> queue = 198 new BoundedPriorityQueue<IntFloatPair>(K, IntFloatPair.SECOND_ITEM_ASCENDING_COMPARATOR); 199 200 //prepare working data 201 List<IntFloatPair> list = new ArrayList<IntFloatPair>(K + 1); 202 for (int i = 0; i < K + 1; i++) { 203 list.add(new IntFloatPair()); 204 } 205 206 // search on each query 207 for (int n = 0; n < N; ++n) { 208 List<IntFloatPair> result = search(qus.get(n), queue, list); 209 210 for (int k = 0; k < K; ++k) { 211 final IntFloatPair p = result.get(k); 212 indices[n][k] = p.first; 213 distances[n][k] = p.second; 214 } 215 } 216 } 217 218 @Override 219 public List<IntFloatPair> searchKNN(byte[] query, int K) { 220 // Fix for when the user asks for too many points. 221 K = Math.min(K, data.length); 222 223 final BoundedPriorityQueue<IntFloatPair> queue = 224 new BoundedPriorityQueue<IntFloatPair>(K, IntFloatPair.SECOND_ITEM_ASCENDING_COMPARATOR); 225 226 //prepare working data 227 List<IntFloatPair> list = new ArrayList<IntFloatPair>(K + 1); 228 for (int i = 0; i < K + 1; i++) { 229 list.add(new IntFloatPair()); 230 } 231 232 // search 233 return search(query, queue, list); 234 } 235 236 @Override 237 public IntFloatPair searchNN(final byte[] query) { 238 final BoundedPriorityQueue<IntFloatPair> queue = 239 new BoundedPriorityQueue<IntFloatPair>(1, IntFloatPair.SECOND_ITEM_ASCENDING_COMPARATOR); 240 241 //prepare working data 242 List<IntFloatPair> list = new ArrayList<IntFloatPair>(2); 243 list.add(new IntFloatPair()); 244 list.add(new IntFloatPair()); 245 246 return search(query, queue, list).get(0); 247 } 248 249 private List<IntFloatPair> search(byte[] query, BoundedPriorityQueue<IntFloatPair> queue, List<IntFloatPair> results) { 250 IntFloatPair wp = null; 251 252 // reset all values in the queue to MAX, -1 253 for (final IntFloatPair p : results) { 254 p.second = Float.MAX_VALUE; 255 p.first = -1; 256 wp = queue.offerItem(p); 257 } 258 259 // perform the search 260 computeDistances(query, queue, wp); 261 262 return queue.toOrderedListDestructive(); 263 } 264 265 protected void computeDistances(byte[] fullQuery, BoundedPriorityQueue<IntFloatPair> queue, IntFloatPair wp) { 266 final float[][] distances = new float[pq.assigners.length][]; 267 268 for (int j = 0, from = 0; j < this.pq.assigners.length; j++) { 269 final ByteNearestNeighbours nn = this.pq.assigners[j]; 270 final int to = nn.numDimensions(); 271 final int K = nn.size(); 272 273 final byte[][] qus = { Arrays.copyOfRange(fullQuery, from, from + to) }; 274 final int[][] idx = new int[1][K]; 275 final float[][] dst = new float[1][K]; 276 nn.searchKNN(qus, K, idx, dst); 277 278 distances[j] = new float[K]; 279 for (int k = 0; k < K; k++) { 280 distances[j][idx[0][k]] = dst[0][k]; 281 } 282 283 from += to; 284 } 285 286 for (int i = 0; i < data.length; i++) { 287 wp.first = i; 288 wp.second = 0; 289 290 for (int j = 0; j < this.pq.assigners.length; j++) { 291 final int centroid = this.data[i][j] + 128; 292 wp.second += distances[j][centroid]; 293 } 294 295 wp = queue.offerItem(wp); 296 } 297 } 298 299 @Override 300 public int numDimensions() { 301 return ndims; 302 } 303 304 @Override 305 public int size() { 306 return data.length; 307 } 308}