1 /*
2  * 文節に対する候補をソートする。
3  * 将来的には近接する文節も見て、単語の結合による評価をする。
4  * ダブった候補の削除もする。
5  *
6  * Funded by IPA未踏ソフトウェア創造事業 2001 9/22
7  * Copyright (C) 2000-2006 TABATA Yusuke
8  * Copyright (C) 2001 UGAWA Tomoharu
9  *
10  */
11 /*
12   This library is free software; you can redistribute it and/or
13   modify it under the terms of the GNU Lesser General Public
14   License as published by the Free Software Foundation; either
15   version 2 of the License, or (at your option) any later version.
16 
17   This library is distributed in the hope that it will be useful,
18   but WITHOUT ANY WARRANTY; without even the implied warranty of
19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20   Lesser General Public License for more details.
21 
22   You should have received a copy of the GNU Lesser General Public
23   License along with this library; if not, write to the Free Software
24   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
25  */
26 #include <limits.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 
30 #include <anthy/segment.h>
31 #include <anthy/splitter.h>
32 #include <anthy/ordering.h>
33 #include "sorter.h"
34 
35 /* お茶入れ学習による候補 */
36 #define OCHAIRE_BASE OCHAIRE_SCORE
37 /* metawordが十分無理矢理くさいときの、ひらがなカタカナのスコア */
38 #define NOCONV_WITH_BIAS 900000
39 /* 普通の候補 */
40 #define NORMAL_BASE 100
41 /* 単漢字 */
42 #define SINGLEWORD_BASE 10
43 /* 複合語 */
44 #define COMPOUND_BASE (OCHAIRE_SCORE / 2)
45 /* 複合語の一部分を一文節にしたもの */
46 #define COMPOUND_PART_BASE 2
47 /* 付属語のみ */
48 #define DEPWORD_BASE (OCHAIRE_SCORE / 2)
49 /* ひらがなカタカナのデフォルトのスコア */
50 #define NOCONV_BASE 1
51 
52 /* 無理っぽい候補割り当てか判断する */
53 static int
uncertain_segment_p(struct seg_ent * se)54 uncertain_segment_p(struct seg_ent *se)
55 {
56   struct meta_word *mw;
57   if (se->nr_metaword == 0) {
58     return 0;
59   }
60 
61   mw = se->mw_array[0];
62 
63   /* 長さの6割 */
64   if (se->len * 3 >= mw->len * 5) {
65     return 1;
66   }
67   return 0;
68 }
69 
70 static void
release_redundant_candidate(struct seg_ent * se)71 release_redundant_candidate(struct seg_ent *se)
72 {
73   int i, j;
74   /* 配列はソートされているのでscoreが0の候補が後ろに並んでいる */
75   for (i = 0; i < se->nr_cands && se->cands[i]->score; i++);
76   /* iから後ろの候補を解放 */
77   if (i < se->nr_cands) {
78     for (j = i; j < se->nr_cands; j++) {
79       anthy_release_cand_ent(se->cands[j]);
80     }
81     se->nr_cands = i;
82   }
83 }
84 
85 /* qsort用の候補比較関数 */
86 static int
candidate_compare_func(const void * p1,const void * p2)87 candidate_compare_func(const void *p1, const void *p2)
88 {
89   const struct cand_ent *const *c1 = p1, *const *c2 = p2;
90   return (*c2)->score - (*c1)->score;
91 }
92 
93 static void
sort_segment(struct seg_ent * se)94 sort_segment(struct seg_ent *se)
95 {
96   qsort(se->cands, se->nr_cands,
97 	sizeof(struct cand_ent *),
98 	candidate_compare_func);
99 }
100 
101 static void
trim_kana_candidate(struct seg_ent * se)102 trim_kana_candidate(struct seg_ent *se)
103 {
104   int i;
105   if (NULL == se->cands) {  /* 辞書もしくは学習データが壊れていた時の対策 */
106     return;
107   }
108   if (se->cands[0]->flag & CEF_KATAKANA) {
109     return ;
110   }
111   for (i = 1; i < se->nr_cands; i++) {
112     if (se->cands[i]->flag & CEF_KATAKANA) {
113       /* 最低点まで下げる */
114       se->cands[i]->score = NOCONV_BASE;
115     }
116   }
117 }
118 
119 static void
check_dupl_candidate(struct seg_ent * se)120 check_dupl_candidate(struct seg_ent *se)
121 {
122   int i,j;
123   for (i = 0; i < se->nr_cands - 1; i++) {
124     for (j = i + 1; j < se->nr_cands; j++) {
125       if (!anthy_xstrcmp(&se->cands[i]->str, &se->cands[j]->str)) {
126 	/* ルールに良くマッチしたものの方を選ぶとかすべき */
127 	se->cands[j]->score = 0;
128 	se->cands[i]->flag |= se->cands[j]->flag;
129       }
130     }
131   }
132 }
133 
134 /* 品詞割り当てによって生成された候補を評価する */
135 static void
eval_candidate_by_metaword(struct cand_ent * ce)136 eval_candidate_by_metaword(struct cand_ent *ce)
137 {
138   int i;
139   int score = 1;
140 
141   /* まず、単語の頻度によるscoreを加算 */
142   for (i = 0; i < ce->nr_words; i++) {
143     struct cand_elm *elm = &ce->elm[i];
144     int pos, div = 1;
145     int freq;
146 
147     if (elm->nth < 0) {
148       /* 候補割り当ての対象外なのでスキップ */
149       continue;
150     }
151     pos = anthy_wtype_get_pos(elm->wt);
152     if (pos == POS_PRE || pos == POS_SUC) {
153       div = 4;
154     }
155 
156     freq = anthy_get_nth_dic_ent_freq(elm->se, elm->nth);
157     score += freq / div;
158   }
159 
160   if (ce->mw) {
161     score *= ce->mw->struct_score;
162     score /= RATIO_BASE;
163   }
164   ce->score = score;
165 }
166 
167 /* 候補を評価する */
168 static void
eval_candidate(struct cand_ent * ce,int uncertain)169 eval_candidate(struct cand_ent *ce, int uncertain)
170 {
171   if ((ce->flag &
172        (CEF_OCHAIRE | CEF_SINGLEWORD | CEF_HIRAGANA |
173 	CEF_KATAKANA | CEF_GUESS | CEF_COMPOUND | CEF_COMPOUND_PART |
174 	CEF_BEST)) == 0) {
175     /* splitterからの情報(metaword)によって生成された候補 */
176     eval_candidate_by_metaword(ce);
177   } else if (ce->flag & CEF_OCHAIRE) {
178     ce->score = OCHAIRE_BASE;
179   } else if (ce->flag & CEF_SINGLEWORD) {
180     ce->score = SINGLEWORD_BASE;
181   } else if (ce->flag & CEF_COMPOUND) {
182     ce->score = COMPOUND_BASE;
183   } else if (ce->flag & CEF_COMPOUND_PART) {
184     ce->score = COMPOUND_PART_BASE;
185   } else if (ce->flag & CEF_BEST) {
186     ce->score = OCHAIRE_BASE;
187   } else if (ce->flag & (CEF_HIRAGANA | CEF_KATAKANA |
188 			 CEF_GUESS)) {
189     if (uncertain) {
190       /*
191        * この文節は外来語などのようなので、生成した候補よりも
192        * ひらがなカタカナの候補を出した方がよい
193        */
194       ce->score = NOCONV_WITH_BIAS;
195       if (CEF_KATAKANA & ce->flag) {
196 	ce->score ++;
197       }
198       if (CEF_GUESS & ce->flag) {
199 	ce->score += 2;
200       }
201     } else {
202       ce->score = NOCONV_BASE;
203     }
204   }
205   ce->score += 1;
206 }
207 
208 static void
eval_segment(struct seg_ent * se)209 eval_segment(struct seg_ent *se)
210 {
211   int i;
212   int uncertain = uncertain_segment_p(se);
213   for (i = 0; i < se->nr_cands; i++) {
214     eval_candidate(se->cands[i], uncertain);
215   }
216 }
217 
218 /* 学習履歴の内容で順位を調整する */
219 static void
apply_learning(struct segment_list * sl,int nth)220 apply_learning(struct segment_list *sl, int nth)
221 {
222   int i;
223 
224   /*
225    * 優先順位の低いものから順に適用する
226    */
227 
228   /* 用例辞書による順序の変更 */
229   anthy_reorder_candidates_by_relation(sl, nth);
230   /* 候補の交換 */
231   for (i = nth; i < sl->nr_segments; i++) {
232     struct seg_ent *seg = anthy_get_nth_segment(sl, i);
233     /* 候補の交換 */
234     anthy_proc_swap_candidate(seg);
235     /* 履歴による順序の変更 */
236     anthy_reorder_candidates_by_history(anthy_get_nth_segment(sl, i));
237   }
238 }
239 
240 /** 外から呼ばれるエントリポイント
241  * @nth以降の文節を対象とする
242  */
243 void
anthy_sort_candidate(struct segment_list * sl,int nth)244 anthy_sort_candidate(struct segment_list *sl, int nth)
245 {
246   int i;
247   for (i = nth; i < sl->nr_segments; i++) {
248     struct seg_ent *seg = anthy_get_nth_segment(sl, i);
249     /* まず評価する */
250     eval_segment(seg);
251     /* つぎにソートする */
252     sort_segment(seg);
253     /* ダブったエントリの点の低い方に0点を付ける */
254     check_dupl_candidate(seg);
255     /* もういちどソートする */
256     sort_segment(seg);
257     /* 評価0の候補を解放 */
258     release_redundant_candidate(seg);
259   }
260 
261   /* 学習の履歴を適用する */
262   apply_learning(sl, nth);
263 
264   /* またソートする */
265   for ( i = nth ; i < sl->nr_segments ; i++){
266     sort_segment(anthy_get_nth_segment(sl, i));
267   }
268   /* カタカナの候補が先頭でなければ最後に回す */
269   for (i = nth; i < sl->nr_segments; i++) {
270     trim_kana_candidate(anthy_get_nth_segment(sl, i));
271   }
272   /* またソートする */
273   for ( i = nth ; i < sl->nr_segments ; i++){
274     sort_segment(anthy_get_nth_segment(sl, i));
275   }
276 }
277