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