Small improvement of new ranking.
[idzebra-moved-to-github.git] / rset / rsrel.c
1 /*
2  * Copyright (C) 1994-1997, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: rsrel.c,v $
7  * Revision 1.19  1997-10-01 11:44:06  adam
8  * Small improvement of new ranking.
9  *
10  * Revision 1.18  1997/09/24 13:36:41  adam
11  * More work on new ranking algorithm.
12  *
13  * Revision 1.17  1997/09/22 12:39:07  adam
14  * Added get_pos method for the ranked result sets.
15  *
16  * Revision 1.16  1997/09/17 12:19:23  adam
17  * Zebra version corresponds to YAZ version 1.4.
18  * Changed Zebra server so that it doesn't depend on global common_resource.
19  *
20  * Revision 1.15  1997/09/09 13:38:16  adam
21  * Partial port to WIN95/NT.
22  *
23  * Revision 1.14  1996/11/08 11:15:58  adam
24  * Compressed isam fully supported.
25  *
26  * Revision 1.13  1996/10/29 13:55:26  adam
27  * Include of zebrautl.h instead of alexutil.h.
28  *
29  * Revision 1.12  1996/10/08 13:00:40  adam
30  * Bug fix: result sets with ranked operands in boolean operations weren't
31  * sorted.
32  *
33  * Revision 1.11  1996/10/07  16:05:29  quinn
34  * Work.
35  *
36  * Revision 1.9  1995/12/11  09:15:26  adam
37  * New set types: sand/sor/snot - ranked versions of and/or/not in
38  * ranked/semi-ranked result sets.
39  * Note: the snot not finished yet.
40  * New rset member: flag.
41  * Bug fix: r_delete in rsrel.c did free bad memory block.
42  *
43  * Revision 1.8  1995/12/05  11:25:45  adam
44  * Doesn't include math.h.
45  *
46  * Revision 1.7  1995/10/12  12:41:57  adam
47  * Private info (buf) moved from struct rset_control to struct rset.
48  * Bug fixes in relevance.
49  *
50  * Revision 1.6  1995/10/10  14:00:04  adam
51  * Function rset_open changed its wflag parameter to general flags.
52  *
53  * Revision 1.5  1995/10/06  14:38:06  adam
54  * New result set method: r_score.
55  * Local no (sysno) and score is transferred to retrieveCtrl.
56  *
57  * Revision 1.4  1995/09/14  07:48:56  adam
58  * Other score calculation.
59  *
60  * Revision 1.3  1995/09/11  15:23:40  adam
61  * More work on relevance search.
62  *
63  * Revision 1.2  1995/09/11  13:09:41  adam
64  * More work on relevance feedback.
65  *
66  * Revision 1.1  1995/09/08  14:52:42  adam
67  * Work on relevance feedback.
68  *
69  */
70
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <assert.h>
75
76 #include <isam.h>
77 #include <isamc.h>
78 #include <rsrel.h>
79 #include <zebrautl.h>
80
81 static void *r_create(const struct rset_control *sel, void *parms,
82                       int *flags);
83 static RSFD r_open (RSET ct, int flag);
84 static void r_close (RSFD rfd);
85 static void r_delete (RSET ct);
86 static void r_rewind (RSFD rfd);
87 static int r_count (RSET ct);
88 static int r_read (RSFD rfd, void *buf);
89 static int r_write (RSFD rfd, const void *buf);
90 static int r_score (RSFD rfd, int *score);
91
92 static const rset_control control = 
93 {
94     "relevance",
95     r_create,
96     r_open,
97     r_close,
98     r_delete,
99     r_rewind,
100     r_count,
101     r_read,
102     r_write,
103     r_score
104 };
105
106 const rset_control *rset_kind_relevance = &control;
107
108 struct rset_rel_info {
109     int     key_size;
110     int     max_rec;
111     int     no_rec;
112     int     (*cmp)(const void *p1, const void *p2);
113     int     (*get_pos)(const void *p);
114     char    *key_buf;                   /* key buffer */
115     float   *score_buf;                 /* score buffer */
116     int     *sort_idx;                  /* score sorted index */
117     int     *sysno_idx;                /* sysno sorted index (ring buffer) */
118     struct rset_rel_rfd *rfd_list;
119 };
120
121 struct rset_rel_rfd {
122     int     last_read_pos;
123     int     position;
124     int     flag;
125     struct rset_rel_rfd *next;
126     struct rset_rel_info *info;
127 };
128
129 static void add_rec (struct rset_rel_info *info, double score, void *key)
130 {
131     int idx, i, j;
132
133     for (i = 0; i<info->no_rec; i++)
134     {
135         idx = info->sort_idx[i];
136         if (score <= info->score_buf[idx])
137             break;
138     }
139     if (info->no_rec < info->max_rec)
140     {                                        /* there is room for this entry */
141         for (j = info->no_rec; j > i; --j)
142             info->sort_idx[j] = info->sort_idx[j-1];
143         idx = info->sort_idx[j] = info->no_rec;
144         ++(info->no_rec);
145     }
146     else if (i == 0)
147         return;                              /* score too low */
148     else
149     {
150         idx = info->sort_idx[0];             /* remove this entry */
151
152         --i;
153         for (j = 0; j < i; ++j)              /* make room */
154             info->sort_idx[j] = info->sort_idx[j+1];
155         info->sort_idx[j] = idx;             /* allocate sort entry */
156     }
157     memcpy (info->key_buf + idx*info->key_size, key, info->key_size);
158     info->score_buf[idx] = score;
159 }
160
161
162 static struct rset_rel_info *qsort_info;
163
164 static int qcomp (const void *p1, const void *p2)
165 {
166     int i1 = *(int*) p1;
167     int i2 = *(int*) p2;
168
169     return qsort_info->cmp (qsort_info->key_buf + i1*qsort_info->key_size,
170                             qsort_info->key_buf + i2*qsort_info->key_size);
171 }
172
173 #define NEW_RANKING 1
174
175 #define SCORE_SHOW 0.0                       /* base score for showing up */
176 #define SCORE_COOC 0.3                       /* component dependent on co-oc */
177 #define SCORE_DYN  (1-(SCORE_SHOW+SCORE_COOC)) /* dynamic component of score */
178
179 static void relevance (struct rset_rel_info *info, rset_relevance_parms *parms)
180 {
181     char **isam_buf;
182     char *isam_tmp_buf;
183     int  *isam_r;
184     int  *max_tf, *tf;
185
186 #if NEW_RANKING
187     int  *pos_tf = NULL;
188     int score_sum = 0;
189     int no_occur = 0;
190     char *isam_prev_buf = NULL;
191     int fact1, fact2;
192 #endif
193     ISPT *isam_pt = NULL;
194     ISAMC_PP *isamc_pp = NULL;
195     int i;
196
197     logf (LOG_DEBUG, "relevance");
198     isam_buf = xmalloc (parms->no_isam_positions * sizeof(*isam_buf));
199     isam_r = xmalloc (sizeof (*isam_r) * parms->no_isam_positions);
200     if (parms->is)
201         isam_pt = xmalloc (sizeof (*isam_pt) * parms->no_isam_positions);
202     else if (parms->isc)
203         isamc_pp = xmalloc (sizeof (*isamc_pp) * parms->no_isam_positions);
204     else
205     {
206         logf (LOG_FATAL, "No isamc or isam in rs_rel");
207         abort ();
208     }
209     isam_tmp_buf = xmalloc (info->key_size);
210     max_tf = xmalloc (sizeof (*max_tf) * parms->no_terms);
211     tf = xmalloc (sizeof (*tf) * parms->no_terms);
212
213     for (i = 0; i<parms->no_terms; i++)
214         max_tf[i] = 0;
215     for (i = 0; i<parms->no_isam_positions; i++)
216     {
217         isam_buf[i] = xmalloc (info->key_size);
218         if (isam_pt)
219         {
220             isam_pt[i] = is_position (parms->is, parms->isam_positions[i]);
221             max_tf [parms->term_no[i]] = is_numkeys (isam_pt[i]);
222             isam_r[i] = is_readkey (isam_pt[i], isam_buf[i]);
223         } 
224         else if (isamc_pp)
225         {
226             isamc_pp[i] = isc_pp_open (parms->isc, parms->isam_positions[i]);
227             max_tf [parms->term_no[i]] = isc_pp_num (isamc_pp[i]);
228             isam_r[i] = isc_pp_read (isamc_pp[i], isam_buf[i]);
229         }
230         logf (LOG_DEBUG, "max tf %d = %d", i, max_tf[i]);
231     }
232 #if NEW_RANKING
233     while (1)
234     {
235         int r, min = -1;
236         int pos = 0;
237         for (i = 0; i<parms->no_isam_positions; i++)
238             if (isam_r[i] && 
239                 (min < 0 ||
240                  (r = (*parms->cmp)(isam_buf[i], isam_buf[min])) < 1))
241                 min = i;
242         if (!isam_prev_buf)
243         {
244             pos_tf = xmalloc (sizeof(*pos_tf) * parms->no_isam_positions);
245             isam_prev_buf = xmalloc (info->key_size);
246             fact1 = 100000/parms->no_isam_positions;
247             fact2 = 100000/(parms->no_isam_positions*parms->no_isam_positions);
248             
249             no_occur = score_sum = 0;
250             memcpy (isam_prev_buf, isam_buf[min], info->key_size);
251             for (i = 0; i<parms->no_isam_positions; i++)
252                 pos_tf[i] = -10;
253         }
254         else if (min < 0 ||
255                  (*parms->cmp)(isam_buf[min], isam_prev_buf) > 1)
256         {
257             logf (LOG_LOG, "final occur = %d ratio=%d",
258                   no_occur, score_sum / no_occur);
259             add_rec (info, score_sum / (10000.0*no_occur), isam_prev_buf);
260             if (min < 0)
261                 break;
262             no_occur = score_sum = 0;
263             memcpy (isam_prev_buf, isam_buf[min], info->key_size);
264             for (i = 0; i<parms->no_isam_positions; i++)
265                 pos_tf[i] = -10;
266         }
267         pos = (*parms->get_pos)(isam_buf[min]);
268         logf (LOG_LOG, "pos=%d", pos);
269         for (i = 0; i<parms->no_isam_positions; i++)
270         {
271             int d = pos - pos_tf[i];
272
273             no_occur++;
274             if (pos_tf[i] < 0 && i != min)
275                 continue;
276             if (d < 10)
277                 d = 10;
278             if (i == min)
279                 score_sum += fact2 / d;
280             else
281                 score_sum += fact1 / d;
282         }
283         pos_tf[min] = pos;
284         logf (LOG_LOG, "score_sum = %d", score_sum);
285         i = min;
286         if (isam_pt)
287             isam_r[i] = is_readkey (isam_pt[i], isam_buf[i]);
288         else if (isamc_pp)
289             isam_r[i] = isc_pp_read (isamc_pp[i], isam_buf[i]);
290     }
291     xfree (isam_prev_buf);
292     xfree (pos_tf);
293 #else
294     while (1)
295     {
296         int min = -1, i, r;
297         double score;
298         int co_oc, last_term;    /* Number of co-occurrences */
299
300         last_term = -1;
301         /* find min with lowest sysno */
302         for (i = 0; i<parms->no_isam_positions; i++)
303         {
304             if (isam_r[i] && 
305                (min < 0 || (r = (*parms->cmp)(isam_buf[i], isam_buf[min])) < 2))
306             {
307                 min = i;
308                 co_oc = 1;
309             }
310             else if (!r && last_term != parms->term_no[i]) /* new occurrence */
311                 co_oc++;
312             last_term = parms->term_no[i];
313         }
314         
315         if (min < 0)
316             break;
317         memcpy (isam_tmp_buf, isam_buf[min], info->key_size);
318         /* calculate for all with those sysno */
319         for (i = 0; i < parms->no_terms; i++)
320             tf[i] = 0;
321         for (i = 0; i<parms->no_isam_positions; i++)
322         {
323             int r;
324             
325             if (isam_r[i])
326                 r = (*parms->cmp)(isam_buf[i], isam_tmp_buf);
327             else 
328                 r = 2;
329             if (r <= 1 && r >= -1)
330             {
331                 do
332                 {
333                     tf[parms->term_no[i]]++;
334                     if (isam_pt)
335                         isam_r[i] = is_readkey (isam_pt[i], isam_buf[i]);
336                     else if (isamc_pp)
337                         isam_r[i] = isc_pp_read (isamc_pp[i], isam_buf[i]);
338                 } while (isam_r[i] && 
339                          (*parms->cmp)(isam_buf[i], isam_tmp_buf) <= 1);
340             }
341         }
342         /* calculate relevance value */
343         score = 0.0;
344         for (i = 0; i<parms->no_terms; i++)
345             if (tf[i])
346                 score += SCORE_SHOW + SCORE_COOC*co_oc/parms->no_terms +
347                     SCORE_DYN*tf[i]/max_tf[i];
348         /* if value is in the top score, then save it - don't emit yet */
349         add_rec (info, score/parms->no_terms, isam_tmp_buf);
350     }
351 #endif
352     for (i = 0; i<info->no_rec; i++)
353         info->sysno_idx[i] = i;
354     qsort_info = info;
355     qsort (info->sysno_idx, info->no_rec, sizeof(*info->sysno_idx), qcomp);
356     for (i = 0; i<parms->no_isam_positions; i++)
357     {
358         if (isam_pt)
359             is_pt_free (isam_pt[i]);
360         if (isamc_pp)
361             isc_pp_close (isamc_pp[i]);
362         xfree (isam_buf[i]);
363     }
364     xfree (max_tf);
365     xfree (isam_tmp_buf);
366     xfree (isam_buf);
367     xfree (isam_r);
368     xfree (isam_pt);
369     xfree (isamc_pp);
370     xfree(tf);
371 }
372
373 static void *r_create (const struct rset_control *sel, void *parms,
374                        int *flags)
375 {
376     rset_relevance_parms *r_parms = parms;
377     struct rset_rel_info *info;
378
379     *flags |= RSET_FLAG_RANKED;
380     info = xmalloc (sizeof(struct rset_rel_info));
381     info->key_size = r_parms->key_size;
382     assert (info->key_size > 1);
383     info->max_rec = r_parms->max_rec;
384     assert (info->max_rec > 1);
385     info->cmp = r_parms->cmp;
386     info->get_pos = r_parms->get_pos;
387
388     info->key_buf = xmalloc (info->key_size * info->max_rec);
389     info->score_buf = xmalloc (sizeof(*info->score_buf) * info->max_rec);
390     info->sort_idx = xmalloc (sizeof(*info->sort_idx) * info->max_rec);
391     info->sysno_idx = xmalloc (sizeof(*info->sysno_idx) * info->max_rec);
392     info->no_rec = 0;
393     info->rfd_list = NULL;
394
395     relevance (info, r_parms);
396     return info;
397 }
398
399 static RSFD r_open (RSET ct, int flag)
400 {
401     struct rset_rel_rfd *rfd;
402     struct rset_rel_info *info = ct->buf;
403
404     if (flag & RSETF_WRITE)
405     {
406         logf (LOG_FATAL, "relevance set type is read-only");
407         return NULL;
408     }
409     rfd = xmalloc (sizeof(*rfd));
410     rfd->flag = flag;
411     rfd->next = info->rfd_list;
412     rfd->info = info;
413     info->rfd_list = rfd;
414     r_rewind (rfd);
415     return rfd;
416 }
417
418 static void r_close (RSFD rfd)
419 {
420     struct rset_rel_info *info = ((struct rset_rel_rfd*)rfd)->info;
421     struct rset_rel_rfd **rfdp;
422     
423     for (rfdp = &info->rfd_list; *rfdp; rfdp = &(*rfdp)->next)
424         if (*rfdp == rfd)
425         {
426             *rfdp = (*rfdp)->next;
427             free (rfd);
428             return;
429         }
430     logf (LOG_FATAL, "r_close but no rfd match!");
431     assert (0);
432 }
433
434 static void r_delete (RSET ct)
435 {
436     struct rset_rel_info *info = ct->buf;
437
438     assert (info->rfd_list == NULL);
439     xfree (info->key_buf);
440     xfree (info->score_buf);
441     xfree (info->sort_idx);
442     xfree (info->sysno_idx);
443     xfree (info);
444 }
445
446 static void r_rewind (RSFD rfd)
447 {
448     struct rset_rel_rfd *p = rfd;
449     struct rset_rel_info *info = p->info;
450
451     if (p->flag & RSETF_SORT_RANK)
452         p->position = info->no_rec;
453     else
454         p->position = 0;
455 }
456
457 static int r_count (RSET ct)
458 {
459     struct rset_rel_info *info = ct->buf;
460
461     return info->no_rec;
462 }
463
464 static int r_read (RSFD rfd, void *buf)
465 {
466     struct rset_rel_rfd *p = rfd;
467     struct rset_rel_info *info = p->info;
468
469     if (p->flag & RSETF_SORT_RANK)
470     {
471         if (p->position <= 0)
472             return 0;
473         --(p->position);
474         p->last_read_pos = info->sort_idx[p->position];
475     }
476     else
477     {
478         if (p->position == info->no_rec)
479             return 0;
480         p->last_read_pos = info->sysno_idx[p->position];
481         ++(p->position);
482     }
483     memcpy ((char*) buf,
484             info->key_buf + info->key_size * p->last_read_pos,
485             info->key_size);
486     return 1;
487 }
488
489 static int r_score (RSFD rfd, int *score)
490 {
491     struct rset_rel_rfd *p = rfd;
492     struct rset_rel_info *info = p->info;
493
494     *score = (int) (1000*info->score_buf[p->last_read_pos]);
495     return 1;
496 }
497
498 static int r_write (RSFD rfd, const void *buf)
499 {
500     logf (LOG_FATAL, "relevance set type is read-only");
501     return -1;
502 }