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