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}