1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "prediction/predictor.h"
31
32 #include <algorithm>
33 #include <string>
34 #include <vector>
35
36 #include "base/flags.h"
37 #include "base/logging.h"
38 #include "converter/segments.h"
39 #include "protocol/commands.pb.h"
40 #include "protocol/config.pb.h"
41
42 DECLARE_bool(enable_expansion_for_dictionary_predictor);
43 DECLARE_bool(enable_expansion_for_user_history_predictor);
44
45 namespace mozc {
46 namespace {
47
48 const int kPredictionSize = 100;
49 // On Mobile mode PREDICTION (including PARTIAL_PREDICTION) behaves like as
50 // conversion so the limit is same as conversion's one.
51 const int kMobilePredictionSize = 200;
52
GetCandidatesSize(const Segments & segments)53 size_t GetCandidatesSize(const Segments &segments) {
54 if (segments.conversion_segments_size() <= 0) {
55 LOG(ERROR) << "No conversion segments found";
56 return 0;
57 }
58 return segments.conversion_segment(0).candidates_size();
59 }
60
61 // TODO(taku): Is it OK to check only |zero_query_suggestion| and
62 // |mixed_conversion|?
IsZeroQuery(const ConversionRequest & request)63 bool IsZeroQuery(const ConversionRequest &request) {
64 return request.request().zero_query_suggestion();
65 }
66
67 } // namespace
68
BasePredictor(PredictorInterface * dictionary_predictor,PredictorInterface * user_history_predictor)69 BasePredictor::BasePredictor(PredictorInterface *dictionary_predictor,
70 PredictorInterface *user_history_predictor)
71 : dictionary_predictor_(dictionary_predictor),
72 user_history_predictor_(user_history_predictor) {
73 DCHECK(dictionary_predictor_.get());
74 DCHECK(user_history_predictor_.get());
75 // TODO(noriyukit): Now the following features are stable, so remove these
76 // flags.
77 FLAGS_enable_expansion_for_dictionary_predictor = true;
78 FLAGS_enable_expansion_for_user_history_predictor = true;
79 }
80
~BasePredictor()81 BasePredictor::~BasePredictor() {}
82
Finish(const ConversionRequest & request,Segments * segments)83 void BasePredictor::Finish(const ConversionRequest &request,
84 Segments *segments) {
85 user_history_predictor_->Finish(request, segments);
86 dictionary_predictor_->Finish(request, segments);
87
88 if (segments->conversion_segments_size() < 1 ||
89 segments->request_type() == Segments::CONVERSION) {
90 return;
91 }
92 Segment *segment = segments->mutable_conversion_segment(0);
93 if (segment->candidates_size() < 1) {
94 return;
95 }
96 // update the key as the original key only contains
97 // the 'prefix'.
98 // note that candidate key may be different from request key (=segment key)
99 // due to suggestion/prediction.
100 segment->set_key(segment->candidate(0).key);
101 }
102
103 // Since DictionaryPredictor is immutable, no need
104 // to call DictionaryPredictor::Revert/Clear*/Finish methods.
Revert(Segments * segments)105 void BasePredictor::Revert(Segments *segments) {
106 user_history_predictor_->Revert(segments);
107 }
108
ClearAllHistory()109 bool BasePredictor::ClearAllHistory() {
110 return user_history_predictor_->ClearAllHistory();
111 }
112
ClearUnusedHistory()113 bool BasePredictor::ClearUnusedHistory() {
114 return user_history_predictor_->ClearUnusedHistory();
115 }
116
ClearHistoryEntry(const string & key,const string & value)117 bool BasePredictor::ClearHistoryEntry(const string &key, const string &value) {
118 return user_history_predictor_->ClearHistoryEntry(key, value);
119 }
120
Wait()121 bool BasePredictor::Wait() {
122 return user_history_predictor_->Wait();
123 }
124
Sync()125 bool BasePredictor::Sync() {
126 return user_history_predictor_->Sync();
127 }
128
Reload()129 bool BasePredictor::Reload() {
130 return user_history_predictor_->Reload();
131 }
132
133 // static
CreateDefaultPredictor(PredictorInterface * dictionary_predictor,PredictorInterface * user_history_predictor)134 PredictorInterface *DefaultPredictor::CreateDefaultPredictor(
135 PredictorInterface *dictionary_predictor,
136 PredictorInterface *user_history_predictor) {
137 return new DefaultPredictor(dictionary_predictor, user_history_predictor);
138 }
139
DefaultPredictor(PredictorInterface * dictionary_predictor,PredictorInterface * user_history_predictor)140 DefaultPredictor::DefaultPredictor(PredictorInterface *dictionary_predictor,
141 PredictorInterface *user_history_predictor)
142 : BasePredictor(dictionary_predictor, user_history_predictor),
143 empty_request_(),
144 predictor_name_("DefaultPredictor") {}
145
~DefaultPredictor()146 DefaultPredictor::~DefaultPredictor() {}
147
PredictForRequest(const ConversionRequest & request,Segments * segments) const148 bool DefaultPredictor::PredictForRequest(const ConversionRequest &request,
149 Segments *segments) const {
150 DCHECK(segments->request_type() == Segments::PREDICTION ||
151 segments->request_type() == Segments::SUGGESTION ||
152 segments->request_type() == Segments::PARTIAL_PREDICTION ||
153 segments->request_type() == Segments::PARTIAL_SUGGESTION);
154
155 if (request.config().presentation_mode()) {
156 return false;
157 }
158
159 int size = kPredictionSize;
160 if (segments->request_type() == Segments::SUGGESTION) {
161 size = std::min(
162 9, std::max(1, static_cast<int>(request.config().suggestions_size())));
163 }
164
165 bool result = false;
166 int remained_size = size;
167 segments->set_max_prediction_candidates_size(static_cast<size_t>(size));
168 result |= user_history_predictor_->PredictForRequest(request, segments);
169 remained_size = size - static_cast<size_t>(GetCandidatesSize(*segments));
170
171 // Do not call dictionary_predictor if the size of candidates get
172 // >= suggestions_size.
173 if (remained_size <= 0) {
174 return result;
175 }
176
177 segments->set_max_prediction_candidates_size(remained_size);
178 result |= dictionary_predictor_->PredictForRequest(request, segments);
179 remained_size = size - static_cast<size_t>(GetCandidatesSize(*segments));
180
181 // Do not call extra_predictor if the size of candidates get
182 // >= suggestions_size.
183 if (remained_size <= 0) {
184 return result;
185 }
186
187 return result;
188 }
189
190 // static
CreateMobilePredictor(PredictorInterface * dictionary_predictor,PredictorInterface * user_history_predictor)191 PredictorInterface *MobilePredictor::CreateMobilePredictor(
192 PredictorInterface *dictionary_predictor,
193 PredictorInterface *user_history_predictor) {
194 return new MobilePredictor(dictionary_predictor, user_history_predictor);
195 }
196
MobilePredictor(PredictorInterface * dictionary_predictor,PredictorInterface * user_history_predictor)197 MobilePredictor::MobilePredictor(PredictorInterface *dictionary_predictor,
198 PredictorInterface *user_history_predictor)
199 : BasePredictor(dictionary_predictor, user_history_predictor),
200 empty_request_(),
201 predictor_name_("MobilePredictor") {}
202
~MobilePredictor()203 MobilePredictor::~MobilePredictor() {}
204
PredictForRequest(const ConversionRequest & request,Segments * segments) const205 bool MobilePredictor::PredictForRequest(const ConversionRequest &request,
206 Segments *segments) const {
207 DCHECK(segments->request_type() == Segments::PREDICTION ||
208 segments->request_type() == Segments::SUGGESTION ||
209 segments->request_type() == Segments::PARTIAL_PREDICTION ||
210 segments->request_type() == Segments::PARTIAL_SUGGESTION);
211
212 if (request.config().presentation_mode()) {
213 return false;
214 }
215
216 DCHECK(segments);
217
218 bool result = false;
219 size_t size = 0;
220 size_t history_suggestion_size = IsZeroQuery(request) ? 3 : 2;
221
222 // TODO(taku,toshiyuki): Must rewrite the logic.
223 switch (segments->request_type()) {
224 case Segments::SUGGESTION: {
225 // Suggestion is triggered at every character insertion.
226 // So here we should use slow predictors.
227 size = GetCandidatesSize(*segments) + history_suggestion_size;
228 segments->set_max_prediction_candidates_size(size);
229 result |= user_history_predictor_->PredictForRequest(request, segments);
230
231 size = GetCandidatesSize(*segments) + 20;
232 segments->set_max_prediction_candidates_size(size);
233 result |= dictionary_predictor_->PredictForRequest(request, segments);
234
235 break;
236 }
237 case Segments::PARTIAL_SUGGESTION: {
238 // PARTIAL SUGGESTION can be triggered in a similar manner to that of
239 // SUGGESTION. We don't call slow predictors by the same reason.
240 size = GetCandidatesSize(*segments) + 20;
241 segments->set_max_prediction_candidates_size(size);
242 result |= dictionary_predictor_->PredictForRequest(request, segments);
243
244 break;
245 }
246 case Segments::PARTIAL_PREDICTION: {
247 segments->set_max_prediction_candidates_size(kMobilePredictionSize);
248 result |= dictionary_predictor_->PredictForRequest(request, segments);
249 break;
250 }
251 case Segments::PREDICTION: {
252 size = GetCandidatesSize(*segments) + history_suggestion_size;
253 segments->set_max_prediction_candidates_size(size);
254 result |= user_history_predictor_->PredictForRequest(request, segments);
255
256 segments->set_max_prediction_candidates_size(kMobilePredictionSize);
257 result |= dictionary_predictor_->PredictForRequest(request, segments);
258 break;
259 }
260 default: {
261 } // Never reach here
262 }
263
264 return result;
265 }
266
267 } // namespace mozc
268