3232public class BM25Similarity extends Similarity {
3333 private final float k1 ;
3434 private final float b ;
35+ private final float k3 ;
3536
3637 /**
3738 * BM25 with the supplied parameter values.
@@ -40,10 +41,14 @@ public class BM25Similarity extends Similarity {
4041 * @param b Controls to what degree document length normalizes tf values.
4142 * @param discountOverlaps True if overlap tokens (tokens with a position of increment of zero)
4243 * are discounted from the document's length.
43- * @throws IllegalArgumentException if {@code k1} is infinite or negative, or if {@code b} is not
44- * within the range {@code [0..1]}
44+ * @param k3 Controls query-side term frequency saturation. When duplicate terms appear in a
45+ * query, their boost is computed as ((k3 + 1) * qtf) / (k3 + qtf) instead of linear summing.
46+ * A negative value disables saturation (linear behavior). Common values are 7 or 8.
47+ * @throws IllegalArgumentException if {@code k1} is infinite or negative
48+ * @throws IllegalArgumentException if {@code b} is not within the range {@code [0..1]}
49+ * @throws IllegalArgumentException if {@code k3} is NaN or infinite
4550 */
46- public BM25Similarity (float k1 , float b , boolean discountOverlaps ) {
51+ public BM25Similarity (float k1 , float b , boolean discountOverlaps , float k3 ) {
4752 super (discountOverlaps );
4853 if (Float .isFinite (k1 ) == false || k1 < 0 ) {
4954 throw new IllegalArgumentException (
@@ -52,8 +57,27 @@ public BM25Similarity(float k1, float b, boolean discountOverlaps) {
5257 if (Float .isNaN (b ) || b < 0 || b > 1 ) {
5358 throw new IllegalArgumentException ("illegal b value: " + b + ", must be between 0 and 1" );
5459 }
60+ if (Float .isNaN (k3 ) || Float .isInfinite (k3 )) {
61+ throw new IllegalArgumentException (
62+ "illegal k3 value: " + k3 + ", must be a finite value (negative to disable)" );
63+ }
5564 this .k1 = k1 ;
5665 this .b = b ;
66+ this .k3 = k3 ;
67+ }
68+
69+ /**
70+ * BM25 with the supplied parameter values.
71+ *
72+ * @param k1 Controls non-linear term frequency normalization (saturation).
73+ * @param b Controls to what degree document length normalizes tf values.
74+ * @param discountOverlaps True if overlap tokens (tokens with a position of increment of zero)
75+ * are discounted from the document's length.
76+ * @throws IllegalArgumentException if {@code k1} is infinite or negative, or if {@code b} is not
77+ * within the range {@code [0..1]}
78+ */
79+ public BM25Similarity (float k1 , float b , boolean discountOverlaps ) {
80+ this (k1 , b , discountOverlaps , -1f );
5781 }
5882
5983 /**
@@ -65,7 +89,7 @@ public BM25Similarity(float k1, float b, boolean discountOverlaps) {
6589 * within the range {@code [0..1]}
6690 */
6791 public BM25Similarity (float k1 , float b ) {
68- this (k1 , b , true );
92+ this (k1 , b , true , - 1f );
6993 }
7094
7195 /**
@@ -82,7 +106,7 @@ public BM25Similarity(float k1, float b) {
82106 * are discounted from the document's length.
83107 */
84108 public BM25Similarity (boolean discountOverlaps ) {
85- this (1.2f , 0.75f , discountOverlaps );
109+ this (1.2f , 0.75f , discountOverlaps , - 1f );
86110 }
87111
88112 /**
@@ -95,7 +119,20 @@ public BM25Similarity(boolean discountOverlaps) {
95119 * </ul>
96120 */
97121 public BM25Similarity () {
98- this (1.2f , 0.75f , true );
122+ this (1.2f , 0.75f , true , -1f );
123+ }
124+
125+ /**
126+ * Computes the query-term weight using BM25's k3 saturation formula: ((k3 + 1) * qtf) / (k3 +
127+ * qtf). When k3 is negative (disabled), falls back to linear weighting where the weight equals
128+ * the query term frequency.
129+ */
130+ @ Override
131+ public float computeQueryTermWeight (int queryTermFrequency ) {
132+ if (k3 < 0 ) {
133+ return (float ) queryTermFrequency ;
134+ }
135+ return ((k3 + 1f ) * queryTermFrequency ) / (k3 + queryTermFrequency );
99136 }
100137
101138 /** Implemented as <code>log(1 + (docCount - docFreq + 0.5)/(docFreq + 0.5))</code>. */
@@ -307,7 +344,7 @@ private List<Explanation> explainConstantFactors() {
307344
308345 @ Override
309346 public String toString () {
310- return "BM25(k1=" + k1 + ",b=" + b + ")" ;
347+ return "BM25(k1=" + k1 + ",b=" + b + ( k3 >= 0 ? ",k3=" + k3 : "" ) + ")" ;
311348 }
312349
313350 /**
@@ -327,4 +364,12 @@ public final float getK1() {
327364 public final float getB () {
328365 return b ;
329366 }
367+
368+ /**
369+ * Returns the <code>k3</code> parameter for query-side term frequency saturation. A negative
370+ * value means saturation is disabled.
371+ */
372+ public final float getK3 () {
373+ return k3 ;
374+ }
330375}
0 commit comments