LIMA
Libre Multilingual Analyzer — C++ API
Loading...
Searching...
No Matches
morph_model_builder.cpp
Go to the documentation of this file.
1// Copyright 2002-2021 CEA LIST
2// SPDX-FileCopyrightText: 2022 CEA LIST <gael.de-chalendar@cea.fr>
3//
4// SPDX-License-Identifier: MIT
5
6#include <string>
7#include <map>
8#include <iostream>
9#include <algorithm>
10
11#include "morph_model_builder.h"
13
14using namespace std;
15
16namespace deeplima
17{
18namespace morph_model
19{
20
21namespace builder
22{
24 map<string, set<string>>& upos,
25 map<string, set<string>>& feats,
26 map<string, set<string>>& feat2upos)
27{
28 CoNLLU::WordLevelAdapter src(&annot);
30 while (src.end() != it)
31 {
32 while(!(*it).is_word() && src.end() != it)
33 {
34 it++;
35 }
36 if (src.end() == it)
37 {
38 break;
39 }
40
41 const string& u = (*it).upos();
42 if (upos.end() == upos.find(u))
43 {
44 upos[u] = set<string>();
45 }
46 const map<string, set<string>>& f = (*it).feats();
47
48 for ( const auto& kv : f )
49 {
50 if (!kv.second.empty())
51 {
52 upos[u].insert(kv.first);
53 feat2upos[kv.first].insert(u);
54 }
55 vector<string> values;
56 values.reserve(kv.second.size());
57 assert(1 == kv.second.size()); // reader doesn't accept multiple values now
58 for ( const string& s : kv.second )
59 {
60 values.push_back(s);
61 }
62 sort(values.begin(), values.end());
63 string s;
64 for ( const string& v : values )
65 {
66 if (!s.empty())
67 {
68 s += " ";
69 }
70 s += v;
71 }
72 feats[kv.first].insert(s);
73 }
74
75 it++;
76 }
77}
78
79size_t min_bits_for_value(size_t val)
80{
81 size_t val_plus_one = val + 1;
82 for (size_t i = 0; i <= 32; ++i)
83 {
84 size_t m = 1 << i;
85 if (val_plus_one <= m)
86 {
87 return i;
88 }
89 }
90 throw overflow_error("Value is too big");
91}
92
93void build_morph_model_inner(map<string, vector<string>>& upos2feat,
94 vector<string>& feats_by_use_count,
95 const map<string, set<string>>& upos,
96 const map<string, set<string>>& feats,
97 const vector<string>& fixed_feats)
98{
99 set<string> fixed_feats_set(fixed_feats.begin(), fixed_feats.end());
100 size_t bits_per_upos = min_bits_for_value(upos.size());
101 size_t max_bits = 0;
102 cout << "bits_per_upos == " << bits_per_upos << endl;
103
104 for ( const auto& kv : upos )
105 {
106 const string& upos_name = kv.first;
107
108 // upos
109 upos2feat[upos_name] = { "upos" };
110 upos2feat[upos_name].reserve(kv.second.size() + fixed_feats.size() + 1);
111 size_t total_bits = bits_per_upos;
112
113 // features applicable to any upos value
114 for ( const string& feat_name : fixed_feats )
115 {
116 auto it = feats.find(feat_name);
117 if (feats.end() != it)
118 {
119 upos2feat[upos_name].push_back(feat_name);
120 const set<string>& feat_values = it->second;
121 total_bits += min_bits_for_value(feat_values.size());
122 }
123 }
124
125 cout << "! " << upos_name << " " << total_bits << " bits ";
126
127 // features specific for the given upos_name
128 for ( const string& feat_name : feats_by_use_count )
129 {
130 auto it = feats.find(feat_name);
131 assert(feats.end() != it);
132 const set<string>& feat_values = it->second;
133 if (kv.second.end() != kv.second.find(feat_name)
134 && fixed_feats_set.end() == fixed_feats_set.find(feat_name))
135 {
136 upos2feat[upos_name].push_back(feat_name);
137 total_bits += min_bits_for_value(feat_values.size());
138 cout << feat_name << "/" << min_bits_for_value(feat_values.size()) << " ";
139 }
140 }
141
142 cout << endl;
143 max_bits = max(max_bits, total_bits);
144 }
145 cout << "Max bits: " << max_bits << endl;
146}
147
148} // namespace builder
149
151 const CoNLLU::Annotation& annotation2)
152{
153 map<string, set<string>> upos; // UPOS -> set of features
154 map<string, set<string>> feats; // feature -> set of values
155 map<string, set<string>> feat2upos; // feature -> upos it is used for
156
157 builder::collect_statistics(annotation1, upos, feats, feat2upos);
158 builder::collect_statistics(annotation2, upos, feats, feat2upos);
159
160 // for ( const auto& kv : feats )
161 // {
162 // cerr << kv.first << "\t:";
163 // for ( const string& s : kv.second )
164 // {
165 // cerr << " " << s;
166 // }
167 // cerr << endl;
168 // }
169 // cerr << endl;
170
171 // for ( const auto& kv : upos )
172 // {
173 // cerr << kv.first << endl;
174 // for ( const string& s : kv.second )
175 // {
176 // cerr << kv.first << "\t" << s << endl;
177 // }
178 // cerr << endl;
179 // }
180
181 // for ( const auto& kv : feat2upos )
182 // {
183 // cerr << kv.first << "\t:";
184 // for ( const string& s : kv.second )
185 // {
186 // cerr << " " << s;
187 // }
188 // cerr << endl;
189 // }
190 // cerr << endl;
191
192 vector<string> feats_by_use_count;
193 feats_by_use_count.reserve(feat2upos.size());
194 for ( const auto& kv : feat2upos )
195 {
196 feats_by_use_count.push_back(kv.first);
197 }
198
199 sort(feats_by_use_count.begin(), feats_by_use_count.end(), [&feat2upos](const string& a, const string& b){
200 return feat2upos[a].size() > feat2upos[b].size();
201 });
202
203 size_t total = 0;
204 // cerr << "UPOS\t\t" << upos.size() << " " << builder::min_bits_for_value(upos.size()) << endl;
205 total += upos.size();
206 for ( const string& s : feats_by_use_count )
207 {
208 // cerr << s << "\t" << feat2upos[s].size();
209 size_t num_values = /*1 +*/ feats[s].size();
210 // cerr << "\t" << num_values << " " << builder::min_bits_for_value(num_values);
211 total += builder::min_bits_for_value(num_values);
212
213 // cerr << endl;
214 }
215 // cout << "Total bits: " << total << endl;
216
217 map<string, vector<string>> upos2feat;
218 vector<bool> used(feats_by_use_count.size(), false);
219 vector<string> fixed_feats = { "Abbr", "Foreign", "Typo" };
220 builder::build_morph_model_inner(upos2feat, feats_by_use_count, upos, feats, fixed_feats);
221
222 morph_model_t model;
223 model.add_feat("upos");
224 for ( const auto& kv : upos )
225 {
226 const string& upos_name = kv.first;
227 /*size_t upos_id =*/ model.add_upos(upos_name);
228
229 for ( const string& feat_name : kv.second )
230 {
231 /*size_t feat_id =*/ model.add_feat(feat_name);
232 }
233 }
234
235 map<pair<feat_base_t, uint8_t>, size_t> masks;
236 vector<map<size_t, pair<feat_base_t, uint8_t>>> uposid2featid2mask;
237 uposid2featid2mask.resize(upos2feat.size());
238 for ( const auto& kv : upos2feat )
239 {
240 const string& upos_name = kv.first;
241 size_t upos_id = model.get_upos_id(upos_name);
242
243 size_t offset = 0;
244 for ( const string& feat_name : kv.second )
245 {
246 size_t width = 0;
247 size_t feat_id = model.get_feat_id(feat_name);
248
249 if (feat_name == "upos")
250 {
251 width = builder::min_bits_for_value(upos.size());
252 }
253 else
254 {
255 auto it = feats.find(feat_name);
256 assert(feats.end() != it);
257 const set<string>& feat_values = it->second;
258 width = builder::min_bits_for_value(feat_values.size());
259 }
260 assert(width > 0);
261
262 feat_base_t mask = ((feat_base_t(1) << width) - 1) << offset;
263 assert(mask != 0);
264 cout << pretty_bits_to_string(mask) << " " << upos_name << " " << upos_id << " "
265 << feat_name << " " << width << " " << offset
266 << " " << endl;
267 masks[make_pair(mask, offset)] += 1;
268 uposid2featid2mask[upos_id][feat_id] = make_pair(mask, offset);
269
270 offset += width;
271 }
272 }
273
274 size_t upos_width = builder::min_bits_for_value(upos.size());
275 model.m_upos_mask = ((feat_base_t(1) << upos_width) - 1);
276 model.m_masks.reserve(masks.size());
277 model.m_offset.reserve(masks.size());
278 for ( const auto& kv : masks )
279 {
280 feat_base_t mask = kv.first.first;
281 uint8_t offset = kv.first.second;
282 masks[kv.first] = model.m_masks.size();
283 model.m_masks.push_back(mask);
284 model.m_offset.push_back(offset);
285 }
286
287 model.m_feats2mask.resize(uposid2featid2mask.size());
288 for (size_t i = 0; i < uposid2featid2mask.size(); ++i)
289 {
290 for ( const auto& kv : uposid2featid2mask[i] )
291 {
292 model.m_feats2mask[i][kv.first] = masks[kv.second];
293 }
294 }
295
296 model.m_feats.resize(feats.size() + 1);
297 for ( const auto& kv : feats )
298 {
299 const string& feat_name = kv.first;
300 const set<string>& feat_values = kv.second;
301 size_t feat_id = model.get_feat_id(feat_name);
302
303 size_t empty_code = model.m_feats[feat_id].add_key("_");
304 assert(0 == empty_code);
305 for ( const string& value : feat_values )
306 {
307 model.m_feats[feat_id].add_key(value);
308 }
309 }
310
311 return model;
312}
313
314} // morph_model
315} // deeplima
316
virtual const_iterator begin() const
Definition treebank.h:403
virtual const_iterator end() const
Definition treebank.h:409
static morph_model_t build(const CoNLLU::Annotation &annotation1, const CoNLLU::Annotation &annotation2)
Helper class for morphology data (upos, features) binarization.
Definition morph_model.h:93
std::vector< std::map< size_t, size_t > > m_feats2mask
size_t get_feat_id(const std::string &name) const
size_t get_upos_id(const std::string &name) const
std::vector< uint8_t > m_offset
size_t add_upos(const std::string &name)
size_t add_feat(const std::string &name)
std::vector< feat_base_t > m_masks
void build_morph_model_inner(map< string, vector< string > > &upos2feat, vector< string > &feats_by_use_count, const map< string, set< string > > &upos, const map< string, set< string > > &feats, const vector< string > &fixed_feats)
void collect_statistics(const CoNLLU::Annotation &annot, map< string, set< string > > &upos, map< string, set< string > > &feats, map< string, set< string > > &feat2upos)
std::string pretty_bits_to_string(const T arg)
Definition pretty.h:28
STL namespace.
PUGI__FN void sort(I begin, I end, const Pred &pred)
Definition pugixml.cpp:7549