knowrob  2.1.0
A Knowledge Base System for Cognition-enabled Robots
ReasonerManager.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of KnowRob, please consult
3  * https://github.com/knowrob/knowrob for license details.
4  */
5 
6 #include "knowrob/Logger.h"
7 #include "knowrob/reasoner/ReasonerManager.h"
8 #include "knowrob/reasoner/ReasonerError.h"
9 
10 using namespace knowrob;
11 
12 ReasonerManager::ReasonerManager(KnowledgeBase *kb, const std::shared_ptr<StorageManager> &backendManager)
13  : PluginManager(),
14  kb_(kb),
15  backendManager_(backendManager) {
16 }
17 
19  std::lock_guard<std::mutex> scoped_lock(staticMutex_);
20  for (auto &x: pluginPool_) {
21  x.second->value()->unload();
22  // make sure reasoner does not interact with the manager anymore
23  x.second->value()->setReasonerManager(nullptr);
24  }
25 }
26 
27 void ReasonerManager::setReasonerStorage(const std::shared_ptr<NamedPlugin<Reasoner>> &plugin,
28  const std::shared_ptr<Storage> &dataBackend) {
29  if (plugin->language() == PluginLanguage::PYTHON) {
30  py::gil_lock acquire;
31  plugin->value()->setStorage(dataBackend);
32  } else {
33  plugin->value()->setStorage(dataBackend);
34  }
35  reasonerBackends_[plugin->value()->reasonerName()->stringForm()] = dataBackend;
36 }
37 
38 std::shared_ptr<Storage> ReasonerManager::getReasonerStorage(const std::shared_ptr<NamedReasoner> &reasoner) {
39  auto it = reasonerBackends_.find(reasoner->name());
40  if (it != reasonerBackends_.end()) {
41  return it->second;
42  } else {
43  return nullptr;
44  }
45 }
46 
47 std::vector<DefiningReasoner> ReasonerManager::findDefiningReasoner(const PredicateIndicator &indicator) const {
48  std::vector<DefiningReasoner> reasoners;
49 
50  if (indicator.arity() == 1) {
51  // unary predicates can be RDF class expressions.
52  // in this case we need to check the class hierarchy.
53  auto rdfClass = kb_->vocabulary()->getDefinedClass(indicator.functor()->stringForm());
54  if (rdfClass) {
55  for (auto &x: goalDriven_) {
56  for (auto &definedClassIndicator : x.second->definedClasses()) {
57  auto definedClass = kb_->vocabulary()->getDefinedClass(definedClassIndicator.functor()->stringForm());
58  if(definedClass && definedClass->isSubClassOf(rdfClass)) {
59  reasoners.push_back({x.second, definedClass->iriAtom()});
60  break;
61  }
62  }
63  }
64  return reasoners;
65  }
66  }
67 
68  if (indicator.arity() == 2) {
69  // binary predicates can be RDF properties.
70  // in this case we need to check the property hierarchy.
71  auto rdfProperty = kb_->vocabulary()->getDefinedProperty(indicator.functor()->stringForm());
72  if (rdfProperty) {
73  for (auto &x: goalDriven_) {
74  for (auto &definedPropertyIndicator : x.second->definedRelations()) {
75  auto definedProperty = kb_->vocabulary()->getDefinedProperty(definedPropertyIndicator.functor()->stringForm());
76  if ( definedProperty && definedProperty->isSubPropertyOf(rdfProperty) ) {
77  reasoners.push_back({x.second, definedProperty->iriAtom()});
78  break;
79  }
80  }
81  }
82  return reasoners;
83  }
84  }
85 
86  for (auto &x: goalDriven_) {
87  if (x.second->isRelationDefined(indicator) || (indicator.arity()==1 && x.second->isClassDefined(indicator.functor()->stringForm()))) {
88  reasoners.push_back({x.second, indicator.functor()});
89  }
90  }
91  return reasoners;
92 }
93 
94 std::shared_ptr<NamedReasoner> ReasonerManager::loadPlugin(const boost::property_tree::ptree &config) {
95  // get a reasoner factory
96  std::shared_ptr<ReasonerFactory> factory = findFactory(config);
97  // make sure factory was found above
98  if (!factory) throw ReasonerError("failed to load a reasoner.");
99  // create a reasoner id, or use name property
100  std::string reasonerID = getPluginID(factory, config);
101  KB_INFO("Using reasoner `{}` with type `{}`.", reasonerID, factory->name());
102 
103  // create a new reasoner instance
104  auto reasoner = factory->create(reasonerID);
105  // reasoner need to have a reference to the reasoner manager such that
106  // predicates can be defined that interact with the KB
107  reasoner->value()->setReasonerManager(this);
108  reasoner->value()->setReasonerName(reasonerID);
109 
110  auto backendName = config.get_optional<std::string>("data-backend");
111  if (backendName.has_value()) {
112  auto definedBackend = backendManager_->getPluginWithID(backendName.value());
113  if (definedBackend) {
114  setReasonerStorage(reasoner, definedBackend->value());
115  } else {
116  throw ReasonerError("Reasoner `{}` refers to unknown data-backend `{}`.", reasonerID, backendName.value());
117  }
118  } else {
119  // check if reasoner implements DataBackend interface
120  auto backend = std::dynamic_pointer_cast<Storage>(reasoner->value());
121  if (backend) {
122  setReasonerStorage(reasoner, backend);
123  backendManager_->addPlugin(reasonerID, reasoner->language(), backend);
124  } else {
125  throw ReasonerError("Reasoner `{}` has no 'data-backend' configured.", reasonerID);
126  }
127  }
128  auto definedReasoner = addPlugin(reasonerID, reasoner->language(), reasoner->value());
129 
130  PropertyTree pluginConfig(std::make_shared<boost::property_tree::ptree>(config));
131  if (!initializeReasoner(reasoner, pluginConfig)) {
132  KB_WARN("Reasoner `{}` failed to loadConfig.", reasonerID);
133  } else {
134  // load the reasoner-specific data sources.
135  for (auto &dataSource: pluginConfig.dataSources()) {
136  if (!reasoner->value()->loadDataSource(dataSource)) {
137  KB_WARN("Reasoner `{}` failed to load data source {}.", reasonerID, dataSource->uri());
138  }
139  }
140  }
141 
142  return definedReasoner;
143 }
144 
145 std::shared_ptr<NamedReasoner>
146 ReasonerManager::addPlugin(std::string_view reasonerID, PluginLanguage language, const std::shared_ptr<Reasoner> &reasoner) {
147  if (pluginPool_.find(reasonerID) != pluginPool_.end()) {
148  KB_WARN("overwriting reasoner with name '{}'", reasonerID);
149  }
150  auto managedReasoner = std::make_shared<NamedReasoner>(reasonerID, language, reasoner);
151  pluginPool_.emplace(managedReasoner->name(), managedReasoner);
152  reasoner->setReasonerManager(this);
153  reasoner->setReasonerName(reasonerID);
154  initPlugin(managedReasoner);
155  // indicate that the origin `reasonerID` is a reasoner, and thus belongs to the session
156  backendManager_->vocabulary()->importHierarchy()->addDirectImport(
157  backendManager_->vocabulary()->importHierarchy()->ORIGIN_REASONER, reasonerID);
158 
159  // check if reasoner implements DataBackend interface
160  auto backend = std::dynamic_pointer_cast<Storage>(reasoner);
161  if (backend) {
162  setReasonerStorage(managedReasoner, backend);
163  backendManager_->addPlugin(reasonerID, language, backend);
164  }
165 
166  return managedReasoner;
167 }
168 
169 bool ReasonerManager::initializeReasoner(const std::shared_ptr<NamedReasoner> &namedReasoner, PropertyTree &config) {
170  if (namedReasoner->language() == PluginLanguage::PYTHON) {
171  py::gil_lock acquire;
172  return namedReasoner->value()->initializeReasoner(config);
173  } else {
174  return namedReasoner->value()->initializeReasoner(config);
175  }
176 }
177 
178 void ReasonerManager::initPlugin(const std::shared_ptr<NamedReasoner> &namedReasoner) {
179  // initialize the reasoner language type (entering python code requires special treatment)
180  namedReasoner->value()->setReasonerLanguage(namedReasoner->language());
181  // check if the reasoner is data-driven
182  auto dataDriven = std::dynamic_pointer_cast<DataDrivenReasoner>(namedReasoner->value());
183  if (dataDriven) {
184  KB_INFO("Using data-driven reasoner with id '{}'.", namedReasoner->name());
185  dataDriven_[namedReasoner->name()] = dataDriven;
186  }
187  // check if the reasoner is goal-driven
188  auto goalDriven = std::dynamic_pointer_cast<GoalDrivenReasoner>(namedReasoner->value());
189  if (goalDriven) {
190  KB_INFO("Using goal-driven reasoner with id '{}'.", namedReasoner->name());
191  goalDriven_[namedReasoner->name()] = goalDriven;
192  }
193 }
194 
196  const GoalDrivenReasonerPtr &reasoner,
197  const std::vector<FirstOrderLiteralPtr> &literals,
198  const QueryContextPtr &ctx) {
199  auto reasonerRunner = std::make_shared<ReasonerRunner>();
200  reasonerRunner->reasoner = reasoner;
201  if(literals.size()>1) {
202  auto conjunction = std::make_shared<SimpleConjunction>(literals);
203  reasonerRunner->query = std::make_shared<Goal>(conjunction, ctx);
204  } else if (literals.size() == 1) {
205  reasonerRunner->query = std::make_shared<Goal>(literals[0], ctx);
206  } else {
207  throw ReasonerError("Reasoner {} received an empty query.", *reasoner->reasonerName());
208  }
209  KB_DEBUG("Evaluating query `{}` with reasoner \"{}\"",
210  *reasonerRunner->query->formula(), *reasoner->reasonerName());
211  // avoid that the exception handler keeps a reference to the reasonerRunner as
212  // this would prevent the runner from being destroyed.
213  auto reasonerRunnerPtr = reasonerRunner.get();
214  // run reasoner in a thread
215  DefaultThreadPool()->pushWork(
216  reasonerRunner,
217  [reasonerRunnerPtr](const std::exception &exc) {
218  KB_ERROR("Reasoner {} produced an error in query evaluation. {} [{}]",
219  *reasonerRunnerPtr->reasoner->reasonerName(), exc.what(), *reasonerRunnerPtr->query->formula());
220  reasonerRunnerPtr->query->finish();
221  });
222  // return the (incomplete) answer buffer
223  return reasonerRunner->query->answerBuffer();
224 }
#define KB_DEBUG
Definition: Logger.h:25
#define KB_INFO
Definition: Logger.h:26
#define KB_ERROR
Definition: Logger.h:28
#define KB_WARN
Definition: Logger.h:27
auto & vocabulary() const
Definition: KnowledgeBase.h:67
std::shared_ptr< PluginFactory< Reasoner > > findFactory(const boost::property_tree::ptree &config)
std::string getPluginID(const std::shared_ptr< PluginFactory< Reasoner >> &factory, const boost::property_tree::ptree &config)
std::map< std::string_view, std::shared_ptr< NamedPlugin< Reasoner > >, std::less<> > pluginPool_
auto & dataSources() const
Definition: PropertyTree.h:83
std::shared_ptr< NamedReasoner > loadPlugin(const boost::property_tree::ptree &config) override
std::vector< DefiningReasoner > findDefiningReasoner(const PredicateIndicator &indicator) const
ReasonerManager(KnowledgeBase *kb, const std::shared_ptr< StorageManager > &backendManager)
std::shared_ptr< Storage > getReasonerStorage(const std::shared_ptr< NamedReasoner > &reasoner)
std::shared_ptr< NamedReasoner > addPlugin(std::string_view reasonerID, PluginLanguage language, const std::shared_ptr< Reasoner > &reasoner) override
static TokenBufferPtr evaluateQuery(const GoalDrivenReasonerPtr &reasoner, const std::vector< FirstOrderLiteralPtr > &literals, const QueryContextPtr &ctx)
TermRule & string()
Definition: terms.cpp:63
PluginLanguage
Definition: NamedPlugin.h:16
std::shared_ptr< TokenBuffer > TokenBufferPtr
Definition: TokenBuffer.h:43
std::shared_ptr< ThreadPool > DefaultThreadPool()
Definition: ThreadPool.cpp:19
std::shared_ptr< const QueryContext > QueryContextPtr
Definition: QueryContext.h:41
std::shared_ptr< GoalDrivenReasoner > GoalDrivenReasonerPtr