xapian-core  2.0.0
pl2weight.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2013 Aarsh Shah
5  * Copyright (C) 2013,2014,2016,2024 Olly Betts
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see
19  * <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 
24 #include "xapian/weight.h"
25 
26 #include "weightinternal.h"
27 
28 #include "serialise-double.h"
29 
30 #include "xapian/error.h"
31 
32 #include <algorithm>
33 #include <cmath>
34 
35 using namespace std;
36 
37 namespace Xapian {
38 
39 PL2Weight::PL2Weight(double c) : param_c(c)
40 {
41  if (param_c <= 0)
42  throw Xapian::InvalidArgumentError("Parameter c is invalid");
49  need_stat(WDF);
51  need_stat(WQF);
52 }
53 
54 PL2Weight *
56 {
57  return new PL2Weight(param_c);
58 }
59 
60 void
61 PL2Weight::init(double factor_)
62 {
63  if (factor_ == 0.0) {
64  // This object is for the term-independent contribution, and that's
65  // always zero for this scheme.
66  return;
67  }
68 
69  factor = factor_;
70 
71  if (get_wdf_upper_bound() == 0) {
72  // The "extra" weight object is cloned, init() called and then
73  // get_maxextra() is called and we discover that we don't need it.
74  // So we need to handle that case (which will give us 0 from
75  // get_wdf_upper_bound() here).
76  upper_bound = 0;
77  return;
78  }
79 
80  factor *= get_wqf();
81 
83 
84  double base_change(1.0 / log(2.0));
85  double mean = double(get_collection_freq()) / get_collection_size();
86  P1 = mean * base_change + 0.5 * log2(2.0 * M_PI);
87  P2 = log2(mean) + base_change;
88 
89  double wdfn_lower = log2(1 + cl / get_doclength_upper_bound());
90  double divisior = max(get_wdf_upper_bound(), get_doclength_lower_bound());
91  double wdfn_upper = get_wdf_upper_bound() * log2(1 + cl / divisior);
92 
93  // Calculate an upper bound on the weights which get_sumpart() can return.
94  //
95  // We consider the equation for P as the sum of two parts which we
96  // maximise individually:
97  //
98  // (a) (wdfn + 0.5) / (wdfn + 1) * log2(wdfn)
99  // (b) (P1 - P2 * wdfn) / (wdfn + 1)
100  //
101  // To maximise (a), the fractional part is always positive (since wdfn>0)
102  // and is maximised by maximising wdfn - clearer when rewritten as:
103  // (1 - 0.5 / (wdfn + 1))
104  //
105  // The log part of (a) is clearly also maximised by maximising wdfn,
106  // so we want to evaluate (a) at wdfn=wdfn_upper.
107  double P_max2a = (wdfn_upper + 0.5) * log2(wdfn_upper) / (wdfn_upper + 1.0);
108  // To maximise (b) substitute x=wdfn+1 (so x>1) and we get:
109  //
110  // (P1 + P2)/x - P2
111  //
112  // Differentiating wrt x gives:
113  //
114  // -(P1 + P2)/x²
115  //
116  // So there are no local minima or maxima, and the function is continuous
117  // in the range of interest, so the sign of this differential tells us
118  // whether we want to maximise or minimise wdfn, and since x>1, we can
119  // just consider the sign of: (P1 + P2)
120  //
121  // Commonly P1 + P2 > 0, in which case we evaluate P at wdfn=wdfn_upper
122  // giving us a bound that can't be bettered if wdfn_upper is tight.
123  double wdfn_optb = P1 + P2 > 0 ? wdfn_upper : wdfn_lower;
124  double P_max2b = (P1 - P2 * wdfn_optb) / (wdfn_optb + 1.0);
125  upper_bound = factor * (P_max2a + P_max2b);
126 
127  if (rare(upper_bound <= 0)) upper_bound = 0;
128 }
129 
130 string
132 {
133  return "pl2";
134 }
135 
136 string
138 {
139  return serialise_double(param_c);
140 }
141 
142 PL2Weight *
143 PL2Weight::unserialise(const string & s) const
144 {
145  const char *ptr = s.data();
146  const char *end = ptr + s.size();
147  double c = unserialise_double(&ptr, end);
148  if (rare(ptr != end))
149  throw Xapian::SerialisationError("Extra data in PL2Weight::unserialise()");
150  return new PL2Weight(c);
151 }
152 
153 double
156 {
157  if (wdf == 0) return 0.0;
158 
159  double wdfn = wdf * log2(1 + cl / len);
160 
161  double P = P1 + (wdfn + 0.5) * log2(wdfn) - P2 * wdfn;
162  if (rare(P <= 0)) return 0.0;
163 
164  return factor * P / (wdfn + 1.0);
165 }
166 
167 double
169 {
170  return upper_bound;
171 }
172 
173 [[noreturn]]
174 static inline void
175 parameter_error(const char* message, const char* params)
176 {
177  Xapian::Weight::Internal::parameter_error(message, "pl2", params);
178 }
179 
180 PL2Weight*
181 PL2Weight::create_from_parameters(const char* params) const
182 {
183  const char* p = params;
184  if (*p == '\0')
185  return new Xapian::PL2Weight();
186  double c = 1.0;
188  parameter_error("Parameter is invalid", params);
189  if (*p)
190  parameter_error("Extra data after parameter", params);
191  return new Xapian::PL2Weight(c);
192 }
193 
194 }
InvalidArgumentError indicates an invalid parameter value was passed to the API.
Definition: error.h:229
This class implements the PL2 weighting scheme.
Definition: weight.h:1671
PL2Weight * unserialise(const std::string &serialised) const
Unserialise parameters.
Definition: pl2weight.cc:143
double get_sumpart(Xapian::termcount wdf, Xapian::termcount doclen, Xapian::termcount uniqterms, Xapian::termcount wdfdocmax) const
Calculate the weight contribution for this object's term to a document.
Definition: pl2weight.cc:154
double upper_bound
The upper bound on the weight.
Definition: weight.h:1679
void init(double factor_)
Allow the subclass to perform any initialisation it needs to.
Definition: pl2weight.cc:61
double param_c
The wdf normalization parameter in the formula.
Definition: weight.h:1676
PL2Weight * clone() const
Clone this object.
Definition: pl2weight.cc:55
std::string serialise() const
Return this object's parameters serialised as a single string.
Definition: pl2weight.cc:137
std::string name() const
Return the name of this weighting scheme, e.g.
Definition: pl2weight.cc:131
double cl
Set by init() to (param_c * get_average_length())
Definition: weight.h:1685
double P1
Constants for a given term in a given query.
Definition: weight.h:1682
double factor
The factor to multiply weights by.
Definition: weight.h:1673
double get_maxpart() const
Return an upper bound on what get_sumpart() can return for any document.
Definition: pl2weight.cc:168
PL2Weight * create_from_parameters(const char *params) const
Create from a human-readable parameter string.
Definition: pl2weight.cc:181
Indicates an error in the std::string serialisation of an object.
Definition: error.h:917
static void parameter_error(const char *msg, const std::string &scheme, const char *params)
static bool double_param(const char **p, double *ptr_val)
Xapian::termcount get_doclength_lower_bound() const
A lower bound on the minimum length of any document in the shard.
Definition: weight.h:586
void need_stat(stat_flags flag)
Tell Xapian that your subclass will want a particular statistic.
Definition: weight.h:183
Xapian::termcount get_wqf() const
The within-query-frequency of this term.
Definition: weight.h:570
Xapian::termcount get_collection_freq() const
The collection frequency of the term.
Definition: weight.h:564
Xapian::doccount get_collection_size() const
The number of documents in the collection.
Definition: weight.h:549
Xapian::doclength get_average_length() const
The average length of a document in the collection.
Definition: weight.h:555
Xapian::termcount get_doclength_upper_bound() const
An upper bound on the maximum length of any document in the shard.
Definition: weight.h:576
@ AVERAGE_LENGTH
Average length of documents in the collection.
Definition: weight.h:47
@ DOC_LENGTH_MAX
Upper bound on document lengths.
Definition: weight.h:73
@ DOC_LENGTH
Length of the current document (sum wdf).
Definition: weight.h:59
@ WQF
Within-query-frequency of the current term.
Definition: weight.h:55
@ COLLECTION_SIZE
Number of documents in the collection.
Definition: weight.h:43
@ WDF_MAX
Upper bound on wdf.
Definition: weight.h:81
@ DOC_LENGTH_MIN
Lower bound on (non-zero) document lengths.
Definition: weight.h:65
@ COLLECTION_FREQ
Sum of wdf over the whole collection for the current term.
Definition: weight.h:83
@ WDF
Within-document-frequency of the current term in the current document.
Definition: weight.h:57
Xapian::termcount get_wdf_upper_bound() const
An upper bound on the wdf of this term in the shard.
Definition: weight.h:594
#define rare(COND)
Definition: config.h:607
PositionList * p
Hierarchy of classes which Xapian can throw as exceptions.
The Xapian namespace contains public interfaces for the Xapian library.
Definition: compactor.cc:82
unsigned XAPIAN_TERMCOUNT_BASE_TYPE termcount
A counts of terms.
Definition: types.h:64
static void parameter_error(const char *message, const char *params)
Definition: bb2weight.cc:185
string serialise_double(double v)
Serialise a double to a string.
double unserialise_double(const char **p, const char *end)
Unserialise a double serialised by serialise_double.
functions to serialise and unserialise a double
Weighting scheme API.
Xapian::Weight::Internal class, holding database and term statistics.