View Javadoc

1   package net.obsearch.query;
2   import java.util.List;
3   import java.util.LinkedList;
4   import java.util.HashSet;
5   import java.util.Set;
6   import net.obsearch.ob.OBShort;
7   import net.obsearch.result.OBResultShort;
8   import net.obsearch.result.OBPriorityQueueShort;
9   import net.obsearch.exception.OBException;
10  import net.obsearch.AbstractOBResult;
11  import net.obsearch.asserts.OBAsserts;
12  /*
13      OBSearch: a distributed similarity search engine
14      This project is to similarity search what 'bit-torrent' is to downloads.
15      Copyright (C)  2007 Arnoldo Jose Muller Molina
16  
17    	This program is free software: you can redistribute it and/or modify
18      it under the terms of the GNU General Public License as published by
19      the Free Software Foundation, either version 3 of the License, or
20      (at your option) any later version.
21  
22      This program is distributed in the hope that it will be useful,
23      but WITHOUT ANY WARRANTY; without even the implied warranty of
24      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25      GNU General Public License for more details.
26  
27      You should have received a copy of the GNU General Public License
28      along with this program.  If not, see <http://www.gnu.org/licenses/>.
29   */
30  /**
31   * Object used to store a query request.
32   * @author Arnoldo Jose Muller Molina
33   * @since 0.7
34   */
35  
36  
37  public final class OBQueryShort<O extends OBShort> extends AbstractOBQuery<O> {
38  
39  
40  		private OBResultShort<O> query;
41      /**
42       * Holds the results for the query.
43       */
44      protected OBPriorityQueueShort<O> result;
45  
46      /**
47       * Minimum part of the rectangle of the query.
48       */ 
49  		protected short[] min;
50  
51  		
52  		/**
53       * Maximum part of the rectangle of the query.
54       */ 
55  		protected short[] max;
56  
57  		
58  		/**
59       * SMAPed vector
60       */ 
61  
62  		protected short[] smap;
63  
64      /**
65       * Constructor.
66       */
67      public OBQueryShort(){
68      }
69  
70  		public O getObject(){
71  				return query.getObject();
72  		}
73  
74      /**
75       * Creates a new OBQueryShort object.
76       * @param object
77       *            The object that will be matched.
78       * @param range
79       *            The range to be used for the match.
80       * @param result
81       *            The priority queue were the results will be stored.
82       */
83      public OBQueryShort(O object, short range, OBPriorityQueueShort<O> result){
84  
85          query = new OBResultShort<O>(object,-1,range);
86          this.result = result;
87      }
88  
89  		public OBQueryShort(O object, OBPriorityQueueShort<O> result){
90  				this(object, Short.MAX_VALUE, result);
91  		}
92  
93  		
94  
95  		/**
96  		 * Returns true if the given rectangle collides
97       * with this query.
98       * @param rectangle The rectangle to search.
99       */
100 		public boolean collides(short[][] rectangle){
101 				short[] minOther = rectangle[0];
102 				short[] maxOther = rectangle[1];
103 				boolean res = true;
104 				assert minOther.length == smap.length;
105 				int i = 0;
106 				while (i < minOther.length) {
107 				    if(max[i] < minOther[i] || min[i] > maxOther[i]){
108 								res =false;
109 								break;
110 						}
111 				    i++;
112 				}
113 				return res;
114 		}
115 
116 		/**
117      * Creates a new OBQueryShort object.
118      * @param object
119      *            The object that will be matched.
120      * @param range
121      *            The range to be used for the match.
122      * @param result
123      *            The priority queue were the results will be stored.
124 		 * @param smap 
125      *            SMAP vector representation of the given object.
126      */
127     public OBQueryShort(O object, short range, OBPriorityQueueShort<O> result, short[] smap){
128 
129         this(object,range,result);
130 				this.smap = smap;
131 				if(smap != null){
132 						min = new short[smap.length];
133 				max = new short[smap.length];
134 				}
135 				updateRectangle();
136 				
137     }
138 
139 		private void updateRectangle(){
140 				if(smap != null){
141 				int i = 0;
142 				while (i < smap.length) {
143 				    min[i] = (short)Math.max(smap[i] - query.getDistance(), 0);
144 						max[i] = (short)Math.min(smap[i] + query.getDistance(), Short.MAX_VALUE);
145 				    i++;
146 				}
147 				}
148 		}
149 
150 		/**
151 		 * Return low of the query rectangle.
152      */ 
153 		public short[] getLow(){
154 				return min;
155 		}
156 
157 		/**
158 		 * Return low of the query rectangle.
159      */ 
160 		public short[] getHigh(){
161 				return max;
162 		}
163 
164     /**
165      * @return The current results of the matching.
166      */
167     public OBPriorityQueueShort<O> getResult(){
168         return result;
169     }
170 
171     /**
172      * Set the results of the matching to a new object.
173      * @param result
174      *            The new result.
175      */
176     public void setResult(OBPriorityQueueShort<O> result){
177         this.result = result;
178     }
179 
180    /**
181     * Returns true if we should calculate the real distance.
182     * @param smapDistance The lower-resolution distance calculated
183     * with SMAP.
184     * 
185     */
186     public boolean isCandidate(short smapDistance){
187         return smapDistance <= query.getDistance() && result.isCandidate(smapDistance);
188     }
189 
190 
191 		public  boolean add(long id, O object) throws InstantiationException, IllegalAccessException , OBException{
192 				return add(id, object,  query.getObject().distance(object));
193 		}
194 
195 		public short getDistance(){
196 				return query.getDistance();
197 		}
198 
199 		public List<AbstractOBResult<O>> getSortedElements(){
200 				List<AbstractOBResult<O>> res = new LinkedList<AbstractOBResult<O>>();
201 				for(OBResultShort<O> r : result.getSortedElements()){
202 						res.add(r);
203 				}
204 				return res;	
205 		}
206 
207    /**
208     * Add the given object, object id and distance of type float to the
209     * queue. Updates the range of the query as needed if the range
210     * shrinks after this insertion.
211     * @param id
212     *            The id of the object to be used
213     * @param obj
214     *            The object to be added
215     * @param d
216     *            The distance to be added
217     * @throws IllegalAccessException
218     *             If there is a problem when instantiating objects O
219     * @throws InstantiationException
220     *             If there is a problem when instantiating objects O
221     */
222     public boolean add(long id, O obj, short d) throws InstantiationException, IllegalAccessException {
223 				if(d <= currentRange()){
224 				boolean res = result.add(id,obj,d);
225 				short temp = result.updateRange(query.getDistance());
226 				if(temp != query.getDistance()){
227   					query.setDistance( (short)(temp-(short)1));
228             if(query.getDistance() < 0){
229 								query.setDistance( (short)0);
230 						}
231 
232 
233 						updateRectangle();
234         }
235 				return res;
236 				}else{
237 						return false;
238 				}
239 		}
240 
241 		private short currentRange(){
242 				if(result.getSize() == result.getK()){
243 						//						assert result.peek().getDistance() <= query.getDistance();
244 						return (short)Math.min(query.getDistance(), result.peek().getDistance());
245 				}else{
246 						return query.getDistance();
247 				}
248 		}
249 
250 		/**
251 		 * Returns true if the originalRange has been modified.
252 		 * @return true If the current range (getDistance()) is different than originalRange.
253 		 */
254 		public boolean updatedRange(short originalRange){
255 				return query.getDistance() != originalRange;
256 		}
257 
258 		/**
259 		 * @return true if the underlying priority queue's size is equal to k
260      */
261 		public boolean isFull(){
262 				return result.isFull();
263 		}
264 
265 
266 		public  double recall(List<AbstractOBResult<O>> perfectQuery){
267 				int hits = 0;
268 				Set<AbstractOBResult<O>> s = new HashSet<AbstractOBResult<O>>();
269 				for(OBResultShort<O> r : this.result.getSortedElements()){			
270 						int i = 0;
271 						for(AbstractOBResult<O> d : perfectQuery){
272 								if(! s.contains(d) && d.compareTo(r) == 0){
273 										s.add(d);
274 										hits++;
275 										break;
276 								}
277 								i++;
278 						}
279 				}
280 				return (double)hits / (double)perfectQuery.size();
281 		}
282 
283 
284 		public  double ep(List<AbstractOBResult<O>> dbin){
285 				List<OBResultShort<O>> query = getResult().getSortedElements();
286 				int i = 0;
287 				int result = 0;
288 				Set<OBResultShort<O>> s = new HashSet<OBResultShort<O>>();
289 				for(OBResultShort<O> r : query){
290 						// find the position in the db. 
291 						int cx = 0;
292 						for(AbstractOBResult<O> cr : dbin){
293 								OBResultShort<O> c = (OBResultShort<O>)cr;
294 								if(! s.contains(c) &&c.compareTo(r) == 0){
295 										s.add(c);
296 										result += cx - i;
297 										break;
298 								}
299 								cx++;
300 						}
301 						i++;
302 				}
303 				if(query.size() == 0){
304 						return 0;
305 				}else{
306 						double res = ((double)result)/ ((double)(query.size() * dbin.size()));
307 						return res;
308 				}
309 		}
310 
311 
312 		/**
313      * Calculate the EP value for a sorted list of distances.
314      */
315 		public  double ep(short[] dbin){
316 				return epAux(dbin) * (1 / (double)getResult().getSize());
317 		}
318 
319 		
320 				/**
321 	 * Calculate the 1 + E  or (c for Adonis et al) for approx. nearest neighbor
322 	 * This is the approximation and "real" is the real result.
323 	 * @param q
324 	 * @return
325 	 * @throws RAException
326 	 */
327 		public double approx(short[] dbin) throws OBException {
328 
329 				List<OBResultShort<O>> query = getResult().getSortedElements();
330 		// get the last guys
331 		if(! this.isFull()){
332 				return 1;
333 		}
334 		
335 		int last = query.size() - 1;
336 		return Math.abs((query.get(last).getDistance()) / dbin[last]);
337 	}
338 
339 		/**
340      * Calculate the EP value for a sorted list of distances.
341 		 * Does not multiply by 1/k
342      */
343 		private  double epAux(short[] dbin){
344 				List<OBResultShort<O>> query = getResult().getSortedElements();
345 				int i = 0;
346 				int result = 0;
347         // hold the visited elements
348 				Set<Integer> s = new HashSet<Integer>();
349 				for(OBResultShort<O> r : query){
350 						// find the position in the db. 
351 						int cx = 0;
352 						for(short cr : dbin){								
353 								if(! s.contains(cx) && cr == r.getDistance()){
354 										s.add(cx);
355 										result += cx - i;
356 										break;
357 								}
358 								cx++;
359 						}
360 						i++;
361 				}
362 				if(query.size() == 0){
363 						return 0;
364 				}else{
365 						double res = ((double)result)/ ((double)(dbin.length));
366 						return res;
367 				}
368 		}
369 
370 
371 		/**
372 		 * Calculates ep without multiplying by 1/k and 
373 		 */ 
374 		public  double compound(short[] dbin){
375 				List<OBResultShort<O>> query = getResult().getSortedElements();
376 				int i = 0;
377 				
378 				double res = 0;
379         // hold the visited elements
380 				Set<Integer> s = new HashSet<Integer>();
381 				for(OBResultShort<O> r : query){
382 
383 						double ep = 1 - (((double)rank(r.getDistance(), dbin) - (i))/(double)(dbin.length));
384 						// fix rounding error
385 						if(r.getDistance() == 0){
386 								assert dbin[i] == 0;
387 								res += 1;
388 						}else{
389 								res += ((double)dbin[i] / (double)r.getDistance()) * ep;
390 						}
391 						i++;
392 				}
393 				return res / (double) query.size();
394 		}
395 
396 		/*
397 		 * Calculates the relative distance error.
398 		 */
399     public double rde(short[] dbin){
400 				return rdeAux(dbin) / (double) getResult().getSize();
401 		}
402 
403 		/*
404 		 * Calculates the relative distance error without dividing by 1/k
405 		 */
406 		private  double rdeAux(short[] dbin){
407 				List<OBResultShort<O>> query = getResult().getSortedElements();
408 				int i = 0;
409 				double result = 0;
410         // hold the visited elements
411 				for(OBResultShort<O> r : query){
412 						// find the position in the db. 
413 						if(dbin[i] != 0){
414 								result +=  ( (double) r.getDistance() / (double)dbin[i]) - 1; 
415 						}
416 						i++;
417 				}
418 				return result;
419 		}
420 
421 
422 		/**
423 		 * Total distance ratio
424 		 */
425 		public  double tDR(short[] dbin){
426 				List<OBResultShort<O>> query = getResult().getSortedElements();
427 				int i = 0;
428 				double up = 0;
429 				double down = 0;
430         // hold the visited elements
431 				for(OBResultShort<O> r : query){
432 						// find the position in the db. 
433 						up += (double)dbin[i];
434 						down += (double) r.getDistance();
435 						i++;
436 				}
437 				return up / down;
438 		}
439 
440 		/** 
441      * Calculates the precision
442 		 */
443 		public double precision(short[] dbin){
444 				List<OBResultShort<O>> query = getResult().getSortedElements();
445 				int count = 0;
446         // hold the visited elements
447 				for(OBResultShort<O> r : query){
448 						if(rank(r.getDistance(), dbin) < query.size()) {
449 								count++;
450 						}
451 				}
452 				return (double) count / (double) query.size();
453 		}
454 
455 		/**
456 		 * find the position of distance in the given dbin list
457 		 */ 
458 		private int rank(short distance, short[] dbin){
459 				int i = 0;
460 				while(i < dbin.length){
461 						if(distance == dbin[i]){
462 								break;
463 						}
464 						i++;
465 				}
466 				return i;
467 		}
468 
469 		/**
470 		 * peek to the largest value if the queue is full.
471 		 */
472 		public short peek() throws OBException{
473 				OBAsserts.chkAssert(isFull(), "Queue is not full");
474 				return result.peek().getDistance();
475 		}
476 		
477 }
478 
479