LIMA
Libre Multilingual Analyzer — C++ API
Loading...
Searching...
No Matches
KnowledgeBasedSemanticRoleLabeler.cpp
Go to the documentation of this file.
1// Copyright 2016 CEA LIST
2// SPDX-FileCopyrightText: 2022 CEA LIST <gael.de-chalendar@cea.fr>
3//
4// SPDX-License-Identifier: MIT
5
7
21
22#include <string>
23#ifdef _DEBUG
24 #undef _DEBUG
25 #include <Python.h>
26 #define _DEBUG
27#else
28 #include <Python.h>
29#endif
30#include <QRegularExpression>
31#include <QtCore/QTemporaryFile>
32
33using namespace std;
37using namespace Lima::Common::Misc;
38
39namespace Lima
40{
41namespace LinguisticProcessing
42{
43namespace SemanticAnalysis
44{
45
47
48
50{
51public:
54
55 PyObject* m_instance;
56 std::shared_ptr<MediaProcessUnit> m_dumper;
57 std::shared_ptr<MediaProcessUnit> m_loader;
61};
62
67
71
74
75
80
82{
84 LERROR << "Failed to import the sys module";
85 PyErr_Print();
86};
87
89{
91 LERROR << "Cannot instantiate the SemanticRoleLabeler python class";
92 PyErr_Print();
93 Py_Exit(1);
94};
95
98 Manager* manager)
99
100{
101#ifdef DEBUG_LP
103 LDEBUG << "KnowledgeBasedSemanticRoleLabeler::init";
104#endif
105
106 MediaId language=manager->getInitializationParameters().media;
107 try {
108 string dumperName=unitConfiguration.getParamsValueAtKey("dumper");
109 // create the dumper
110 m_d->m_dumper = manager->getObject(dumperName);
111 }
114 LERROR << "Missing 'dumper' parameter in KnowledgeBasedSemanticRoleLabeler group for language "
115 << (int)language << " !";
116 throw InvalidConfiguration();
117 }
118
119 try {
120 string loaderName=unitConfiguration.getParamsValueAtKey("loader");
121 // create the loader
122 m_d->m_loader=manager->getObject(loaderName);
123 }
124 catch (InvalidConfiguration& ) {
125 m_d->m_loader = 0;
126 }
129 LERROR << "Missing 'loader' parameter in KnowledgeBasedSemanticRoleLabeler group for language "
130 << (int)language << " !";
131 throw InvalidConfiguration();
132 }
133
134 try {
135 m_d->m_temporaryFileMetadata = QString::fromUtf8(unitConfiguration.getParamsValueAtKey("temporaryFileMetadata").c_str());
136 }
138 // optional parameter: keep default value (empty)
139 }
140
141 if (m_d->m_temporaryFileMetadata.isEmpty())
142 {
143 try {
144 m_d->m_inputSuffix=QString::fromUtf8(unitConfiguration.getParamsValueAtKey("inputSuffix").c_str());
145 }
148 LERROR << "Missing 'inputSuffix' parameter in KnowledgeBasedSemanticRoleLabeler group for language "
149 << (int)language << " !";
150 throw InvalidConfiguration();
151 }
152
153 try {
154 m_d->m_outputSuffix=QString::fromUtf8(unitConfiguration.getParamsValueAtKey("outputSuffix").c_str());
155 }
158 LERROR << "Missing 'outputSuffix' parameter in KnowledgeBasedSemanticRoleLabeler group for language "
159 << (int)language << " !";
160 throw InvalidConfiguration();
161 }
162 }
163 QString kbsrlLogLevel = "error";
164 try
165 {
166 kbsrlLogLevel = QString::fromUtf8(unitConfiguration.getParamsValueAtKey("loglevel").c_str());
167 }
168 catch (NoSuchParam& )
169 {
170 // keep default
171 }
172
173 QString path;
174 try
175 {
176 path = QString::fromUtf8(unitConfiguration.getParamsValueAtKey("path").c_str());
177 }
178 catch (NoSuchParam& )
179 {
181 LERROR << "no param 'path' in KnowledgeBasedSemanticRoleLabeler group configuration";
182 throw InvalidConfiguration();
183 }
184
185 QString mode = "VerbNet";
186 try
187 {
188 mode = QString::fromUtf8(unitConfiguration.getParamsValueAtKey("mode").c_str());
189 if (mode != "VerbNet" && mode != "FrameNet")
190 {
192 LERROR << "Unknown semantic annotation mode" << mode;
193 throw InvalidConfiguration();
194 }
195 }
196 catch (NoSuchParam& )
197 {
198 // keep default
199 }
200
201 // Initialize the python SRL system
202 /*
203 * Find the first python executable in the path and use it as the program name.
204 *
205 * This allows to find the modules set up in an activated virtualenv
206 */
207 QString str_program_name;
208 QString pathEnv = QString::fromUtf8(qgetenv("PATH").constData());
209 QStringList paths = pathEnv.split(QRegularExpression("[;:]"));
210 for (auto it = paths.begin(); it != paths.end(); ++it)
211 {
212 const auto & path = *it;
213 if (QFile::exists(path + "/python" ))
214 {
215 str_program_name = path + "/python";
216 break;
217 }
218 }
219#ifndef WIN32
220 Py_SetProgramName(const_cast<wchar_t*>( str_program_name.toStdWString().c_str()));
221#else
222 Py_SetProgramName( (wchar_t*)str_program_name.unicode() );
223#endif
224
225
226 Py_Initialize();
227
228 PyObject* main_module = PyImport_ImportModule("__main__");
229 PyObject* main_dict = PyModule_GetDict(main_module);
230 PyObject* sys_module = PyImport_ImportModule("sys");
231 if (sys_module == nullptr)
232 {
234 Py_Exit(1);
235 }
236
237 PyDict_SetItemString(main_dict, "sys", sys_module);
238
239 // Add the path to the knowledgesrl pachkage to putho path
240 PyObject* pythonpath = PySys_GetObject("path");
241 if (PyList_Append(pythonpath, PyUnicode_DecodeFSDefault(path.toUtf8().constData())) == -1)
242 {
244 LERROR << "Failed to append to python path";
245 PyErr_Print();
246 Py_Exit(1);
247 }
248
249 // Import the semanticrolelabeler module
250 PyObject* semanticrolelabeler_module = PyImport_ImportModule("semanticrolelabeler");
251 if (semanticrolelabeler_module == NULL)
252 {
254 LERROR << "KnowledgeBasedSemanticRoleLabeler::init"<< __FILE__ << __LINE__ << ": Failed to import srl semanticrolelabeler module";
255 PyErr_Print();
256 Py_Exit(1);
257 }
258
259 // Create the semantic role labeller instance
260 m_d->m_instance = PyObject_CallMethod(
261 semanticrolelabeler_module, "SemanticRoleLabeler", "[sss]",
262 QString(QLatin1String("--loglevel=%1")).arg(kbsrlLogLevel).toUtf8().constData(),
263 QString(QLatin1String("--frame-lexicon=%1")).arg(mode).toUtf8().constData(),
264 QString(QLatin1String("--language=%1")).arg(Lima::Common::MediaticData::MediaticData::single().getMediaId(language).c_str()).toUtf8().constData());
265 if (m_d->m_instance == nullptr)
266 {
268 }
269}
270
272{
274 LERROR << "no LinguisticMetaData ! abort";
275};
276
278{
280 LERROR << "KnowledgeBasedSemanticRoleLabeler: unable to create temporary file";
281};
282
283auto temporary_file_srl_not_open = [](QScopedPointer<QTemporaryFile>& temporaryFile)
284{
286 LERROR << "KnowledgeBasedSemanticRoleLabeler: unable to open temporary file for dumping SRL CoNLL data to it"<< temporaryFile->fileName();
287 LERROR << "KnowledgeBasedSemanticRoleLabeler: keep (do not auto remove) it for debug purpose." ;
288 temporaryFile->setAutoRemove(false);
289};
290
291auto failed_to_load_data_from_temporary_file = [](QScopedPointer<QTemporaryFile>& temporaryFile)
292{
294 LERROR << "KnowledgeBasedSemanticRoleLabeler: failed to load data from temporary file" << temporaryFile->fileName();
295 LERROR << "KnowledgeBasedSemanticRoleLabeler: keep (do not auto remove) it for debug purpose." << temporaryFile->fileName();
296 temporaryFile->setAutoRemove(false);
297};
298
300{
302 LERROR << "Failure during call of the annotate method on" << conllInput;
303 PyErr_Print();
304 Py_Exit(1);
305};
306
308 AnalysisContent& analysis) const
309{
310 TimeUtilsController knowledgeBasedSemanticRoleLabelerProcessTime("KnowledgeBasedSemanticRoleLabeler");
311#ifdef DEBUG_LP
313 LINFO << "start SRL process";
314#endif
315
316 auto metadata = std::dynamic_pointer_cast<LinguisticMetaData>(analysis.getData("LinguisticMetaData"));
317 if (metadata == nullptr)
318 {
320 return MISSING_DATA;
321 }
322
323 QScopedPointer<QTemporaryFile> temporaryFile;
324 if (!m_d->m_temporaryFileMetadata.isEmpty())
325 {
326 QScopedPointer<QTemporaryFile> otherTemp(new QTemporaryFile());
327 temporaryFile.swap(otherTemp);
328 if (!temporaryFile->open())
329 {
332 }
333 metadata->setMetaData(m_d->m_temporaryFileMetadata.toUtf8().constData(),
334 temporaryFile->fileName().toUtf8().constData());
335 }
336
337 // Use CoNLL duper to produce the input to the SRL
338 LimaStatusCode returnCode(SUCCESS_ID);
339 returnCode=m_d->m_dumper->process(analysis);
340 if (returnCode!=SUCCESS_ID) {
342 LERROR << "KnowledgeBasedSemanticRoleLabeler: failed to dump data to temporary file";
343 return returnCode;
344 }
345
346 QString conllInput;
347
348 if (m_d->m_temporaryFileMetadata.isEmpty())
349 {
350 QString fileName = QString::fromUtf8(metadata->getMetaData("FileName").c_str());
351 QString inputFilename;
352 if (!m_d->m_inputSuffix.isEmpty())
353 {
354 inputFilename = fileName+ m_d->m_inputSuffix;
355 }
356 QFile inputFile(inputFilename);
357 inputFile.open(QIODevice::ReadOnly);
358 conllInput = QString::fromUtf8(inputFile.readAll().constData());
359 inputFile.close();
360 }
361 else
362 {
363 if (!temporaryFile->open())
364 {
366 LERROR << "KnowledgeBasedSemanticRoleLabeler: unable to open temporary file after dumping CoNLL data to it"<< temporaryFile->fileName();
367 LERROR << "KnowledgeBasedSemanticRoleLabeler: keep (do not auto remove) it for debug purpose." ;
368 temporaryFile->setAutoRemove(false);
370 }
371 conllInput = QString::fromUtf8(temporaryFile->readAll().constData());
372#ifdef DEBUG_LP
373 temporaryFile->setAutoRemove(false);
375 LDEBUG << "KnowledgeBasedSemanticRoleLabeler: keeping temporary file after dumping CoNLL data to it for debugging"<< temporaryFile->fileName();
376#endif
377 temporaryFile->close();
378 }
379
380 // Run the semantic role labeller
381 PyObject* callResult = PyObject_CallMethod(m_d->m_instance, "annotate", "ss",
382 conllInput.toUtf8().constData(),
383 metadata->getMetaData("Lang").c_str()
384 );
385 if (callResult == nullptr)
386 {
388 }
389
390 // Display the SRL result
391 const char* result = PyUnicode_AsUTF8(callResult);
392 if (result == NULL)
393 {
395 LERROR << "Cannot convert result item to string";
396 PyErr_Print();
397 Py_Exit(1);
398 }
399#ifdef DEBUG_LP
400 LDEBUG << "Python result is:" << result;
401#endif
402 if (m_d->m_temporaryFileMetadata.isEmpty())
403 {
404 QString outputFilename;
405 if (!m_d->m_outputSuffix.isEmpty())
406 {
407 QString fileName = QString::fromUtf8(metadata->getMetaData("FileName").c_str());
408 outputFilename = fileName + m_d->m_outputSuffix;
409 }
410 QFile outputFile(outputFilename);
411 outputFile.open(QIODevice::WriteOnly);
412 outputFile.write(result);
413 outputFile.close();
414 }
415 else
416 {
417 if ( !temporaryFile->open() )
418 {
419 temporary_file_srl_not_open(temporaryFile);
421 }
422 if (!temporaryFile->seek(0))
423 {
425 LERROR << "KnowledgeBasedSemanticRoleLabeler: unable to seek to the beginning of temporary file"<< temporaryFile->fileName();
426 LERROR << "KnowledgeBasedSemanticRoleLabeler: keep (do not auto remove) it for debug purpose." ;
427 temporaryFile->setAutoRemove(false);
428 return UNKNOWN_ERROR;
429 }
430 if (temporaryFile->write(result) == -1)
431 {
433 LERROR << "KnowledgeBasedSemanticRoleLabeler: unable to write SRL result to temporary file"<< temporaryFile->fileName();
434 LERROR << "KnowledgeBasedSemanticRoleLabeler: keep (do not auto remove) it for debug purpose." ;
435 temporaryFile->setAutoRemove(false);
436 return UNKNOWN_ERROR;
437 }
438 temporaryFile->close();
439 }
440 Py_DECREF(callResult);
441 // Import the CoNLL result
442 returnCode=m_d->m_loader->process(analysis);
443 if (returnCode != SUCCESS_ID)
444 {
446 return returnCode;
447 }
448
449 return returnCode;
450}
451
452} //namespace SemanticAnalysis
453} // namespace LinguisticProcessing
454} // namespace Lima
#define KNOWLEDGEBASEDSEMANTICROLELABELER_CLASSID
#define LDEBUG
Definition LimaCommon.h:157
#define LINFO
Definition LimaCommon.h:158
#define LERROR
Definition LimaCommon.h:161
#define SEMANTICANALYSISLOGINIT
Defines a Factory to create Object of type Base.
Holds all data that pass through the ProcessUnits Analysis data are shared pointers,...
std::shared_ptr< AnalysisData > getData(const QString &id)
return AnalysisData by id
return a message when a 'param' was not found
Manage initialization of InitializableObjects using configuration module and parameters.
const InitializationParameters & getInitializationParameters() const
get Initialization Parameters
Use this exception to signal an error in one of the configuration files.
Definition LimaCommon.h:345
This dumper outputs analysis result in various CoNLL formats.
Definition ConllDumper.h:30
void init(Common::XMLConfigurationFiles::GroupConfigurationStructure &unitConfiguration, Manager *manager) override
initialize with parameters from configuration file.
LimaStatusCode process(AnalysisContent &analysis) const override
Process on data in analysisContent.
virtual std::shared_ptr< Object > getObject(const std::string &id)
If object doesn't exists, call the create method.
static const MediaticData & single()
const singleton accessor
Definition Singleton.h:51
This file contains a class to control log of informations about time, such as logging cumulated time ...
static SimpleFactory< MediaProcessUnit, KnowledgeBasedSemanticRoleLabeler > knowledgeBasedSemanticRoleLabelerFactory(KNOWLEDGEBASEDSEMANTICROLELABELER_CLASSID)
NAUTITIA.
LimaStatusCode
Definition LimaCommon.h:236
@ CANNOT_OPEN_FILE_ERROR
Definition LimaCommon.h:239
@ UNKNOWN_ERROR
Definition LimaCommon.h:238
@ SUCCESS_ID
Definition LimaCommon.h:237
@ MISSING_DATA
Definition LimaCommon.h:243
STL namespace.
launch exception related to the configuration file parsing