OpenCog Framework  Branch: master, revision 6f0b7fc776b08468cf1b74aa9db028f387b4f0c0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
DefaultPatternMatchCB.cc
Go to the documentation of this file.
1 /*
2  * DefaultPatternMatchCB.cc
3  *
4  * Copyright (C) 2008,2009,2014,2015 Linas Vepstas
5  *
6  * Author: Linas Vepstas <linasvepstas@gmail.com> February 2008
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 
27 
28 #include "DefaultPatternMatchCB.h"
29 
30 using namespace opencog;
31 
32 // Uncomment below to enable debug print
33 // #define DEBUG
34 #ifdef DEBUG
35 #define dbgprt(f, varargs...) printf(f, ##varargs)
36 #else
37 #define dbgprt(f, varargs...)
38 #endif
39 
40 /* ======================================================== */
41 
43  _classserver(classserver()),
44  _temp_aspace(as),
45  _instor(&_temp_aspace),
46  _as(as)
47 {
48  _connectives.insert(SEQUENTIAL_AND_LINK);
49  _connectives.insert(AND_LINK);
50  _connectives.insert(OR_LINK);
51  _connectives.insert(NOT_LINK);
52 }
53 
55  const Pattern& pat)
56 {
59  _have_evaluatables = (0 < _dynamic->size());
60 }
61 
62 
63 /* ======================================================== */
64 
76  const Handle& nsoln_h)
77 {
78  // If equality, then a match.
79  return npat_h == nsoln_h;
80 }
81 
91  const Handle& nsoln_h)
92 {
93  Type pattype = npat_h->getType();
94 
95  // If the ungrounded term is not of type VariableNode, then just
96  // accept the match. This allows any kind of node types to be
97  // explicitly bound as variables. However, the type VariableNode
98  // gets special handling, below.
99  if (pattype != VARIABLE_NODE) return true;
100 
101  // If the ungrounded term is a variable, then see if there
102  // are any restrictions on the variable type.
103  // If no restrictions, we are good to go.
104  if (_type_restrictions->empty()) return true;
105 
106  // If we are here, there's a restriction on the grounding type.
107  // Validate the node type, if needed.
108  VariableTypeMap::const_iterator it = _type_restrictions->find(npat_h);
109  if (it == _type_restrictions->end()) return true;
110 
111  // Is the ground-atom type in our list of allowed types?
112  Type soltype = nsoln_h->getType();
113  const std::set<Type> &tset = it->second;
114  std::set<Type>::const_iterator allow = tset.find(soltype);
115  return allow != tset.end();
116 }
117 
131  const LinkPtr& lsoln)
132 {
133  // If the pattern is exactly the same link as the proposed
134  // grounding, then its a perfect match.
135  if (lpat == lsoln) return true;
136 
137  // Accept all ChoiceLink's by default! We will get another shot
138  // at it when the contents of the ChoiceLink are examined.
139  Type pattype = lpat->getType();
140  if (CHOICE_LINK == pattype) return true;
141 
142  if (lpat->getArity() != lsoln->getArity()) return false;
143  Type soltype = lsoln->getType();
144 
145  // If types differ, no match
146  return pattype == soltype;
147 }
148 
150  const LinkPtr& lgnd)
151 {
152  if (not _have_evaluatables) return true;
153  Handle hp(lpat);
154  if (_dynamic->find(hp) == _dynamic->end()) return true;
155 
156  // We will find ourselves here whenever the link contain
157  // a GroundedPredicateNode. In this case, execute the
158  // node, and declare a match, or no match, depending
159  // one how the evaluation turned out. Its "crisp logic"
160  // because we use a greater-than-half for the TV.
161  // This is the same behavior as used in evaluate_term().
162  TruthValuePtr tv(EvaluationLink::do_evaluate(_as, lgnd->getHandle()));
163  return tv->getMean() >= 0.5;
164 }
165 
180  const Handle& grnd)
181 {
182  // Is the pattern same as the ground?
183  // if (ptrn == grnd) return false;
184  // Well, in a "normal" world, it intuitively makes sense to reject
185  // clauses that are grounded by themselves. In the real world, this
186  // runs afoul of several unusual situations. The one we care about
187  // is an evaluatable clause which contains no variables. In this
188  // case, we need to accept the match in order for a SatisfactionLink
189  // to get valued correctly. Tested in SequenceUTest.
190  // if (ptrn == grnd) return false;
191 
192  // This if-statement handles the case given in the callback description.
193  // It is tested by EvaluationUTest.
194  if (ptrn->getType() == VARIABLE_NODE and
195  grnd->getType() == EVALUATION_LINK and
196  0 < LinkCast(grnd)->getArity() and
197  LinkCast(grnd)->getOutgoingAtom(0)->getType() ==
198  GROUNDED_PREDICATE_NODE)
199  {
200  dbgprt("Evaluate the grounding clause=\n%s\n",
201  grnd->toShortString().c_str());
202  // We make two awkard asumptions here: the ground term itself
203  // does not contain any variables, and so does not need any
204  // further grounding. This actuall seems reasonable. The second
205  // assumption is that the EvaluationLink is actually evaluatable,
206  // which seems reasonable, except that everything else in the
207  // default callback ignores the TV on EvaluationLinks. So this
208  // is kind-of schizophrenic here. Not sure what else to do.
211 
212  dbgprt("clause_match evaluation yeilded tv=%s\n", tvp->toString().c_str());
213 
214  // XXX FIXME: we are making a crisp-logic go/no-go decision
215  // based on the TV strength. Perhaps something more subtle might be
216  // wanted, here.
217  bool relation_holds = tvp->getMean() > 0.5;
218  return relation_holds;
219  }
220  return true;
221 }
222 
230  const Handle& grnd)
231 {
232  if (Handle::UNDEFINED == grnd) return true;
233  _optionals_present = true;
234  return false;
235 }
236 
237 /* ======================================================== */
238 
240  const std::map<Handle, Handle>& gnds)
241 {
242  // Evaluation of the link requires working with an atomspace
243  // of some sort, so that the atoms can be communicated to scheme or
244  // python for the actual evaluation. We don't want to put the
245  // proposed grounding into the "real" atomspace, because the
246  // grounding might be insane. So we put it here. This is probably
247  // not very efficient, but will do for now...
248 
249  Handle gvirt(_instor.instantiate(virt, gnds));
250 
251  dbgprt("Enter eval_term CB with virt=\n%s\n",
252  virt->toShortString().c_str());
253  dbgprt("grounded by gvirt=\n%s\n",
254  gvirt->toShortString().c_str());
255 
256 
257  // At this time, we expect all virutal links to be in one of two
258  // forms: either EvaluationLink's or GreaterThanLink's. The
259  // EvaluationLinks should have the structure
260  //
261  // EvaluationLink
262  // GroundedPredicateNode "scm:blah"
263  // ListLink
264  // Arg1Atom
265  // Arg2Atom
266  //
267  // The GreaterThanLink's should have the "obvious" structure
268  //
269  // GreaterThanLink
270  // Arg1Atom
271  // Arg2Atom
272  //
273  // XXX TODO as discussed on the mailing list, we should perhaps first
274  // see if the following can be found in the atomspace:
275  //
276  // EvaluationLink
277  // PredicateNode "blah" ; not Grounded any more, and scm: stripped
278  // ListLink
279  // Arg1Atom
280  // Arg2Atom
281  //
282  // If it does, we should declare a match. If not, only then run the
283  // do_evaluate callback. Alternately, perhaps the
284  // EvaluationLink::do_evaluate() method should do this ??? Its a toss-up.
285 
288 
289  dbgprt("eval_term evaluation yeilded tv=%s\n", tvp->toString().c_str());
290 
291  // XXX FIXME: we are making a crsip-logic go/no-go decision
292  // based on the TV strength. Perhaps something more subtle might be
293  // wanted, here.
294  bool relation_holds = tvp->getMean() > 0.5;
295  return relation_holds;
296 }
297 
298 /* ======================================================== */
299 
308  const std::map<Handle, Handle>& gnds)
309 {
310  dbgprt("Enter eval_sentence CB with top=\n%s\n",
311  top->toShortString().c_str());
312 
313  if (top->getType() == VARIABLE_NODE)
314  {
315  return eval_term(top, gnds);
316  }
317 
318  LinkPtr ltop(LinkCast(top));
319  if (NULL == ltop)
320  throw InvalidParamException(TRACE_INFO,
321  "Not expecting a Node, here %s\n",
322  top->toShortString().c_str());
323 
324  const HandleSeq& oset = ltop->getOutgoingSet();
325  if (0 == oset.size())
326  throw InvalidParamException(TRACE_INFO,
327  "Expecting logical connective to have at least one child!");
328 
329  Type term_type = top->getType();
330  if (OR_LINK == term_type)
331  {
332  for (const Handle& h : oset)
333  if (eval_sentence(h, gnds)) return true;
334 
335  return false;
336  }
337  else if (AND_LINK == term_type or SEQUENTIAL_AND_LINK == term_type)
338  {
339  for (const Handle& h : oset)
340  if (not eval_sentence(h, gnds)) return false;
341 
342  return true;
343  }
344  else if (NOT_LINK == term_type)
345  {
346  if (1 != oset.size())
347  throw InvalidParamException(TRACE_INFO,
348  "NotLink can have only one child!");
349 
350  return not eval_sentence(oset[0], gnds);
351  }
352  else if (EVALUATION_LINK == term_type or
353  _classserver.isA(term_type, VIRTUAL_LINK))
354  {
355  return eval_term(top, gnds);
356  }
357  throw InvalidParamException(TRACE_INFO,
358  "Unknown logical connective %s\n",
359  top->toShortString().c_str());
360 }
361 
362 /* ===================== END OF FILE ===================== */
virtual void set_pattern(const Variables &, const Pattern &)
virtual bool variable_match(const Handle &, const Handle &)
std::vector< Handle > HandleSeq
a list of handles
Definition: Handle.h:246
std::shared_ptr< TruthValue > TruthValuePtr
Definition: TruthValue.h:85
std::set< Handle > evaluatable_terms
Definition: Pattern.h:120
virtual std::string toShortString(std::string indent="")=0
Type getType() const
Definition: Atom.h:197
#define dbgprt(f, varargs...)
std::shared_ptr< Link > LinkPtr
Definition: Atom.h:53
ClassServer & classserver(ClassServerFactory *=ClassServer::createInstance)
Definition: ClassServer.cc:159
virtual bool post_link_match(const LinkPtr &, const LinkPtr &)
static const Handle UNDEFINED
Definition: Handle.h:77
Handle instantiate(const Handle &expr, const std::map< Handle, Handle > &vars)
bool isA(Type sub, Type super)
Definition: ClassServer.h:144
virtual bool link_match(const LinkPtr &, const LinkPtr &)
static LinkPtr LinkCast(const Handle &h)
Definition: Link.h:263
bool eval_term(const Handle &pat, const std::map< Handle, Handle > &gnds)
virtual bool clause_match(const Handle &, const Handle &)
const std::set< Handle > * _dynamic
const VariableTypeMap * _type_restrictions
unsigned short Type
type of Atoms, represented as short integer (16 bits)
Definition: types.h:40
VariableTypeMap typemap
Definition: Pattern.h:66
void clear()
Clear the atomspace, remove all atoms.
Definition: AtomSpace.cc:355
virtual bool node_match(const Handle &, const Handle &)
bool eval_sentence(const Handle &pat, const std::map< Handle, Handle > &gnds)
virtual bool optional_clause_match(const Handle &pattrn, const Handle &grnd)