Similarity.java
001 package gate.creole.annic.apache.lucene.search;
002 
003 /**
004  * Copyright 2004 The Apache Software Foundation
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     http://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 
019 import java.io.IOException;
020 
021 import java.util.Collection;
022 import java.util.Iterator;
023 
024 import gate.creole.annic.apache.lucene.index.Term;
025 
026 import gate.creole.annic.apache.lucene.index.IndexReader;       // for javadoc
027 import gate.creole.annic.apache.lucene.index.IndexWriter;       // for javadoc
028 import gate.creole.annic.apache.lucene.document.Field;          // for javadoc
029 
030 
031 /** Expert: Scoring API.
032  <p>Subclasses implement search scoring.
033  *
034  <p>The score of query <code>q</code> for document <code>d</code> is defined
035  * in terms of these methods as follows:
036  *
037  <table cellpadding="0" cellspacing="0" border="0">
038  *  <tr>
039  *    <td valign="middle" align="right" rowspan="2">score(q,d) =<br></td>
040  *    <td valign="middle" align="center">
041  *    <big><big><big><big><big>&Sigma;</big></big></big></big></big></td>
042  *    <td valign="middle"><small>
043  *    {@link #tf(int) tf}(t in d) *
044  *    {@link #idf(Term,Searcher) idf}(t) *
045  *    {@link Field#getBoost getBoost}(t.field in d) *
046  *    {@link #lengthNorm(String,int) lengthNorm}(t.field in d)
047  *    </small></td>
048  *    <td valign="middle" rowspan="2">&nbsp;*
049  *    {@link #coord(int,int) coord}(q,d) *
050  *    {@link #queryNorm(float) queryNorm}(q)
051  *    </td>
052  *  </tr>
053  *  <tr>
054  *   <td valign="top" align="right">
055  *    <small>t in q</small>
056  *    </td>
057  *  </tr>
058  </table>
059  *
060  @see #setDefault(Similarity)
061  @see IndexWriter#setSimilarity(Similarity)
062  @see Searcher#setSimilarity(Similarity)
063  */
064 @SuppressWarnings("rawtypes")
065 public abstract class Similarity {
066   /** The Similarity implementation used by default. */
067   private static Similarity defaultImpl = new DefaultSimilarity();
068 
069   /** Set the default Similarity implementation used by indexing and search
070    * code.
071    *
072    @see Searcher#setSimilarity(Similarity)
073    @see IndexWriter#setSimilarity(Similarity)
074    */
075   public static void setDefault(Similarity similarity) {
076     Similarity.defaultImpl = similarity;
077   }
078 
079   /** Return the default Similarity implementation used by indexing and search
080    * code.
081    *
082    <p>This is initially an instance of {@link DefaultSimilarity}.
083    *
084    @see Searcher#setSimilarity(Similarity)
085    @see IndexWriter#setSimilarity(Similarity)
086    */
087   public static Similarity getDefault() {
088     return Similarity.defaultImpl;
089   }
090 
091   /** Cache of decoded bytes. */
092   private static final float[] NORM_TABLE = new float[256];
093 
094   static {
095     for (int i = 0; i < 256; i++)
096       NORM_TABLE[i= byteToFloat((byte)i);
097   }
098 
099   /** Decodes a normalization factor stored in an index.
100    @see #encodeNorm(float)
101    */
102   public static float decodeNorm(byte b) {
103     return NORM_TABLE[b & 0xFF];
104   }
105 
106   /** Computes the normalization value for a field given the total number of
107    * terms contained in a field.  These values, together with field boosts, are
108    * stored in an index and multipled into scores for hits on each field by the
109    * search code.
110    *
111    <p>Matches in longer fields are less precise, so implemenations of this
112    * method usually return smaller values when <code>numTokens</code> is large,
113    * and larger values when <code>numTokens</code> is small.
114    *
115    <p>That these values are computed under {@link
116    * IndexWriter#addDocument(Document)} and stored then using
117    * {#encodeNorm(float)}.  Thus they have limited precision, and documents
118    * must be re-indexed if this method is altered.
119    *
120    @param fieldName the name of the field
121    @param numTokens the total number of tokens contained in fields named
122    <i>fieldName</i> of <i>doc</i>.
123    @return a normalization factor for hits on this field of this document
124    *
125    @see Field#setBoost(float)
126    */
127   public abstract float lengthNorm(String fieldName, int numTokens);
128 
129   /** Computes the normalization value for a query given the sum of the squared
130    * weights of each of the query terms.  This value is then multipled into the
131    * weight of each query term.
132    *
133    <p>This does not affect ranking, but rather just attempts to make scores
134    * from different queries comparable.
135    *
136    @param sumOfSquaredWeights the sum of the squares of query term weights
137    @return a normalization factor for query weights
138    */
139   public abstract float queryNorm(float sumOfSquaredWeights);
140 
141   /** Encodes a normalization factor for storage in an index.
142    *
143    <p>The encoding uses a five-bit exponent and three-bit mantissa, thus
144    * representing values from around 7x10^9 to 2x10^-9 with about one
145    * significant decimal digit of accuracy.  Zero is also represented.
146    * Negative numbers are rounded up to zero.  Values too large to represent
147    * are rounded down to the largest representable value.  Positive values too
148    * small to represent are rounded up to the smallest positive representable
149    * value.
150    *
151    @see Field#setBoost(float)
152    */
153   public static byte encodeNorm(float f) {
154     return floatToByte(f);
155   }
156 
157   private static float byteToFloat(byte b) {
158     if (b == 0)                                   // zero is a special case
159       return 0.0f;
160     int mantissa = b & 7;
161     int exponent = (b >> 331;
162     int bits = ((exponent+(63-15)) << 24(mantissa << 21);
163     return Float.intBitsToFloat(bits);
164   }
165 
166   private static byte floatToByte(float f) {
167     if (f < 0.0f)                                 // round negatives up to zero
168       f = 0.0f;
169 
170     if (f == 0.0f)                                // zero is a special case
171       return 0;
172 
173     int bits = Float.floatToIntBits(f);           // parse float into parts
174     int mantissa = (bits & 0xffffff>> 21;
175     int exponent = (((bits >> 240x7f6315;
176 
177     if (exponent > 31) {                          // overflow: use max value
178       exponent = 31;
179       mantissa = 7;
180     }
181 
182     if (exponent < 0) {                           // underflow: use min value
183       exponent = 0;
184       mantissa = 1;
185     }
186 
187     return (byte)((exponent << 3| mantissa);    // pack into a byte
188    }
189 
190 
191   /** Computes a score factor based on a term or phrase's frequency in a
192    * document.  This value is multiplied by the {@link #idf(Term, Searcher)}
193    * factor for each term in the query and these products are then summed to
194    * form the initial score for a document.
195    *
196    <p>Terms and phrases repeated in a document indicate the topic of the
197    * document, so implementations of this method usually return larger values
198    * when <code>freq</code> is large, and smaller values when <code>freq</code>
199    * is small.
200    *
201    <p>The default implementation calls {@link #tf(float)}.
202    *
203    @param freq the frequency of a term within a document
204    @return a score factor based on a term's within-document frequency
205    */
206   public float tf(int freq) {
207     return tf((float)freq);
208   }
209 
210   /** Computes the amount of a sloppy phrase match, based on an edit distance.
211    * This value is summed for each sloppy phrase match in a document to form
212    * the frequency that is passed to {@link #tf(float)}.
213    *
214    <p>A phrase match with a small edit distance to a document passage more
215    * closely matches the document, so implementations of this method usually
216    * return larger values when the edit distance is small and smaller values
217    * when it is large.
218    *
219    @see PhraseQuery#setSlop(int)
220    @param distance the edit distance of this sloppy phrase match
221    @return the frequency increment for this match
222    */
223   public abstract float sloppyFreq(int distance);
224 
225   /** Computes a score factor based on a term or phrase's frequency in a
226    * document.  This value is multiplied by the {@link #idf(Term, Searcher)}
227    * factor for each term in the query and these products are then summed to
228    * form the initial score for a document.
229    *
230    <p>Terms and phrases repeated in a document indicate the topic of the
231    * document, so implemenations of this method usually return larger values
232    * when <code>freq</code> is large, and smaller values when <code>freq</code>
233    * is small.
234    *
235    @param freq the frequency of a term within a document
236    @return a score factor based on a term's within-document frequency
237    */
238   public abstract float tf(float freq);
239 
240   /** Computes a score factor for a simple term.
241    *
242    <p>The default implementation is:<pre>
243    *   return idf(searcher.docFreq(term), searcher.maxDoc());
244    </pre>
245    *
246    * Note that {@link Searcher#maxDoc()} is used instead of
247    {@link IndexReader#numDocs()} because it is proportional to
248    {@link Searcher#docFreq(Term)} , i.e., when one is inaccurate,
249    * so is the other, and in the same direction.
250    *
251    @param term the term in question
252    @param searcher the document collection being searched
253    @return a score factor for the term
254    */
255   public float idf(Term term, Searcher searcherthrows IOException {
256     return idf(searcher.docFreq(term), searcher.maxDoc());
257   }
258 
259   /** Computes a score factor for a phrase.
260    *
261    <p>The default implementation sums the {@link #idf(Term,Searcher)} factor
262    * for each term in the phrase.
263    *
264    @param terms the terms in the phrase
265    @param searcher the document collection being searched
266    @return a score factor for the phrase
267    */
268   public float idf(Collection terms, Searcher searcherthrows IOException {
269     float idf = 0.0f;
270     Iterator i = terms.iterator();
271     while (i.hasNext()) {
272       idf += idf((Term)i.next(), searcher);
273     }
274     return idf;
275   }
276 
277   /** Computes a score factor based on a term's document frequency (the number
278    * of documents which contain the term).  This value is multiplied by the
279    {@link #tf(int)} factor for each term in the query and these products are
280    * then summed to form the initial score for a document.
281    *
282    <p>Terms that occur in fewer documents are better indicators of topic, so
283    * implemenations of this method usually return larger values for rare terms,
284    * and smaller values for common terms.
285    *
286    @param docFreq the number of documents which contain the term
287    @param numDocs the total number of documents in the collection
288    @return a score factor based on the term's document frequency
289    */
290   public abstract float idf(int docFreq, int numDocs);
291 
292   /** Computes a score factor based on the fraction of all query terms that a
293    * document contains.  This value is multiplied into scores.
294    *
295    <p>The presence of a large portion of the query terms indicates a better
296    * match with the query, so implemenations of this method usually return
297    * larger values when the ratio between these parameters is large and smaller
298    * values when the ratio between them is small.
299    *
300    @param overlap the number of query terms matched in the document
301    @param maxOverlap the total number of terms in the query
302    @return a score factor based on term overlap with the query
303    */
304   public abstract float coord(int overlap, int maxOverlap);
305 }