OpenCog Framework  Branch: master, revision 6f0b7fc776b08468cf1b74aa9db028f387b4f0c0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
BackwardChainer.cc
Go to the documentation of this file.
1 /*
2  * BackwardChainer.cc
3  *
4  * Copyright (C) 2014 Misgana Bayetta
5  * Copyright (C) 2015 OpenCog Foundation
6  *
7  * Author: Misgana Bayetta <misgana.bayetta@gmail.com> October 2014
8  * William Ma <https://github.com/williampma>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU Affero General Public License v3 as
12  * published by the Free Software Foundation and including the exceptions
13  * at http://opencog.org/wiki/Licenses
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU Affero General Public License
21  * along with this program; if not, write to:
22  * Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25 
26 #include "BackwardChainer.h"
27 #include "BackwardChainerPMCB.h"
28 #include "UnifyPMCB.h"
29 
30 #include <opencog/util/random.h>
31 
37 
38 using namespace opencog;
39 
41  : _as(as), _configReader(as, rbs),
42  // create a garbage superspace with _as as parent, so codes acting on
43  // _garbage will see stuff in _as, but codes acting on _as will not
44  // see stuff in _garbage
45  _garbage_superspace(&_as) {}
46 
53 {
54  _init_target = init_target;
55 
59 }
60 
62 {
63  return _configReader;
64 }
65 
67 {
68  return _configReader;
69 }
70 
79 {
80  int i = 0;
81 
83  {
84  do_step();
85 
86  // XXX TODO check the TV of the initial target to see if we want more steps
87 
88  i++;
89  }
90 }
91 
96 {
97  logger().debug("[BackwardChainer] ==========================================");
98  logger().debug("[BackwardChainer] Start of a single BC step");
99  logger().debug("[BackwardChainer] %d potential targets", _targets_set.size());
100 
101  // do target selection using some criteria
102  // XXX for example, choose target with low TV 50% of the time
103  Target& selected_target = _targets_set.select();
104 
105  logger().debug("[BackwardChainer] Selected target " + selected_target.get_handle()->toShortString());
106  logger().debug("[BackwardChainer] with var_decl " + selected_target.get_vardecl()->toShortString());
107 
108  if (selected_target.get_varseq().empty())
109  logger().debug("[BackwardChainer] Target is 'Truth Value Query'");
110  else
111  logger().debug("[BackwardChainer] Target is 'Variable Fullfillment Query'");
112 
113  process_target(selected_target);
114 
115  // XXX Cannot clear since some target are reversed grounded rule's input
116  // and contain temp variables. Need a better clean that keep stuff used
117  // in the target set
118  //_garbage_superspace.clear();
119 
120  logger().debug("[BackwardChainer] End of a single BC step");
121 }
122 
129 {
131 }
132 
139 {
140  Handle htarget = target.get_handle();
141 
142  // Check whether this target is a virtual link and is useless to explore
143  if (classserver().isA(htarget->getType(), VIRTUAL_LINK))
144  {
145  logger().debug("[BackwardChainer] Boring virtual link goal, "
146  "skipping " + htarget->toShortString());
147  return;
148  }
149 
150  // before doing any real backward chaining, see if any variables in
151  // vardecl can already be grounded
152  if (not target.get_varseq().empty())
153  {
154  std::vector<VarMap> kb_vmap;
155 
156  HandleSeq kb_match = match_knowledge_base(htarget, target.get_vardecl(),
157  kb_vmap);
158 
159  // Matched something in the knowledge base? Then need to store
160  // any grounding as a possible solution for this target
161  if (not kb_match.empty())
162  {
163  logger().debug("[BackwardChainer] Matched something in knowledge base, "
164  "storing the grounding");
165 
166  for (size_t i = 0; i < kb_match.size(); ++i)
167  {
168  Handle& soln = kb_match[i];
169  VarMap& vgm = kb_vmap[i];
170 
171  logger().debug("[BackwardChainer] Looking at grounding "
172  + soln->toShortString());
173 
174  // add whatever it matched as Target (so new variables can be
175  // filled, and TV updated)
176  _targets_set.emplace(soln,
178 
179  target.store_varmap(vgm);
180  }
181  }
182  }
183 
184  // If logical link, break it up, add each to new targets and return
185  if (_logical_link_types.count(htarget->getType()) == 1)
186  {
187  logger().debug("[BackwardChainer] Breaking into sub-targets");
188 
189  HandleSeq sub_premises = LinkCast(htarget)->getOutgoingSet();
190 
191  for (Handle& h : sub_premises)
192  _targets_set.emplace(h, _garbage_superspace.add_atom(gen_sub_varlist(h, target.get_vardecl(), std::set<Handle>())));
193 
194  return;
195  }
196 
197  /*************************************************/
198  /**** This is where the actual BC step starts ****/
199  /*************************************************/
200 
201  // Find all rules whose implicand can be unified to htarget
202  std::vector<Rule> acceptable_rules = filter_rules(target);
203 
204  logger().debug("[BackwardChainer] %d rules unifiable", acceptable_rules.size());
205 
206  // If no rules to backward chain on, no way to solve this target
207  if (acceptable_rules.empty())
208  return;
209 
210  Rule selected_rule = select_rule(target, acceptable_rules);
211  Rule standardized_rule =
213 
214  logger().debug("[BackwardChainer] Selected rule "
215  + standardized_rule.get_handle()->toShortString());
216 
217  Handle hrule_implicant = standardized_rule.get_implicant();
218  Handle hrule_vardecl = standardized_rule.get_vardecl();
219  HandleSeq qrule_outputs = standardized_rule.get_implicand_seq();
220 
221  std::vector<VarMap> all_implicand_to_target_mappings;
222 
223  // A rule can have multiple outputs, and more than one output will unify
224  // to our target, so get all outputs that works
225  for (Handle h : qrule_outputs)
226  {
227  VarMap temp_mapping;
228 
229  if (not unify(h,
230  htarget,
231  _garbage_superspace.add_atom(gen_sub_varlist(h, hrule_vardecl, std::set<Handle>())),
232  target.get_vardecl(),
233  temp_mapping))
234  continue;
235 
236  all_implicand_to_target_mappings.push_back(temp_mapping);
237  }
238 
239  // try sub-atom unification only if no whole output unification is possible
240  if (all_implicand_to_target_mappings.empty())
241  {
242  logger().debug("[BackwardChainer] Trying sub-atom unification");
243 
244  UnorderedHandleSet output_expanded;
245  for (Handle h : qrule_outputs)
246  {
248  output_expanded.insert(hs.begin(), hs.end());
249  output_expanded.erase(h);
250  }
251 
252  for (Handle h : output_expanded)
253  {
254  VarMap temp_mapping;
255 
256  if (not unify(h,
257  htarget,
258  _garbage_superspace.add_atom(gen_sub_varlist(h, hrule_vardecl, std::set<Handle>())),
259  target.get_vardecl(),
260  temp_mapping))
261  continue;
262 
263  all_implicand_to_target_mappings.push_back(temp_mapping);
264  }
265  }
266 
267  logger().debug("[BackwardChainer] Found %d implicand's output unifiable",
268  all_implicand_to_target_mappings.size());
269 
270  // Randomly select one of the mapping (so that each time the
271  // same target is visited, and the same rule is selected, it is
272  // possible to select a different output to map to)
273  //
274  // XXX TODO use all possible output mapping instead; ie visit them
275  // all, and add all resulting new targets to targets list; this will
276  // avoid having to visit the target multiple times to get all
277  // possible output mappings
278  VarMap implicand_normal_mapping = rand_element(all_implicand_to_target_mappings);
279  for (auto& p : implicand_normal_mapping)
280  logger().debug("[BackwardChainer] Chosen mapping is "
281  + p.first->toShortString()
282  + " to " + p.second->toShortString());
283 
284  // Reverse ground the implicant with the grounding we found from
285  // unifying the implicand
287  std::vector<VarMap> premises_vmap_list, premises_vmap_list_alt;
288  Handle hrule_implicant_normal_grounded = subt.substitute(hrule_implicant, implicand_normal_mapping);
289 
290  logger().debug("[BackwardChainer] Reverse grounded as "
291  + hrule_implicant_normal_grounded->toShortString());
292 
293  // Find all matching premises matching the implicant, where premises_vmap_list
294  // will be the mapping from free variables in himplicant to stuff in a premise
295  HandleSeq possible_premises =
296  match_knowledge_base(hrule_implicant_normal_grounded,
297  _garbage_superspace.add_atom(gen_sub_varlist(hrule_implicant_normal_grounded, hrule_vardecl, target.get_varset())),
298  premises_vmap_list);
299 
300  // only need to generate QuoteLink version when there are free variables
301  if (not target.get_varseq().empty())
302  {
303  // Generate another version where each variables (free or bound) are inside
304  // QuoteLink; mostly to handle where PM cannot map a variable to itself
305  VarMap implicand_quoted_mapping;
306  for (auto& p : implicand_normal_mapping)
307  {
308  // find all variables
309  FindAtoms fv(VARIABLE_NODE);
310  fv.search_set(p.second);
311 
312  // wrap a QuoteLink on each variable
313  VarMap quote_mapping;
314  for (auto& h: fv.varset)
315  quote_mapping[h] = _garbage_superspace.add_atom(createLink(QUOTE_LINK, h));
316 
318  implicand_quoted_mapping[p.first] = subt.substitute(p.second, quote_mapping);
319  }
320 
321  // Reverse ground 2nd version, try it with QuoteLink around variables
322  Handle hrule_implicant_quoted_grounded = subt.substitute(hrule_implicant, implicand_quoted_mapping);
323 
324  logger().debug("[BackwardChainer] Alternative reverse grounded as "
325  + hrule_implicant_quoted_grounded->toShortString());
326 
327  HandleSeq possible_premises_alt =
328  match_knowledge_base(hrule_implicant_quoted_grounded,
329  _garbage_superspace.add_atom(gen_sub_varlist(hrule_implicant_quoted_grounded, hrule_vardecl, target.get_varset())),
330  premises_vmap_list_alt);
331 
332  // collect the possible premises from the two verions of mapping
333  possible_premises.insert(possible_premises.end(),
334  possible_premises_alt.begin(),
335  possible_premises_alt.end());
336  premises_vmap_list.insert(premises_vmap_list.end(), premises_vmap_list_alt.begin(),
337  premises_vmap_list_alt.end());
338  }
339 
340  logger().debug("[BackwardChainer] %d possible permises", possible_premises.size());
341 
342  // If no possible premises, then the reverse grounded rule's implicant
343  // could be added as potential target. Note that however, such target are
344  // not yet in the main atomspace, since whatever the grounded implicant
345  // is, it is possible that it is not valid (like the reverse of if-then
346  // is not always true). It will require some future steps to see if
347  // another rule will generate the target and add it to the main atomspace.
348  // Also note that these targets could contain variables from standardized
349  // apart version of the rule, and should not be added to the main space.
350  if (possible_premises.size() == 0)
351  {
352  logger().debug("[BackwardChainer] Adding rule's grounded input as Target");
353 
354  target.store_step(selected_rule, { hrule_implicant_normal_grounded });
355  _targets_set.emplace(hrule_implicant_normal_grounded,
356  _garbage_superspace.add_atom(gen_sub_varlist(hrule_implicant_normal_grounded, hrule_vardecl, target.get_varset())));
357  return;
358  }
359 
360  // For each set of possible premises, check if they already satisfy the
361  // target, so that we can apply the rule while we are looking at it in the
362  // same step
363  for (size_t i = 0; i < possible_premises.size(); i++)
364  {
365  Handle hp = possible_premises[i];
366  VarMap vm = premises_vmap_list[i];
367 
368  logger().debug("[BackwardChainer] Checking permises " + hp->toShortString());
369 
370  // reverse ground the rule's outputs with the mapping to the premise
371  // so that when we ground the premise, we know how to generate
372  // the final output
373  Handle output_grounded = subt.substitute(standardized_rule.get_implicand(), implicand_normal_mapping);
374 
375  logger().debug("[BackwardChainer] Output reverse grounded step 1 as " + output_grounded->toShortString());
376  output_grounded = subt.substitute(output_grounded, vm);
377 
378  logger().debug("[BackwardChainer] Output reverse grounded step 2 as " + output_grounded->toShortString());
379 
380  std::vector<VarMap> vm_list;
381 
382  // include the implicand mapping into vm so we can do variable chasing
383  vm.insert(implicand_normal_mapping.begin(), implicand_normal_mapping.end());
384 
385  // use pattern matcher to try to ground the variables (if any) in the
386  // selected premises, so we can use this grounding to "apply" the rule
387  // to generate the rule's final output
388  HandleSeq grounded_premises = ground_premises(hp, vm, vm_list);
389 
390  // Check each grounding to see if any has no variable
391  for (size_t i = 0; i < grounded_premises.size(); ++i)
392  {
393  VarMultimap results;
394  Handle& g = grounded_premises[i];
395  VarMap& m = vm_list.at(i);
396 
397  logger().debug("[BackwardChainer] Checking possible permises grounding "
398  + g->toShortString());
399 
400  FindAtoms fv(VARIABLE_NODE);
401  fv.search_set(g);
402 
403  // If some grounding cannot solve the goal, will need to BC
404  if (not fv.varset.empty())
405  continue;
406 
407  // This is a premise grounding that can solve the target, so
408  // apply it by using the mapping to ground the target, and add
409  // it to _as since this is not garbage; this should generate
410  // all the outputs of the rule, and execute any evaluatable
411  //
412  // XXX TODO the TV of the original "Variable Fullfillment" target
413  // need to be changed here... right?
414  Instantiator inst(&_as);
415  Handle added = inst.instantiate(output_grounded, m);
416 
417  logger().debug("[BackwardChainer] Added " + added->toShortString() + " to _as");
418 
419  // Add the grounding to the return results
420  for (Handle& h : target.get_varseq())
421  results[h].emplace(m.at(h));
422 
423  target.store_varmap(results);
424  }
425 
426  // XXX TODO premise selection would be done here to
427  // determine whether to BC on a premise
428 
429 
430 
431  // non-logical link can be added straight to targets list
432  if (_logical_link_types.count(hp->getType()) == 0)
433  {
434  target.store_step(selected_rule, { hp });
436  continue;
437  }
438 
439  logger().debug("[BackwardChainer] Before breaking apart into sub-premises");
440 
441  // Else break out any logical link and add to targets
442  HandleSeq sub_premises = LinkCast(hp)->getOutgoingSet();
443  target.store_step(selected_rule, sub_premises);
444 
445  for (Handle& s : sub_premises)
447  }
448 
449  return;
450 }
451 
461 std::vector<Rule> BackwardChainer::filter_rules(const Target& target)
462 {
463  std::vector<Rule> rules;
464 
465  Handle htarget = target.get_handle();
466  Handle htarget_vardecl = target.get_vardecl();
467 
468  for (Rule& r : _configReader.get_rules())
469  {
470  // unify against the standardized version, so the result will match
471  // with what we will be applying against at later step
472  Rule standardized_rule = r.gen_standardize_apart(&_garbage_superspace);
473 
474  Handle hrule_vardecl = standardized_rule.get_vardecl();
475  HandleSeq output = standardized_rule.get_implicand_seq();
476  bool unifiable = false;
477 
478  // check if any of the implicand's output can be unified to target
479  for (Handle h : output)
480  {
481  VarMap mapping;
482 
483  if (not unify(h,
484  htarget,
485  _garbage_superspace.add_atom(gen_sub_varlist(h, hrule_vardecl, std::set<Handle>())),
486  htarget_vardecl,
487  mapping))
488  continue;
489 
490  unifiable = true;
491  break;
492  }
493 
494  // if not unifiable, try sub-atom unification
495  if (not unifiable)
496  {
497  UnorderedHandleSet output_expanded;
498  for (Handle h : output)
499  {
501  output_expanded.insert(hs.begin(), hs.end());
502  output_expanded.erase(h);
503  }
504 
505  for (Handle h : output_expanded)
506  {
507  VarMap mapping;
508 
509  if (not unify(h,
510  htarget,
511  _garbage_superspace.add_atom(gen_sub_varlist(h, hrule_vardecl, std::set<Handle>())),
512  htarget_vardecl,
513  mapping))
514  continue;
515 
516  unifiable = true;
517  break;
518  }
519  }
520 
521  // move on to next rule if htarget cannot map to the output
522  if (not unifiable)
523  continue;
524 
525  rules.push_back(r);
526  }
527 
528  return rules;
529 }
530 
540  Handle hpattern_vardecl,
541  vector<VarMap>& vmap)
542 {
543  // Get all VariableNodes (unquoted)
544  FindAtoms fv(VARIABLE_NODE);
545  fv.search_set(hpattern);
546 
547  if (hpattern_vardecl == Handle::UNDEFINED)
548  {
549  HandleSeq vars;
550  for (auto& h : fv.varset)
551  vars.push_back(h);
552 
553  hpattern_vardecl = _garbage_superspace.add_atom(createVariableList(vars));
554  }
555 
556  logger().debug("[BackwardChainer] Matching knowledge base with "
557  " %s and variables %s",
558  hpattern->toShortString().c_str(),
559  hpattern_vardecl->toShortString().c_str());
560 
561  if (VariableListCast(hpattern_vardecl)->get_variables().varseq.empty())
562  return HandleSeq();
563 
564  // Pattern Match on _garbage_superspace since some atoms in hpattern could
565  // be in the _garbage space
566  PatternLinkPtr sl(createPatternLink(hpattern_vardecl, hpattern));
568  VariableListCast(hpattern_vardecl));
569 
570  sl->satisfy(pmcb);
571 
572  vector<map<Handle, Handle>> var_solns = pmcb.get_var_list();
573  vector<map<Handle, Handle>> pred_solns = pmcb.get_pred_list();
574 
575  HandleSeq results;
576 
577  logger().debug("[BackwardChainer] Pattern matcher found %d matches",
578  var_solns.size());
579 
580  for (size_t i = 0; i < var_solns.size(); i++)
581  {
582  HandleSeq i_pred_soln;
583 
584  // check for bad mapping
585  for (auto& p : pred_solns[i])
586  {
587  // don't want matched clause that is part of a rule
588  auto& rules = _configReader.get_rules();
589  if (std::any_of(rules.begin(), rules.end(), [&](Rule& r) {
590  return is_atom_in_tree(r.get_handle(), p.second); }))
591  {
592  logger().debug("[BackwardChainer] matched clause in rule");
593  break;
594  }
595 
596  // don't want matched stuff with some part of a rule inside
597  if (std::any_of(rules.begin(), rules.end(), [&](Rule& r) {
598  return is_atom_in_tree(p.second, r.get_handle()); }))
599  {
600  logger().debug("[BackwardChainer] matched clause wrapping rule");
601  break;
602  }
603 
604  // don't want matched clause that is not in the parent _as
605  if (_as.get_atom(p.second) == Handle::UNDEFINED)
606  {
607  logger().debug("[BackwardChainer] matched clause not in _as");
608  break;
609  }
610 
611  i_pred_soln.push_back(p.second);
612  }
613 
614  if (i_pred_soln.size() != pred_solns[i].size())
615  continue;
616 
617  // if the original htarget is multi-clause, wrap the solution with the
618  // same logical link
619  // XXX TODO preserve htarget's order (but logical link are unordered...)
620  Handle this_result;
621  if (_logical_link_types.count(hpattern->getType()) == 1)
622  this_result = _garbage_superspace.add_link(hpattern->getType(),
623  i_pred_soln);
624  else
625  this_result = i_pred_soln[0];
626 
627  results.push_back(this_result);
628  vmap.push_back(var_solns[i]);
629  }
630 
631  return results;
632 }
633 
643  const VarMap& premise_vmap,
644  std::vector<VarMap>& vmap_list)
645 {
646  HandleSeq results;
647 
648  // XXX are all variables in premises free?
649  FindAtoms fv(VARIABLE_NODE);
650  fv.search_set(hpremise);
651 
652  // if the target is already fully grounded
653  if (fv.varset.empty())
654  {
655  VarMap old_map = premise_vmap;
656  VarMap new_map;
657 
658  // do variable chasing
659  for (const auto& p : premise_vmap)
660  {
661  if (old_map.count(p.second) == 1)
662  {
663  new_map[p.first] = old_map[p.second];
664  new_map[p.second] = old_map[p.second];
665  old_map.erase(p.second);
666  }
667  else
668  new_map[p.first] = p.second;
669  }
670 
671  // add any leftover mapping into final ouput
672  new_map.insert(old_map.begin(), old_map.end());
673 
674  vmap_list.push_back(new_map);
675  results.push_back(hpremise);
676 
677  return results;
678  }
679 
680  Handle premises = hpremise;
681 
682  if (_logical_link_types.count(premises->getType()) == 1)
683  {
684  HandleSeq sub_premises;
685  HandleSeq oset = LinkCast(hpremise)->getOutgoingSet();
686 
687  for (const Handle& h : oset)
688  {
689  // ignore premises with no free var
690  if (get_free_vars_in_tree(h).empty())
691  continue;
692 
693  sub_premises.push_back(h);
694  }
695 
696  if (sub_premises.size() == 1)
697  premises = sub_premises[0];
698  else
699  premises = _garbage_superspace.add_link(hpremise->getType(),
700  sub_premises);
701  }
702 
703  logger().debug("[BackwardChainer] Grounding " + premises->toShortString());
704 
705  std::vector<VarMap> temp_vmap_list;
706 
707  HandleSeq temp_results = match_knowledge_base(premises, Handle::UNDEFINED, temp_vmap_list);
708 
709  // chase the variables so that if a variable A were mapped to another
710  // variable B in premise_vmap, and after pattern matching, B now map
711  // to some solution, change A to map to the same solution
712  for (unsigned int i = 0; i < temp_results.size(); ++i)
713  {
714  VarMap& tvm = temp_vmap_list[i];
715  VarMap this_map;
716 
717  for (const auto& p : premise_vmap)
718  {
719  if (tvm.count(p.second) == 1)
720  {
721  this_map[p.first] = tvm[p.second];
722  this_map[p.second] = tvm[p.second];
723  tvm.erase(p.second);
724  }
725  else
726  this_map[p.first] = p.second;
727  }
728 
729  // add any leftover mapping into final ouput
730  this_map.insert(tvm.begin(), tvm.end());
731 
732  vmap_list.push_back(this_map);
733  results.push_back(temp_results[i]);
734  }
735 
736  return results;
737 }
738 
757 bool BackwardChainer::unify(const Handle& hsource,
758  const Handle& hmatch,
759  Handle hsource_vardecl,
760  Handle hmatch_vardecl,
761  VarMap& result)
762 {
763  // lazy way of restricting PM to be between two atoms
764  AtomSpace temp_space;
765 
766  Handle temp_hsource = temp_space.add_atom(hsource);
767  Handle temp_hmatch = temp_space.add_atom(hmatch);
768  Handle temp_hsource_vardecl = temp_space.add_atom(hsource_vardecl);
769  Handle temp_hmatch_vardecl = temp_space.add_atom(hmatch_vardecl);
770 
771  FindAtoms fv(VARIABLE_NODE);
772  fv.search_set(hsource);
773 
774  if (temp_hsource_vardecl == Handle::UNDEFINED)
775  {
776  HandleSeq vars;
777  for (const Handle& h : fv.varset)
778  vars.push_back(h);
779 
780  temp_hsource_vardecl = temp_space.add_atom(createVariableList(vars));
781  }
782 
783  PatternLinkPtr sl(createPatternLink(temp_hsource_vardecl, temp_hsource));
784  UnifyPMCB pmcb(&temp_space, VariableListCast(temp_hsource_vardecl), VariableListCast(temp_hmatch_vardecl));
785 
786  sl->satisfy(pmcb);
787 
788  // if no grounding
789  if (pmcb.get_var_list().size() == 0)
790  return false;
791 
792  std::vector<std::map<Handle, Handle>> pred_list = pmcb.get_pred_list();
793  std::vector<std::map<Handle, Handle>> var_list = pmcb.get_var_list();
794 
795  VarMap good_map;
796 
797  // go thru each solution, and get the first one that map the whole temp_hmatch
798  //
799  // XXX TODO branch on the various groundings? how to properly handle
800  // multiple possible unify option????
801  for (size_t i = 0; i < pred_list.size(); ++i)
802  {
803  for (const auto& p : pred_list[i])
804  {
805  if (is_atom_in_tree(p.second, temp_hmatch))
806  {
807  good_map = var_list[i];
808  i = pred_list.size();
809  break;
810  }
811  }
812  }
813 
814  // if none of the mapping map the whole temp_hmatch (possible in the case
815  // of sub-atom unification that map a typed variable to another variable)
816  if (good_map.size() == 0)
817  return false;
818 
819  // change the mapping from temp_atomspace to current atomspace
820  for (auto& p : good_map)
821  {
822  Handle var = p.first;
823  Handle grn = p.second;
824 
825  result[_garbage_superspace.get_atom(var)] =
827  }
828 
829  return true;
830 }
831 
842 Rule BackwardChainer::select_rule(Target& target, const std::vector<Rule>& rules)
843 {
844  // store how many times each rule has been used for the target
845  std::vector<double> weights;
846  std::for_each(rules.begin(), rules.end(),
847  [&](const Rule& r)
848  { weights.push_back(target.get_selection_count() - target.rule_count(r) + 1); });
849 
850  // Select the rule that has been applied least
851  return rules[randGen().randDiscrete(weights)];
852 }
853 
870  const Handle& parent_varlist,
871  std::set<Handle> additional_free_varset)
872 {
873  FindAtoms fv(VARIABLE_NODE);
874  fv.search_set(parent);
875 
876  HandleSeq oset = LinkCast(parent_varlist)->getOutgoingSet();
877  HandleSeq final_oset;
878 
879  // for each var in varlist, check if it is used in parent
880  for (const Handle& h : oset)
881  {
882  Type t = h->getType();
883  if (VARIABLE_NODE == t && fv.varset.count(h) == 1)
884  {
885  final_oset.push_back(h);
886  additional_free_varset.erase(h);
887  }
888  else if (TYPED_VARIABLE_LINK == t
889  and fv.varset.count(LinkCast(h)->getOutgoingSet()[0]) == 1)
890  {
891  final_oset.push_back(h);
892  additional_free_varset.erase(LinkCast(h)->getOutgoingSet()[0]);
893  }
894  }
895 
896  // for each var left in the additional_free_varset, check
897  // if it is used in parent
898  for (const Handle& h : additional_free_varset)
899  {
900  if (fv.varset.count(h) == 1)
901  final_oset.push_back(h);
902  }
903 
904  return Handle(createVariableList(final_oset));
905 }
906 
std::set< Handle > varset
Definition: FindUtils.h:88
std::vector< Rule > filter_rules(const Target &target)
#define createLink
Definition: Link.h:269
Handle get_implicand()
Definition: Rule.cc:146
Rule gen_standardize_apart(AtomSpace *as)
Definition: Rule.cc:220
static bool is_atom_in_tree(const Handle &tree, const Handle &atom)
Definition: FindUtils.h:186
unsigned int size()
Definition: Target.cc:161
void set_target(Handle init_target)
std::vector< Handle > HandleSeq
a list of handles
Definition: Handle.h:246
Handle get_vardecl()
Definition: Rule.cc:103
HandleSeq get_varseq() const
Definition: Target.h:77
virtual std::string toShortString(std::string indent="")=0
#define createPatternLink
Definition: PatternLink.h:188
Handle get_vardecl() const
Definition: Target.h:92
Rule select_rule(Target &target, const std::vector< Rule > &rules)
void store_varmap(VarMultimap &vm)
Definition: Target.cc:76
void emplace(Handle h, Handle hvardecl)
Definition: Target.cc:150
int get_maximum_iterations() const
std::vector< std::map< Handle, Handle > > get_pred_list()
Type getType() const
Definition: Atom.h:197
ClassServer & classserver(ClassServerFactory *=ClassServer::createInstance)
Definition: ClassServer.cc:159
const VarMultimap & get_chaining_result()
Handle get_handle() const
Definition: Target.h:70
static const Handle UNDEFINED
Definition: Handle.h:77
UREConfigReader _configReader
#define createVariableList
Definition: VariableList.h:101
HandleSeq get_implicand_seq()
Definition: Rule.cc:163
HandleSeq match_knowledge_base(const Handle &htarget, Handle htarget_vardecl, std::vector< VarMap > &vmap)
Handle instantiate(const Handle &expr, const std::map< Handle, Handle > &vars)
HandleSeq ground_premises(const Handle &htarget, const VarMap &vmap, std::vector< VarMap > &vmap_list)
Handle get_atom(Handle)
Definition: AtomSpace.cc:312
HandleSeq get_free_vars_in_tree(const Handle &tree)
std::map< Handle, UnorderedHandleSet > VarMultimap
bool unify(const Handle &hsource, const Handle &hmatch, Handle hsource_vardecl, Handle hmatch_vardecl, VarMap &result)
static VariableListPtr VariableListCast(const Handle &h)
Definition: VariableList.h:95
const std::vector< Rule > & get_rules() const
Handle substitute(const Handle &expr, const std::map< Handle, Handle > &vars)
Definition: Substitutor.h:83
static LinkPtr LinkCast(const Handle &h)
Definition: Link.h:263
unsigned int rule_count(const Rule &r)
Definition: Target.cc:107
std::set< Handle > get_varset() const
Definition: Target.h:87
UREConfigReader & get_config()
unordered_set< Type > _logical_link_types
std::map< Handle, Handle > VarMap
UnorderedHandleSet get_all_unique_atoms(Handle h)
Definition: AtomUtils.cc:113
std::shared_ptr< PatternLink > PatternLinkPtr
Definition: PatternLink.h:181
void store_step(const Rule &r, const HandleSeq &premises)
Definition: Target.cc:61
unsigned int get_selection_count() const
Definition: Target.h:101
Target & select()
Definition: Target.cc:178
unsigned short Type
type of Atoms, represented as short integer (16 bits)
Definition: types.h:40
std::unordered_set< Handle, handle_hash > UnorderedHandleSet
a hash that associates the handle to its unique identificator
Definition: Handle.h:250
Handle add_link(Type t, const HandleSeq &outgoing, bool async=false)
Definition: AtomSpace.cc:175
Handle gen_sub_varlist(const Handle &parent, const Handle &parent_varlist, std::set< Handle > additional_free_varset)
void process_target(Target &target)
const VarMultimap & get_varmap() const
Definition: Target.h:99
Target & get(Handle &h)
Definition: Target.cc:204
void search_set(const Handle &h)
Definition: FindUtils.h:128
Handle get_handle()
Definition: Rule.cc:93
BackwardChainer(AtomSpace &as, Handle rbs)
Handle get_implicant()
Definition: Rule.cc:113
std::vector< std::map< Handle, Handle > > get_var_list()
Handle add_atom(AtomPtr atom, bool async=false)
Definition: AtomSpace.cc:100