OpenCog Framework  Branch: master, revision 6f0b7fc776b08468cf1b74aa9db028f387b4f0c0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
PatternMatch.cc
Go to the documentation of this file.
1 /*
2  * PatternMatch.cc
3  *
4  * Copyright (C) 2009, 2014, 2015 Linas Vepstas
5  *
6  * Author: Linas Vepstas <linasvepstas@gmail.com> January 2009
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Affero General Public License v3 as
10  * published by the Free Software Foundation and including the exceptions
11  * at http://opencog.org/wiki/Licenses
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Affero General Public License
19  * along with this program; if not, write to:
20  * Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #include <opencog/util/Logger.h>
25 
28 
31 
32 #include "PatternMatch.h"
33 #include "PatternMatchEngine.h"
34 #include "PatternMatchCallback.h"
35 
36 using namespace opencog;
37 
38 // Uncomment below to enable debug print
39 // #define DEBUG
40 #ifdef DEBUG
41  #define dbgprt(f, varargs...) printf(f, ##varargs)
42 #else
43  #define dbgprt(f, varargs...)
44 #endif
45 
46 /* ================================================================= */
51 {
52  private:
54 
55  public:
57 
58  // Pass all the calls straight through, except one.
59  bool node_match(const Handle& node1, const Handle& node2) {
60  return _cb.node_match(node1, node2);
61  }
62  bool variable_match(const Handle& node1, const Handle& node2) {
63  return _cb.variable_match(node1, node2);
64  }
65  bool link_match(const LinkPtr& link1, const LinkPtr& link2) {
66  return _cb.link_match(link1, link2);
67  }
68  bool post_link_match(const LinkPtr& link1, const LinkPtr& link2) {
69  return _cb.post_link_match(link1, link2);
70  }
71  bool fuzzy_match(const Handle& h1, const Handle& h2) {
72  return _cb.fuzzy_match(h1, h2);
73  }
74  bool evaluate_sentence(const Handle& link_h,
75  const std::map<Handle, Handle> &gnds) {
76  throw InvalidParamException(TRACE_INFO,
77  "Not expecting a virtual link here!");
78  }
79  bool clause_match(const Handle& pattrn_link_h, const Handle& grnd_link_h) {
80  return _cb.clause_match(pattrn_link_h, grnd_link_h);
81  }
82  bool optional_clause_match(const Handle& pattrn, const Handle& grnd) {
83  return _cb.optional_clause_match(pattrn, grnd);
84  }
86  return _cb.get_incoming_set(h);
87  }
88  void push(void) { _cb.push(); }
89  void pop(void) { _cb.pop(); }
90  void set_pattern(const Variables& vars,
91  const Pattern& pat)
92  {
93  _cb.set_pattern(vars, pat);
94  }
95 
97  {
98  return _cb.initiate_search(pme);
99  }
100 
101  // This one we don't pass through. Instead, we collect the
102  // groundings.
103  bool grounding(const std::map<Handle, Handle> &var_soln,
104  const std::map<Handle, Handle> &term_soln)
105  {
106  _term_groundings.push_back(term_soln);
107  _var_groundings.push_back(var_soln);
108  return false;
109  }
110 
111  std::vector<std::map<Handle, Handle>> _term_groundings;
112  std::vector<std::map<Handle, Handle>> _var_groundings;
113 };
114 
132  const std::vector<Handle>& virtuals,
133  const std::vector<Handle>& negations, // currently ignored
134  const std::map<Handle, Handle>& var_gnds,
135  const std::map<Handle, Handle>& term_gnds,
136  // copies, NOT references!
137  std::vector<std::vector<std::map<Handle, Handle>>> comp_var_gnds,
138  std::vector<std::vector<std::map<Handle, Handle>>> comp_term_gnds)
139 {
140  // If we are done with the recursive step, then we have one of the
141  // many combinatoric possibilities in the var_gnds and term_gnds
142  // maps. Submit this grounding map to the virtual links, and see
143  // what they've got to say about it.
144  if (0 == comp_var_gnds.size())
145  {
146 #ifdef DEBUG
147  dbgprt("\nExplore one possible combinatoric grounding "
148  "(var_gnds.size = %zu, term_gnds.size = %zu):\n",
149  var_gnds.size(), term_gnds.size());
150  PatternMatchEngine::print_solution(var_gnds, term_gnds);
151 #endif
152 
153  // Note, FYI, that if there are no virtual clauses at all,
154  // then this loop falls straight-through, and the grounding
155  // is reported as a match to the callback. That is, the
156  // virtuals only serve to reject possibilities.
157  for (const Handle& virt : virtuals)
158  {
159  // At this time, we expect all virtual links to be in
160  // one of two forms: either EvaluationLink's or
161  // GreaterThanLink's. The EvaluationLinks should have
162  // the structure
163  //
164  // EvaluationLink
165  // GroundedPredicateNode "scm:blah"
166  // ListLink
167  // Arg1Atom
168  // Arg2Atom
169  //
170  // The GreaterThanLink's should have the "obvious" structure
171  //
172  // GreaterThanLink
173  // Arg1Atom
174  // Arg2Atom
175  //
176  // In either case, one or more VariableNodes should appear
177  // in the Arg atoms. So, we ground the args, and pass that
178  // to the callback.
179 
180  bool match = cb.evaluate_sentence(virt, var_gnds);
181 
182  if (not match) return false;
183  }
184 
185  // Yay! We found one! We now have a fully and completely grounded
186  // pattern! See what the callback thinks of it.
187  return cb.grounding(var_gnds, term_gnds);
188  }
189  dbgprt("Component recursion: num comp=%zd\n", comp_var_gnds.size());
190 
191  // Recurse over all components. If component k has N_k groundings,
192  // and there are m components, then we have to explore all
193  // N_0 * N_1 * N_2 * ... N_m possible combinations of groundings.
194  // We do this recursively, by poping N_m off the back, and calling
195  // ourselves.
196  //
197  // vg and vp will be the collection of all of the different possible
198  // groundings for one of the components (well, its for component m,
199  // in the above notation.) So the loop below tries every possibility.
200  std::vector<std::map<Handle, Handle>> vg = comp_var_gnds.back();
201  comp_var_gnds.pop_back();
202  std::vector<std::map<Handle, Handle>> pg = comp_term_gnds.back();
203  comp_term_gnds.pop_back();
204 
205  size_t ngnds = vg.size();
206  for (size_t i=0; i<ngnds; i++)
207  {
208  // Given a set of groundings, tack on those for this component,
209  // and recurse, with one less component. We need to make a copy,
210  // of course.
211  std::map<Handle, Handle> rvg(var_gnds);
212  std::map<Handle, Handle> rpg(term_gnds);
213 
214  const std::map<Handle, Handle>& cand_vg(vg[i]);
215  const std::map<Handle, Handle>& cand_pg(pg[i]);
216  rvg.insert(cand_vg.begin(), cand_vg.end());
217  rpg.insert(cand_pg.begin(), cand_pg.end());
218 
219  bool accept = recursive_virtual(cb, virtuals, negations, rvg, rpg,
220  comp_var_gnds, comp_term_gnds);
221 
222  // Halt recursion immediately if match is accepted.
223  if (accept) return true;
224  }
225  return false;
226 }
227 
228 /* ================================================================= */
293 /* ================================================================= */
294 /* ================================================================= */
315 bool BindLink::imply(PatternMatchCallback& pmc, bool check_conn)
316 {
317  if (check_conn and 0 == _virtual.size() and 1 < _components.size())
318  throw InvalidParamException(TRACE_INFO,
319  "BindLink consists of multiple disconnected components!");
320  // PatternLink::check_connectivity(_comps);
321 
322  return PatternLink::satisfy(pmc);
323 }
324 
325 /* ================================================================= */
326 
328 {
329  // If there is just one connected component, we don't have to
330  // do anything special to find a grounding for it. Proceed
331  // in a direct fashion.
332  if (1 == _num_comps)
333  {
334  PatternMatchEngine pme(pmcb, _varlist, _pat);
335 
336 #ifdef DEBUG
337  debug_print();
338 #endif
339  pmcb.set_pattern(_varlist, _pat);
340  bool found = pmcb.initiate_search(&pme);
341 
342 #ifdef DEBUG
343  printf("==================== Done with Search ==================\n");
344 #endif
345  return found;
346  }
347 
348  // If we are here, then we've got a knot in the center of it all.
349  // Removing the virtual clauses from the hypergraph typically causes
350  // the hypergraph to fall apart into multiple components, (i.e. none
351  // are connected to one another). The virtual clauses tie all of
352  // these back together into a single connected graph.
353  //
354  // There are several solution strategies possible at this point.
355  // The one that we will pursue, for now, is to first ground all of
356  // the distinct components individually, and then run each possible
357  // grounding combination through the virtual link, for the final
358  // accept/reject determination.
359 
360 #ifdef DEBUG
361  printf("VIRTUAL PATTERN: ====================== "
362  "num comp=%zd num virts=%zd\n", _num_comps, _num_virts);
363  printf("Virtuals are:\n");
364  size_t iii=0;
365  for (const Handle& v : _virtual)
366  {
367  printf("Virtual clause %zu of %zu:\n%s\n", iii, _num_virts,
368  v->toShortString().c_str());
369  iii++;
370  }
371 #endif
372 
373  std::vector<std::vector<std::map<Handle, Handle>>> comp_term_gnds;
374  std::vector<std::vector<std::map<Handle, Handle>>> comp_var_gnds;
375 
376  for (size_t i=0; i<_num_comps; i++)
377  {
378  dbgprt("BEGIN COMPONENT GROUNDING %zu of %zu: ======================\n",
379  i + 1, _num_comps);
380  // Pass through the callbacks, collect up answers.
381  PMCGroundings gcb(pmcb);
383  clp->satisfy(gcb);
384 
385  comp_var_gnds.push_back(gcb._var_groundings);
386  comp_term_gnds.push_back(gcb._term_groundings);
387  }
388 
389  // And now, try grounding each of the virtual clauses.
390  dbgprt("BEGIN component recursion: ====================== "
391  "num comp=%zd num virts=%zd\n",
392  comp_var_gnds.size(), _virtual.size());
393  std::map<Handle, Handle> empty_vg;
394  std::map<Handle, Handle> empty_pg;
395  std::vector<Handle> optionals; // currently ignored
396  return PatternMatch::recursive_virtual(pmcb, _virtual, optionals,
397  empty_vg, empty_pg,
398  comp_var_gnds, comp_term_gnds);
399 }
400 
401 /* ===================== END OF FILE ===================== */
402 
virtual bool evaluate_sentence(const Handle &eval, const std::map< Handle, Handle > &gnds)=0
void set_pattern(const Variables &vars, const Pattern &pat)
Definition: PatternMatch.cc:90
bool evaluate_sentence(const Handle &link_h, const std::map< Handle, Handle > &gnds)
Definition: PatternMatch.cc:74
PatternMatchCallback & _cb
Definition: PatternMatch.cc:53
bool link_match(const LinkPtr &link1, const LinkPtr &link2)
Definition: PatternMatch.cc:65
bool grounding(const std::map< Handle, Handle > &var_soln, const std::map< Handle, Handle > &term_soln)
bool variable_match(const Handle &node1, const Handle &node2)
Definition: PatternMatch.cc:62
virtual bool initiate_search(PatternMatchEngine *)=0
std::vector< std::map< Handle, Handle > > _var_groundings
bool clause_match(const Handle &pattrn_link_h, const Handle &grnd_link_h)
Definition: PatternMatch.cc:79
static void print_solution(const std::map< Handle, Handle > &vars, const std::map< Handle, Handle > &clauses)
std::vector< std::map< Handle, Handle > > _term_groundings
PMCGroundings(PatternMatchCallback &cb)
Definition: PatternMatch.cc:56
virtual void set_pattern(const Variables &vars, const Pattern &pat)=0
bool node_match(const Handle &node1, const Handle &node2)
Definition: PatternMatch.cc:59
bool fuzzy_match(const Handle &h1, const Handle &h2)
Definition: PatternMatch.cc:71
std::shared_ptr< Link > LinkPtr
Definition: Atom.h:53
static bool recursive_virtual(PatternMatchCallback &cb, const std::vector< Handle > &virtuals, const std::vector< Handle > &negations, const std::map< Handle, Handle > &var_gnds, const std::map< Handle, Handle > &term_gnds, std::vector< std::vector< std::map< Handle, Handle >>> comp_var_gnds, std::vector< std::vector< std::map< Handle, Handle >>> comp_term_gnds)
bool post_link_match(const LinkPtr &link1, const LinkPtr &link2)
Definition: PatternMatch.cc:68
static PatternLinkPtr PatternLinkCast(const Handle &h)
Definition: PatternLink.h:182
std::vector< LinkPtr > IncomingSet
Definition: Atom.h:55
#define dbgprt(f, varargs...)
Definition: PatternMatch.cc:43
virtual bool grounding(const std::map< Handle, Handle > &var_soln, const std::map< Handle, Handle > &term_soln)=0
IncomingSet get_incoming_set(const Handle &h)
Definition: PatternMatch.cc:85
std::shared_ptr< PatternLink > PatternLinkPtr
Definition: PatternLink.h:181
bool initiate_search(PatternMatchEngine *pme)
Definition: PatternMatch.cc:96
bool optional_clause_match(const Handle &pattrn, const Handle &grnd)
Definition: PatternMatch.cc:82
void pop(void)
Definition: PatternMatch.cc:89
void push(void)
Definition: PatternMatch.cc:88