From 8f885085c3af37976fe1115867141aa2dbf260c6 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sat, 26 Jan 2019 13:38:37 -0600 Subject: [PATCH 01/27] Add StrengthOf, ConfidenceOf link types. Per discussion in #2004 this should help PLN. --- opencog/atoms/atom_types/atom_types.script | 6 +- opencog/atoms/core/TruthValueOfLink.cc | 86 ++++++++++++++++++++++ opencog/atoms/core/TruthValueOfLink.h | 52 ++++++++++++- 3 files changed, 142 insertions(+), 2 deletions(-) diff --git a/opencog/atoms/atom_types/atom_types.script b/opencog/atoms/atom_types/atom_types.script index f4c8b1d9f6..154ffddf15 100644 --- a/opencog/atoms/atom_types/atom_types.script +++ b/opencog/atoms/atom_types/atom_types.script @@ -571,9 +571,13 @@ FUNCTION_LINK <- FREE_LINK EXECUTION_OUTPUT_LINK <- FUNCTION_LINK // Return the indicated value on the indicated atom. -// XXX It doesn't *always* return numbers ... +// XXX It's marked as "NUMERIC_OUTPUT, but doesn't *always* return +// numbers ... Probably should have a FLOAT_VALUE_OF_LINK to fix this. +// Ignore this issue for now ... VALUE_OF_LINK <- FUNCTION_LINK,NUMERIC_OUTPUT_LINK TRUTH_VALUE_OF_LINK <- VALUE_OF_LINK +STRENGTH_OF_LINK <- VALUE_OF_LINK +CONFIDENCE_OF_LINK <- VALUE_OF_LINK // Basic arithmetic operators FOLD_LINK <- FUNCTION_LINK diff --git a/opencog/atoms/core/TruthValueOfLink.cc b/opencog/atoms/core/TruthValueOfLink.cc index a847d4715d..e640b48604 100644 --- a/opencog/atoms/core/TruthValueOfLink.cc +++ b/opencog/atoms/core/TruthValueOfLink.cc @@ -61,6 +61,92 @@ ValuePtr TruthValueOfLink::execute() const return ValueCast(_outgoing[0]->getTruthValue()); } +// ============================================================= + +StrengthOfLink::StrengthOfLink(const HandleSeq& oset, Type t) + : ValueOfLink(oset, t) +{ + if (not nameserver().isA(t, STRENGTH_OF_LINK)) + { + const std::string& tname = nameserver().getTypeName(t); + throw InvalidParamException(TRACE_INFO, + "Expecting an StrengthOfLink, got %s", tname.c_str()); + } +} + +StrengthOfLink::StrengthOfLink(const Link &l) + : ValueOfLink(l) +{ + // Type must be as expected + Type tscope = l.get_type(); + if (not nameserver().isA(tscope, STRENGTH_OF_LINK)) + { + const std::string& tname = nameserver().getTypeName(tscope); + throw InvalidParamException(TRACE_INFO, + "Expecting an StrengthOfLink, got %s", tname.c_str()); + } +} + +// --------------------------------------------------------------- + +/// When executed, this will return the Strengths of all of the +/// atoms in the outgoing set. +ValuePtr StrengthOfLink::execute() const +{ + std::vector strengths; + + for (const Handle& h : _outgoing) + { + strengths.push_back(h->getTruthValue()->get_mean()); + } + + return createFloatValue(strengths); +} + +// ============================================================= + +ConfidenceOfLink::ConfidenceOfLink(const HandleSeq& oset, Type t) + : ValueOfLink(oset, t) +{ + if (not nameserver().isA(t, CONFIDENCE_OF_LINK)) + { + const std::string& tname = nameserver().getTypeName(t); + throw InvalidParamException(TRACE_INFO, + "Expecting an ConfidenceOfLink, got %s", tname.c_str()); + } +} + +ConfidenceOfLink::ConfidenceOfLink(const Link &l) + : ValueOfLink(l) +{ + // Type must be as expected + Type tscope = l.get_type(); + if (not nameserver().isA(tscope, CONFIDENCE_OF_LINK)) + { + const std::string& tname = nameserver().getTypeName(tscope); + throw InvalidParamException(TRACE_INFO, + "Expecting an ConfidenceOfLink, got %s", tname.c_str()); + } +} + +// --------------------------------------------------------------- + +/// When executed, this will return the Confidences of all of the +/// atoms in the outgoing set. +ValuePtr ConfidenceOfLink::execute() const +{ + std::vector confids; + + for (const Handle& h : _outgoing) + { + confids.push_back(h->getTruthValue()->get_confidence()); + } + + return createFloatValue(confids); +} + DEFINE_LINK_FACTORY(TruthValueOfLink, TRUTH_VALUE_OF_LINK) +// DEFINE_LINK_FACTORY(StrengthOfLink, STRENGTH_OF_LINK) +// DEFINE_LINK_FACTORY(ConfidenceOfLink, CONFIDENCE_OF_LINK) /* ===================== END OF FILE ===================== */ diff --git a/opencog/atoms/core/TruthValueOfLink.h b/opencog/atoms/core/TruthValueOfLink.h index 1bb9d8ac88..77627ea2c8 100644 --- a/opencog/atoms/core/TruthValueOfLink.h +++ b/opencog/atoms/core/TruthValueOfLink.h @@ -39,7 +39,7 @@ class TruthValueOfLink : public ValueOfLink TruthValueOfLink(const HandleSeq&, Type=TRUTH_VALUE_OF_LINK); TruthValueOfLink(const Link &l); - // Return a pointer to the atom being specified. + // Return a pointer to the extracted value. virtual ValuePtr execute() const; static Handle factory(const Handle&); @@ -53,6 +53,56 @@ static inline TruthValueOfLinkPtr TruthValueOfLinkCast(AtomPtr a) #define createTruthValueOfLink std::make_shared +// ==================================================================== + +/// The StrengthOfLink returns the strength of a truth value on the +/// indicated atom. (Strength is the first of the sequence of floats). +/// +class StrengthOfLink : public ValueOfLink +{ +public: + StrengthOfLink(const HandleSeq&, Type=STRENGTH_OF_LINK); + StrengthOfLink(const Link &l); + + // Return a pointer to the extracted value. + virtual ValuePtr execute() const; + + static Handle factory(const Handle&); +}; + +typedef std::shared_ptr StrengthOfLinkPtr; +static inline StrengthOfLinkPtr StrengthOfLinkCast(const Handle& h) + { return std::dynamic_pointer_cast(h); } +static inline StrengthOfLinkPtr StrengthOfLinkCast(AtomPtr a) + { return std::dynamic_pointer_cast(a); } + +#define createStrengthOfLink std::make_shared + +// ==================================================================== + +/// The ConfidenceOfLink returns the strength of a truth value on the +/// indicated atom. (Confidence is the first of the sequence of floats). +/// +class ConfidenceOfLink : public ValueOfLink +{ +public: + ConfidenceOfLink(const HandleSeq&, Type=CONFIDENCE_OF_LINK); + ConfidenceOfLink(const Link &l); + + // Return a pointer to the extracted value. + virtual ValuePtr execute() const; + + static Handle factory(const Handle&); +}; + +typedef std::shared_ptr ConfidenceOfLinkPtr; +static inline ConfidenceOfLinkPtr ConfidenceOfLinkCast(const Handle& h) + { return std::dynamic_pointer_cast(h); } +static inline ConfidenceOfLinkPtr ConfidenceOfLinkCast(AtomPtr a) + { return std::dynamic_pointer_cast(a); } + +#define createConfidenceOfLink std::make_shared + /** @}*/ } From 5e22c734ebdcf94df085e72ce5e9ce60bb65a781 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sat, 26 Jan 2019 13:50:35 -0600 Subject: [PATCH 02/27] Enable multiple library constructors --- opencog/atoms/base/ClassServer.h | 9 +++++++-- opencog/atoms/core/TruthValueOfLink.cc | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/opencog/atoms/base/ClassServer.h b/opencog/atoms/base/ClassServer.h index cad99be1e4..6362a68b87 100644 --- a/opencog/atoms/base/ClassServer.h +++ b/opencog/atoms/base/ClassServer.h @@ -102,6 +102,9 @@ class ClassServer ClassServer& classserver(); +#define TOKENPASTE(x, y) x ## y +#define TOKENPASTE2(x, y) TOKENPASTE(x, y) + #define DEFINE_LINK_FACTORY(CNAME,CTYPE) \ \ Handle CNAME::factory(const Handle& base) \ @@ -123,7 +126,8 @@ Handle CNAME::factory(const Handle& base) \ } \ \ /* This runs when the shared lib is loaded. */ \ -static __attribute__ ((constructor)) void init(void) \ +static __attribute__ ((constructor)) void \ + TOKENPASTE2(init, __COUNTER__)(void) \ { \ classserver().addFactory(CTYPE, &CNAME::factory); \ } @@ -138,7 +142,8 @@ Handle CNAME::factory(const Handle& base) \ } \ \ /* This runs when the shared lib is loaded. */ \ -static __attribute__ ((constructor)) void init(void) \ +static __attribute__ ((constructor)) void \ + TOKENPASTE2(init, __COUNTER__)(void) \ { \ classserver().addFactory(CTYPE, &CNAME::factory); \ } diff --git a/opencog/atoms/core/TruthValueOfLink.cc b/opencog/atoms/core/TruthValueOfLink.cc index e640b48604..f48ceb6400 100644 --- a/opencog/atoms/core/TruthValueOfLink.cc +++ b/opencog/atoms/core/TruthValueOfLink.cc @@ -146,7 +146,7 @@ ValuePtr ConfidenceOfLink::execute() const } DEFINE_LINK_FACTORY(TruthValueOfLink, TRUTH_VALUE_OF_LINK) -// DEFINE_LINK_FACTORY(StrengthOfLink, STRENGTH_OF_LINK) -// DEFINE_LINK_FACTORY(ConfidenceOfLink, CONFIDENCE_OF_LINK) +DEFINE_LINK_FACTORY(StrengthOfLink, STRENGTH_OF_LINK) +DEFINE_LINK_FACTORY(ConfidenceOfLink, CONFIDENCE_OF_LINK) /* ===================== END OF FILE ===================== */ From 3af45f6914eb71be4b77b0d4204d75c84077b2d5 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sat, 26 Jan 2019 15:19:22 -0600 Subject: [PATCH 03/27] Convert FloatValues into TV's --- examples/atomspace/formulas.scm | 31 ++++++++++++++++++++++ opencog/atoms/atom_types/atom_types.script | 7 +++++ opencog/atoms/execution/EvaluationLink.cc | 14 ++++++++++ 3 files changed, 52 insertions(+) create mode 100644 examples/atomspace/formulas.scm diff --git a/examples/atomspace/formulas.scm b/examples/atomspace/formulas.scm new file mode 100644 index 0000000000..6e05a27f7e --- /dev/null +++ b/examples/atomspace/formulas.scm @@ -0,0 +1,31 @@ +; +; formulas.scm -- Declaring formulas that compute truth values. +; +(use-modules (opencog) (opencog exec)) + +; Declare a very simplified example of a truth-value formula used by +; PLN. This merely multiplies a pair of strengths. + +(cog-execute! (StrengthOf (Concept "A" (stv 0.8 1.0)))) + +(Concept "B" (stv 0.6 1.0)) + +(cog-execute! + (TimesLink (StrengthOf (Concept "A")) (StrengthOf (Concept "B")))) + +(cog-evaluate! + (PredicateFormulaLink + (TimesLink (StrengthOf (Concept "A")) (StrengthOf (Concept "B"))) + (TimesLink (ConfidenceOf (Concept "A")) (ConfidenceOf (Concept "B"))))) + +(EvaluationLink + (PredicateFormulaLink + (TimesLink + (StrengthOf (Concept "A")) + (StrengthOf (Concept "B"))) + (List + (Concept "A") + (Concept "B"))) + +(EvaluationLink + (TimesLink (StrengthOf (Variable "$A") (Variable "$B"))) diff --git a/opencog/atoms/atom_types/atom_types.script b/opencog/atoms/atom_types/atom_types.script index 154ffddf15..0b84158a0e 100644 --- a/opencog/atoms/atom_types/atom_types.script +++ b/opencog/atoms/atom_types/atom_types.script @@ -579,6 +579,13 @@ TRUTH_VALUE_OF_LINK <- VALUE_OF_LINK STRENGTH_OF_LINK <- VALUE_OF_LINK CONFIDENCE_OF_LINK <- VALUE_OF_LINK +// The opposite of the above: given something that evaluates to a +// FloatValue, return a TruthValue. Kind-of-like +// GROUNDED_PREDICATE_NODE, but holding the forumla in the atomspace. +// This is not really needed, but will make the transition process +// smoother. +PREDICATE_FORMULA_LINK <- EVALUATABLE_LINK + // Basic arithmetic operators FOLD_LINK <- FUNCTION_LINK ARITHMETIC_LINK <- FOLD_LINK,NUMERIC_LINK,NUMERIC_OUTPUT_LINK diff --git a/opencog/atoms/execution/EvaluationLink.cc b/opencog/atoms/execution/EvaluationLink.cc index 722032a497..5388c5daf7 100644 --- a/opencog/atoms/execution/EvaluationLink.cc +++ b/opencog/atoms/execution/EvaluationLink.cc @@ -619,6 +619,20 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, return do_evaluate(as, reduct, silent); } + if (PREDICATE_FORMULA_LINK == pntype) + { + std::vector nums; + for (const Handle& h: pn->getOutgoingSet()) + { + if (not nameserver().isA(h->get_type(), FUNCTION_LINK)) + throw NotEvaluatableException(); + ValuePtr v(FunctionLinkCast(h)->execute()); + FloatValuePtr fv(FloatValueCast(v)); + nums.push_back(fv->value()[0]); + } + return createSimpleTruthValue(nums); + } + if (GROUNDED_PREDICATE_NODE != pntype) { // Throw a silent exception; this is called in some try..catch blocks. From db3ceee25aac7a53ae09a329232b5c9439472da2 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sat, 26 Jan 2019 15:35:57 -0600 Subject: [PATCH 04/27] Also evaluate outside of the EvluationLink --- opencog/atoms/execution/EvaluationLink.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/opencog/atoms/execution/EvaluationLink.cc b/opencog/atoms/execution/EvaluationLink.cc index 5388c5daf7..af920721fd 100644 --- a/opencog/atoms/execution/EvaluationLink.cc +++ b/opencog/atoms/execution/EvaluationLink.cc @@ -505,6 +505,19 @@ TruthValuePtr EvaluationLink::do_eval_scratch(AtomSpace* as, { return evelnk->getTruthValue(); } + else if (PREDICATE_FORMULA_LINK == t) + { + std::vector nums; + for (const Handle& h: evelnk->getOutgoingSet()) + { + if (not nameserver().isA(h->get_type(), FUNCTION_LINK)) + throw NotEvaluatableException(); + ValuePtr v(FunctionLinkCast(h)->execute()); + FloatValuePtr fv(FloatValueCast(v)); + nums.push_back(fv->value()[0]); + } + return createSimpleTruthValue(nums); + } else if (TRUTH_VALUE_OF_LINK == t) { // If the truth value of the link is being requested, From 3ec2042f23919456768889ca0806e39396a55bad Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sat, 26 Jan 2019 15:49:45 -0600 Subject: [PATCH 05/27] Allow numbers as well. --- examples/atomspace/formulas.scm | 19 ++++++++++--------- opencog/atoms/execution/EvaluationLink.cc | 10 ++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/examples/atomspace/formulas.scm b/examples/atomspace/formulas.scm index 6e05a27f7e..f71a60cf42 100644 --- a/examples/atomspace/formulas.scm +++ b/examples/atomspace/formulas.scm @@ -8,7 +8,7 @@ (cog-execute! (StrengthOf (Concept "A" (stv 0.8 1.0)))) -(Concept "B" (stv 0.6 1.0)) +(Concept "B" (stv 0.6 0.9)) (cog-execute! (TimesLink (StrengthOf (Concept "A")) (StrengthOf (Concept "B")))) @@ -18,14 +18,15 @@ (TimesLink (StrengthOf (Concept "A")) (StrengthOf (Concept "B"))) (TimesLink (ConfidenceOf (Concept "A")) (ConfidenceOf (Concept "B"))))) -(EvaluationLink - (PredicateFormulaLink - (TimesLink - (StrengthOf (Concept "A")) - (StrengthOf (Concept "B"))) - (List - (Concept "A") - (Concept "B"))) +(cog-evaluate! + (PredicateFormulaLink (Number 0.7) (Number 0.314)) + +(cog-evaluate! + (EvaluationLink + (PredicateFormulaLink (Number 0.7) (Number 0.314)) + (List + (Concept "A") + (Concept "B")))) (EvaluationLink (TimesLink (StrengthOf (Variable "$A") (Variable "$B"))) diff --git a/opencog/atoms/execution/EvaluationLink.cc b/opencog/atoms/execution/EvaluationLink.cc index af920721fd..5ad7ab5f0b 100644 --- a/opencog/atoms/execution/EvaluationLink.cc +++ b/opencog/atoms/execution/EvaluationLink.cc @@ -510,6 +510,11 @@ TruthValuePtr EvaluationLink::do_eval_scratch(AtomSpace* as, std::vector nums; for (const Handle& h: evelnk->getOutgoingSet()) { + if (NUMBER_NODE == h->get_type()) + { + nums.push_back(NumberNodeCast(h)->get_value()); + continue; + } if (not nameserver().isA(h->get_type(), FUNCTION_LINK)) throw NotEvaluatableException(); ValuePtr v(FunctionLinkCast(h)->execute()); @@ -637,6 +642,11 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, std::vector nums; for (const Handle& h: pn->getOutgoingSet()) { + if (NUMBER_NODE == h->get_type()) + { + nums.push_back(NumberNodeCast(h)->get_value()); + continue; + } if (not nameserver().isA(h->get_type(), FUNCTION_LINK)) throw NotEvaluatableException(); ValuePtr v(FunctionLinkCast(h)->execute()); From b984b1a7710c869015694881ecd9362a25f66fc5 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sat, 26 Jan 2019 16:26:18 -0600 Subject: [PATCH 06/27] Allow Lambda for the forumlas. --- examples/atomspace/formulas.scm | 21 ++++++++++--- opencog/atoms/execution/EvaluationLink.cc | 38 ++++++++++++++++++++--- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/examples/atomspace/formulas.scm b/examples/atomspace/formulas.scm index f71a60cf42..e220eb1f36 100644 --- a/examples/atomspace/formulas.scm +++ b/examples/atomspace/formulas.scm @@ -10,7 +10,7 @@ (Concept "B" (stv 0.6 0.9)) -(cog-execute! +(cog-execute! (TimesLink (StrengthOf (Concept "A")) (StrengthOf (Concept "B")))) (cog-evaluate! @@ -19,7 +19,7 @@ (TimesLink (ConfidenceOf (Concept "A")) (ConfidenceOf (Concept "B"))))) (cog-evaluate! - (PredicateFormulaLink (Number 0.7) (Number 0.314)) + (PredicateFormulaLink (Number 0.7) (Number 0.314))) (cog-evaluate! (EvaluationLink @@ -28,5 +28,18 @@ (Concept "A") (Concept "B")))) -(EvaluationLink - (TimesLink (StrengthOf (Variable "$A") (Variable "$B"))) +(cog-evaluate! + (EvaluationLink + ; Compute TV = (1-sA*sB, cA*cB) + (PredicateFormulaLink + (Lambda (MinusLink + (Number 1) + (TimesLink + (StrengthOf (Variable "$X")) + (StrengthOf (Variable "$Y"))))) + (Lambda (TimesLink + (ConfidenceOf (Variable "$X")) + (ConfidenceOf (Variable "$Y"))))) + (List + (Concept "A") + (Concept "B")))) diff --git a/opencog/atoms/execution/EvaluationLink.cc b/opencog/atoms/execution/EvaluationLink.cc index 5ad7ab5f0b..4ed66553ce 100644 --- a/opencog/atoms/execution/EvaluationLink.cc +++ b/opencog/atoms/execution/EvaluationLink.cc @@ -516,7 +516,12 @@ TruthValuePtr EvaluationLink::do_eval_scratch(AtomSpace* as, continue; } if (not nameserver().isA(h->get_type(), FUNCTION_LINK)) - throw NotEvaluatableException(); + { + if (silent) + throw NotEvaluatableException(); + throw SyntaxException(TRACE_INFO, + "Expecting a FunctionLink"); + } ValuePtr v(FunctionLinkCast(h)->execute()); FloatValuePtr fv(FloatValueCast(v)); nums.push_back(fv->value()[0]); @@ -637,19 +642,44 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, return do_evaluate(as, reduct, silent); } + // Like a GPN, but the entire function is declared in the + // AtomSpace. if (PREDICATE_FORMULA_LINK == pntype) { + // Collect up two floating point values. std::vector nums; for (const Handle& h: pn->getOutgoingSet()) { + // An ordinary number. if (NUMBER_NODE == h->get_type()) { nums.push_back(NumberNodeCast(h)->get_value()); continue; } - if (not nameserver().isA(h->get_type(), FUNCTION_LINK)) - throw NotEvaluatableException(); - ValuePtr v(FunctionLinkCast(h)->execute()); + + // Something that has variables in it, that need to + // be reduced first, to make sense. + Handle flp(h); + if (LAMBDA_LINK == h->get_type()) + { + LambdaLinkPtr lam(LambdaLinkCast(h)); + Type atype = cargs->get_type(); + + // Set flp and fall through, where it is executed. + flp = lam->beta_reduce(atype == LIST_LINK ? + cargs->getOutgoingSet() + : HandleSeq(1, cargs)); + } + + // Something without variables in it. + if (not nameserver().isA(flp->get_type(), FUNCTION_LINK)) + { + if (silent) + throw NotEvaluatableException(); + throw SyntaxException(TRACE_INFO, + "Expecting a FunctionLink"); + } + ValuePtr v(FunctionLinkCast(flp)->execute()); FloatValuePtr fv(FloatValueCast(v)); nums.push_back(fv->value()[0]); } From 740c42648a64e6583f5f21250b4614b9f34992ab Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sat, 26 Jan 2019 16:44:36 -0600 Subject: [PATCH 07/27] Enable just ordinary formulas. --- examples/atomspace/formulas.scm | 16 ++++++++++ opencog/atoms/execution/EvaluationLink.cc | 36 ++++++++++++++++++----- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/examples/atomspace/formulas.scm b/examples/atomspace/formulas.scm index e220eb1f36..e678c26b78 100644 --- a/examples/atomspace/formulas.scm +++ b/examples/atomspace/formulas.scm @@ -28,6 +28,22 @@ (Concept "A") (Concept "B")))) +(cog-evaluate! + (EvaluationLink + ; Compute TV = (1-sA*sB, cA*cB) + (PredicateFormulaLink + (MinusLink + (Number 1) + (TimesLink + (StrengthOf (Variable "$X")) + (StrengthOf (Variable "$Y")))) + (TimesLink + (ConfidenceOf (Variable "$X")) + (ConfidenceOf (Variable "$Y")))) + (List + (Concept "A") + (Concept "B")))) + (cog-evaluate! (EvaluationLink ; Compute TV = (1-sA*sB, cA*cB) diff --git a/opencog/atoms/execution/EvaluationLink.cc b/opencog/atoms/execution/EvaluationLink.cc index 4ed66553ce..89a14d2566 100644 --- a/opencog/atoms/execution/EvaluationLink.cc +++ b/opencog/atoms/execution/EvaluationLink.cc @@ -657,32 +657,54 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, continue; } - // Something that has variables in it, that need to - // be reduced first, to make sense. - Handle flp(h); + // In case the user wanted to wrap everything in a + // LambdaLink. I don't understand why this is needed, + // but it seems to make some people feel better, so + // we support it. + Handle flh(h); if (LAMBDA_LINK == h->get_type()) { LambdaLinkPtr lam(LambdaLinkCast(h)); Type atype = cargs->get_type(); // Set flp and fall through, where it is executed. - flp = lam->beta_reduce(atype == LIST_LINK ? + flh = lam->beta_reduce(atype == LIST_LINK ? cargs->getOutgoingSet() : HandleSeq(1, cargs)); } - // Something without variables in it. - if (not nameserver().isA(flp->get_type(), FUNCTION_LINK)) + // At this point, we expect a FunctionLink of some kind. + if (not nameserver().isA(flh->get_type(), FUNCTION_LINK)) { if (silent) throw NotEvaluatableException(); throw SyntaxException(TRACE_INFO, "Expecting a FunctionLink"); } - ValuePtr v(FunctionLinkCast(flp)->execute()); + + // If the FunctionLink has free variables in it, + // reduce them with the provided arguments. + FunctionLinkPtr flp(FunctionLinkCast(flh)); + const FreeVariables& fvars = flp->get_vars(); + if (not fvars.empty()) + { + Type atype = cargs->get_type(); + flh = fvars.substitute_nocheck(flh, + atype == LIST_LINK ? + cargs->getOutgoingSet() + : HandleSeq(1, cargs)); + flp = FunctionLinkCast(flh); + } + + // Expecting a FunctionLink without variables. + ValuePtr v(flp->execute()); FloatValuePtr fv(FloatValueCast(v)); nums.push_back(fv->value()[0]); } + + // XXX FIXME; if we are given more than two floats, then + // perhaps we should create some other kind of TruthValue? + // Maybe a distributional one ?? Or a CountTV ?? return createSimpleTruthValue(nums); } From 1edeef7bc014b1fc7695a8e1eefd18cb322f3698 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sat, 26 Jan 2019 16:59:05 -0600 Subject: [PATCH 08/27] Add documentation --- examples/atomspace/formulas.scm | 64 ++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/examples/atomspace/formulas.scm b/examples/atomspace/formulas.scm index e678c26b78..8918af8c17 100644 --- a/examples/atomspace/formulas.scm +++ b/examples/atomspace/formulas.scm @@ -1,43 +1,67 @@ ; ; formulas.scm -- Declaring formulas that compute truth values. ; +; The rule engine, PLN and other subsystems need to be able to compute +; and alter truth values. If those formulas are known a-priori, then +; thay can be hard-coded into GroundedPredicateNodes. However, it is +; possible that those formulas are not yet known: that they will be +; learned using some learning algorithm, for example, MOSES, or maybe +; the Pattern Miner, or some neural network. +; +; In this case, the formulas need to be placed where they can be +; accessed during computation: in the AtomSpace. The example below +; shows how formulas can be declared in the AtomSpace, but in such a +; way that they can also be evaluated to yeild an actual truth value, +; which is then attached to some Atom. +; (use-modules (opencog) (opencog exec)) -; Declare a very simplified example of a truth-value formula used by -; PLN. This merely multiplies a pair of strengths. - +; The StrengthOfLink returns a single floating-point number, +; the strength of a TruthValue. (cog-execute! (StrengthOf (Concept "A" (stv 0.8 1.0)))) +; The demo needs at least one more Atom. (Concept "B" (stv 0.6 0.9)) +; Multiple the strength of the TV's of two atoms. (cog-execute! - (TimesLink (StrengthOf (Concept "A")) (StrengthOf (Concept "B")))) + (Times (StrengthOf (Concept "A")) (StrengthOf (Concept "B")))) +; Create a SimpleTruthValue with a non-trivial formula: +; It will be the TV := (1-sA*sB, cA*cB) where sA and sB are strenghts +; and cA, cB are confidence values. The PredicateFormulaLink assembles +; two floating-point values, and create a SimpleTruthValue out of them. +; (cog-evaluate! - (PredicateFormulaLink - (TimesLink (StrengthOf (Concept "A")) (StrengthOf (Concept "B"))) - (TimesLink (ConfidenceOf (Concept "A")) (ConfidenceOf (Concept "B"))))) + (PredicateFormula + (Minus + (Number 1) + (Times (StrengthOf (Concept "A")) (StrengthOf (Concept "B")))) + (Times (ConfidenceOf (Concept "A")) (ConfidenceOf (Concept "B"))))) +; The values do not need to be formulas; tehy can be hard-coded numbers. (cog-evaluate! - (PredicateFormulaLink (Number 0.7) (Number 0.314))) + (PredicateFormula (Number 0.7) (Number 0.314))) +; The below computes a truth value, and attaches it to the +; EvaluationLink. (cog-evaluate! - (EvaluationLink - (PredicateFormulaLink (Number 0.7) (Number 0.314)) + (Evaluation + (PredicateFormula (Number 0.7) (Number 0.314)) (List (Concept "A") (Concept "B")))) (cog-evaluate! - (EvaluationLink + (Evaluation ; Compute TV = (1-sA*sB, cA*cB) - (PredicateFormulaLink - (MinusLink + (PredicateFormula + (Minus (Number 1) - (TimesLink + (Times (StrengthOf (Variable "$X")) (StrengthOf (Variable "$Y")))) - (TimesLink + (Times (ConfidenceOf (Variable "$X")) (ConfidenceOf (Variable "$Y")))) (List @@ -45,15 +69,15 @@ (Concept "B")))) (cog-evaluate! - (EvaluationLink + (Evaluation ; Compute TV = (1-sA*sB, cA*cB) - (PredicateFormulaLink - (Lambda (MinusLink + (PredicateFormula + (Lambda (Minus (Number 1) - (TimesLink + (Times (StrengthOf (Variable "$X")) (StrengthOf (Variable "$Y"))))) - (Lambda (TimesLink + (Lambda (Times (ConfidenceOf (Variable "$X")) (ConfidenceOf (Variable "$Y"))))) (List From a77d3739e94f771b718b48a588f6f63c474f8a27 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sat, 26 Jan 2019 17:07:58 -0600 Subject: [PATCH 09/27] More documentatiion --- examples/atomspace/formulas.scm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/atomspace/formulas.scm b/examples/atomspace/formulas.scm index 8918af8c17..2cbe727aa0 100644 --- a/examples/atomspace/formulas.scm +++ b/examples/atomspace/formulas.scm @@ -52,6 +52,11 @@ (Concept "A") (Concept "B")))) +; More typically, one wishes to have a formula in the abstract, +; with variables in it, so that one can apply it in any one of +; a number of different situations. In the below, the variables +; are automatically reduced with the Atoms in the ListLink, and +; then the formula is evaluated to obtain a TruthValue. (cog-evaluate! (Evaluation ; Compute TV = (1-sA*sB, cA*cB) @@ -68,6 +73,8 @@ (Concept "A") (Concept "B")))) +; Optionally, you can wrap formulas with LambdaLinks. This doesn't +; really change anything; formulas work fine without LambdaLinks. (cog-evaluate! (Evaluation ; Compute TV = (1-sA*sB, cA*cB) From 10c7c2d936795b16d68fca6c08e32823df515bf4 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sat, 26 Jan 2019 20:13:35 -0600 Subject: [PATCH 10/27] Bug-fix handling of variables --- opencog/atoms/core/TruthValueOfLink.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/opencog/atoms/core/TruthValueOfLink.cc b/opencog/atoms/core/TruthValueOfLink.cc index f48ceb6400..a39b2ef37d 100644 --- a/opencog/atoms/core/TruthValueOfLink.cc +++ b/opencog/atoms/core/TruthValueOfLink.cc @@ -97,6 +97,11 @@ ValuePtr StrengthOfLink::execute() const for (const Handle& h : _outgoing) { + // Cannot take the strength of an ungrounded variable. + Type t = h->get_type(); + if (VARIABLE_NODE == t or GLOB_NODE == t) + return get_handle(); + strengths.push_back(h->getTruthValue()->get_mean()); } @@ -139,6 +144,11 @@ ValuePtr ConfidenceOfLink::execute() const for (const Handle& h : _outgoing) { + // Cannot take the confidence of an ungrounded variable. + Type t = h->get_type(); + if (VARIABLE_NODE == t or GLOB_NODE == t) + return get_handle(); + confids.push_back(h->getTruthValue()->get_confidence()); } From 7b943174fe4a386a3de74c26f964784ee005d82a Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sat, 26 Jan 2019 23:31:26 -0600 Subject: [PATCH 11/27] Additional examples --- examples/atomspace/formulas.scm | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/examples/atomspace/formulas.scm b/examples/atomspace/formulas.scm index 2cbe727aa0..255ef9f9fb 100644 --- a/examples/atomspace/formulas.scm +++ b/examples/atomspace/formulas.scm @@ -90,3 +90,37 @@ (List (Concept "A") (Concept "B")))) + + +; The PedicateFormulaLink behaves just like any other algebraic +; expression with VariableNodes in it. When executed, it might +; reduce a bit, but that is all. +(cog-execute! + (PredicateFormula + (Plus (Number 41) + (Minus + (Number 1) + (Times + (StrengthOf (Variable "$VA")) + (StrengthOf (Variable "$VB"))))) + (Times + (ConfidenceOf (Variable "$VA")) + (ConfidenceOf (Variable "$VB"))))) + +(cog-execute! + (PutLink + (VariableList (Variable "$VA") (Variable "$VB")) + (Evaluation + ; Compute TV = (1-sA*sB, cA*cB) + (PredicateFormula + (Minus + (Number 1) + (Times + (StrengthOf (Variable "$VA")) + (StrengthOf (Variable "$VB")))) + (Times + (ConfidenceOf (Variable "$VA")) + (ConfidenceOf (Variable "$VB")))) + (List + (Variable "$VA") (Variable "$VB"))) + (Set (List (Concept "A") (Concept "B"))))) From d62caf87ec855d301a0de790dddc123cc2b951d5 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sun, 27 Jan 2019 00:22:45 -0600 Subject: [PATCH 12/27] Bug fix for PutLink --- opencog/atoms/execution/Instantiator.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/opencog/atoms/execution/Instantiator.cc b/opencog/atoms/execution/Instantiator.cc index 6844571849..1fbae8a705 100644 --- a/opencog/atoms/execution/Instantiator.cc +++ b/opencog/atoms/execution/Instantiator.cc @@ -493,6 +493,14 @@ Handle Instantiator::walk_tree(const Handle& expr, bool silent) return beta_reduce(expr, *_vmap); } + // Do not reduce PredicateFormulaLink. That is because it contains + // formulas that we will need to re-evaluate in the future, so we + // must not clobber them. + if (PREDICATE_FORMULA_LINK == t) + { + return expr; + } + // If an atom is wrapped by the DontExecLink, then unwrap it, // beta-reduce it, but don't execute it. Consume the DontExecLink. // Actually, don't consume it. See discussion at issue #1303. From 70ef6c850f9ce558a7c7f47bdc2efa4048db920b Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sun, 27 Jan 2019 00:47:45 -0600 Subject: [PATCH 13/27] One last bugfix for the demo --- opencog/atoms/core/PrenexLink.cc | 2 ++ opencog/atoms/execution/Instantiator.cc | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/opencog/atoms/core/PrenexLink.cc b/opencog/atoms/core/PrenexLink.cc index a52060605b..276484e12b 100644 --- a/opencog/atoms/core/PrenexLink.cc +++ b/opencog/atoms/core/PrenexLink.cc @@ -89,7 +89,9 @@ Handle PrenexLink::reassemble(Type prenex, // prenexed. Check for PutLink to avoid infinite recursion. if (PUT_LINK != prenex and not final_varlist.empty() and nameserver().isA(prenex, PRENEX_LINK)) + { return Handle(createLink(prenex, vdecl, newbod)); + } // Otherwise, we are done with the beta-reduction. return newbod; diff --git a/opencog/atoms/execution/Instantiator.cc b/opencog/atoms/execution/Instantiator.cc index 1fbae8a705..42b68ae88b 100644 --- a/opencog/atoms/execution/Instantiator.cc +++ b/opencog/atoms/execution/Instantiator.cc @@ -271,7 +271,9 @@ Handle Instantiator::walk_tree(const Handle& expr, bool silent) else { try { - EvaluationLink::do_evaluate(_as, plo, true); + TruthValuePtr tvp = + EvaluationLink::do_evaluate(_as, plo, true); + plo->setTruthValue(tvp); } catch (const NotEvaluatableException& ex) {} unwrap.push_back(plo); From 533f0264e0d9edb6aba5b95094b79fbd2f973f6f Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sun, 27 Jan 2019 00:58:51 -0600 Subject: [PATCH 14/27] Expand the demo some more --- examples/atomspace/formulas.scm | 55 +++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/examples/atomspace/formulas.scm b/examples/atomspace/formulas.scm index 255ef9f9fb..f7fc814d72 100644 --- a/examples/atomspace/formulas.scm +++ b/examples/atomspace/formulas.scm @@ -107,20 +107,43 @@ (ConfidenceOf (Variable "$VA")) (ConfidenceOf (Variable "$VB"))))) -(cog-execute! - (PutLink - (VariableList (Variable "$VA") (Variable "$VB")) - (Evaluation - ; Compute TV = (1-sA*sB, cA*cB) - (PredicateFormula - (Minus - (Number 1) +; Beta-reducation works as normal. The below will create an +; EvaluationLink with ConceptNode A and B in it, and will set the +; truth value according to the formula. +(define the-put-result + (cog-execute! + (PutLink + (VariableList (Variable "$VA") (Variable "$VB")) + (Evaluation + ; Compute TV = (1-sA*sB, cA*cB) + (PredicateFormula + (Minus + (Number 1) + (Times + (StrengthOf (Variable "$VA")) + (StrengthOf (Variable "$VB")))) (Times - (StrengthOf (Variable "$VA")) - (StrengthOf (Variable "$VB")))) - (Times - (ConfidenceOf (Variable "$VA")) - (ConfidenceOf (Variable "$VB")))) - (List - (Variable "$VA") (Variable "$VB"))) - (Set (List (Concept "A") (Concept "B"))))) + (ConfidenceOf (Variable "$VA")) + (ConfidenceOf (Variable "$VB")))) + (List + (Variable "$VA") (Variable "$VB"))) + (Set (List (Concept "A") (Concept "B")))))) + +; The scheme variable `the-put-result` contains a SetLink with the +; result in it. Lets unwrap it, so that `evelnk` is just the +; EvaluationLink. And tehn we play a little trick. +(define evelnk (cog-outgoing-atom the-put-result 0)) + +; Change the truth value on the two concept nodes ... +(Concept "A" (stv 0.3 0.5)) +(Concept "B" (stv 0.4 0.5)) + +; Re-evaluate the EvaluationLink. Note the TV has been updated! +(cog-evaluate! evelnk) + +; Do it again, for good luck! +(Concept "A" (stv 0.1 0.99)) +(Concept "B" (stv 0.1 0.99)) + +; Re-evaluate the EvaluationLink. The TV is again recomputed! +(cog-evaluate! evelnk) From 5a509b8519d4e3b250ce6eb118e32850abe39fac Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sun, 27 Jan 2019 21:47:41 -0600 Subject: [PATCH 15/27] Another bugfix --- opencog/atoms/execution/EvaluationLink.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/opencog/atoms/execution/EvaluationLink.cc b/opencog/atoms/execution/EvaluationLink.cc index 89a14d2566..b6da102666 100644 --- a/opencog/atoms/execution/EvaluationLink.cc +++ b/opencog/atoms/execution/EvaluationLink.cc @@ -300,7 +300,9 @@ TruthValuePtr EvaluationLink::do_eval_scratch(AtomSpace* as, Instantiator inst(scratch); Handle args(HandleCast(inst.execute(sna.at(1), silent))); - return do_evaluate(scratch, sna.at(0), args, silent); + TruthValuePtr tvp(do_evaluate(scratch, sna.at(0), args, silent)); + evelnk->setTruthValue(tvp); + return tvp; } else if (IDENTICAL_LINK == t) { From 8ac829ebeb9240bd0c206b1c5ec21b5651071600 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sun, 27 Jan 2019 22:47:41 -0600 Subject: [PATCH 16/27] Start creating a unit test. --- examples/atomspace/formulas.scm | 5 +- tests/atoms/CMakeLists.txt | 7 +- tests/atoms/FormulaUTest.cxxtest | 78 ++++++++++++++++++ tests/atoms/formulas.scm | 137 +++++++++++++++++++++++++++++++ 4 files changed, 223 insertions(+), 4 deletions(-) create mode 100644 tests/atoms/FormulaUTest.cxxtest create mode 100644 tests/atoms/formulas.scm diff --git a/examples/atomspace/formulas.scm b/examples/atomspace/formulas.scm index f7fc814d72..1a4f1078e3 100644 --- a/examples/atomspace/formulas.scm +++ b/examples/atomspace/formulas.scm @@ -18,12 +18,13 @@ ; The StrengthOfLink returns a single floating-point number, ; the strength of a TruthValue. -(cog-execute! (StrengthOf (Concept "A" (stv 0.8 1.0)))) +(Concept "A" (stv 0.8 1.0)) +(cog-execute! (StrengthOf (Concept "A"))) ; The demo needs at least one more Atom. (Concept "B" (stv 0.6 0.9)) -; Multiple the strength of the TV's of two atoms. +; Multiply the strength of the TV's of two atoms. (cog-execute! (Times (StrengthOf (Concept "A")) (StrengthOf (Concept "B")))) diff --git a/tests/atoms/CMakeLists.txt b/tests/atoms/CMakeLists.txt index adfd4836c8..4fbbec9e5e 100644 --- a/tests/atoms/CMakeLists.txt +++ b/tests/atoms/CMakeLists.txt @@ -1,6 +1,9 @@ ADD_SUBDIRECTORY (truthvalue) + IF(HAVE_GUILE) + ADD_CXXTEST(HashUTest) + TARGET_LINK_LIBRARIES(HashUTest smob atomspace) ADD_CXXTEST(FreeLinkUTest) TARGET_LINK_LIBRARIES(FreeLinkUTest smob atomspace) ADD_CXXTEST(MapLinkUTest) @@ -19,8 +22,8 @@ IF(HAVE_GUILE) TARGET_LINK_LIBRARIES(PutLinkUTest execution smob atomspace) ADD_CXXTEST(QuotationUTest) TARGET_LINK_LIBRARIES(QuotationUTest execution smob atomspace) - ADD_CXXTEST(HashUTest) - TARGET_LINK_LIBRARIES(HashUTest smob atomspace) + ADD_CXXTEST(FormulaUTest) + TARGET_LINK_LIBRARIES(FormulaUTest execution smob atomspace) ENDIF(HAVE_GUILE) ADD_CXXTEST(AlphaConvertUTest) diff --git a/tests/atoms/FormulaUTest.cxxtest b/tests/atoms/FormulaUTest.cxxtest new file mode 100644 index 0000000000..459062165e --- /dev/null +++ b/tests/atoms/FormulaUTest.cxxtest @@ -0,0 +1,78 @@ +/* + * tests/atoms/FormulaUTest.cxxtest + * + * Copyright (C) 2019 Linas Vepstas + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License v3 as + * published by the Free Software Foundation and including the exceptions + * at http://opencog.org/wiki/Licenses + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program; if not, write to: + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include + +#include + +using namespace opencog; + +class FormulaUTest : public CxxTest::TestSuite +{ +private: + AtomSpace _as; + SchemeEval _eval; + +public: + FormulaUTest() : _eval(&_as) + { + logger().set_timestamp_flag(false); + logger().set_print_to_stdout_flag(true); + + _eval.eval("(add-to-load-path \"" PROJECT_SOURCE_DIR "\")"); + _eval.eval("(load-from-path \"tests/atoms/formulas.scm\")"); + } + + void setUp() {} + + void tearDown() {} + + void test_strength_of(); + +}; + +#define MAXERR 1.0e-12 + +void FormulaUTest::test_strength_of() +{ + logger().info("BEGIN TEST: %s", __FUNCTION__); + + _eval.eval("(Concept \"A\" (stv 0.8 1.0))"); + _eval.eval("(Concept \"B\" (stv 0.6 0.9))"); + + ValuePtr sof = _eval.eval_v("(cog-execute! (StrengthOf (Concept \"A\")))"); + printf("Get strenght_of=%s\n", sof->to_string().c_str()); + TS_ASSERT_EQUALS(sof->get_type(), FLOAT_VALUE); + FloatValuePtr fvp = FloatValueCast(sof); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.8), MAXERR); + + ValuePtr cof = _eval.eval_v("(cog-execute! (ConfidenceOf (Concept \"B\")))"); + printf("Get conf_of=%s\n", cof->to_string().c_str()); + TS_ASSERT_EQUALS(cof->get_type(), FLOAT_VALUE); + fvp = FloatValueCast(cof); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.9), MAXERR); + + logger().info("END TEST: %s", __FUNCTION__); +} diff --git a/tests/atoms/formulas.scm b/tests/atoms/formulas.scm new file mode 100644 index 0000000000..0e85582baa --- /dev/null +++ b/tests/atoms/formulas.scm @@ -0,0 +1,137 @@ +; +; formulas.scm -- Declaring formulas that compute truth values. +; +; This is a modified copy of an example program. It helps verify that +; the example actually works. +; +(use-modules (opencog) (opencog exec)) + +; The StrengthOfLink returns a single floating-point number, +; the strength of a TruthValue. +(define atom-a (Concept "A" (stv 0.8 1.0))) +(define atom-b (Concept "B" (stv 0.6 0.9))) + +; Multiple the strength of the TV's of two atoms. +(cog-execute! + (Times (StrengthOf (Concept "A")) (StrengthOf (Concept "B")))) + +; Create a SimpleTruthValue with a non-trivial formula: +; It will be the TV := (1-sA*sB, cA*cB) where sA and sB are strenghts +; and cA, cB are confidence values. The PredicateFormulaLink assembles +; two floating-point values, and create a SimpleTruthValue out of them. +; +(cog-evaluate! + (PredicateFormula + (Minus + (Number 1) + (Times (StrengthOf (Concept "A")) (StrengthOf (Concept "B")))) + (Times (ConfidenceOf (Concept "A")) (ConfidenceOf (Concept "B"))))) + +; The values do not need to be formulas; tehy can be hard-coded numbers. +(cog-evaluate! + (PredicateFormula (Number 0.7) (Number 0.314))) + +; The below computes a truth value, and attaches it to the +; EvaluationLink. +(cog-evaluate! + (Evaluation + (PredicateFormula (Number 0.7) (Number 0.314)) + (List + (Concept "A") + (Concept "B")))) + +; More typically, one wishes to have a formula in the abstract, +; with variables in it, so that one can apply it in any one of +; a number of different situations. In the below, the variables +; are automatically reduced with the Atoms in the ListLink, and +; then the formula is evaluated to obtain a TruthValue. +(cog-evaluate! + (Evaluation + ; Compute TV = (1-sA*sB, cA*cB) + (PredicateFormula + (Minus + (Number 1) + (Times + (StrengthOf (Variable "$X")) + (StrengthOf (Variable "$Y")))) + (Times + (ConfidenceOf (Variable "$X")) + (ConfidenceOf (Variable "$Y")))) + (List + (Concept "A") + (Concept "B")))) + +; Optionally, you can wrap formulas with LambdaLinks. This doesn't +; really change anything; formulas work fine without LambdaLinks. +(cog-evaluate! + (Evaluation + ; Compute TV = (1-sA*sB, cA*cB) + (PredicateFormula + (Lambda (Minus + (Number 1) + (Times + (StrengthOf (Variable "$X")) + (StrengthOf (Variable "$Y"))))) + (Lambda (Times + (ConfidenceOf (Variable "$X")) + (ConfidenceOf (Variable "$Y"))))) + (List + (Concept "A") + (Concept "B")))) + + +; The PedicateFormulaLink behaves just like any other algebraic +; expression with VariableNodes in it. When executed, it might +; reduce a bit, but that is all. +(cog-execute! + (PredicateFormula + (Plus (Number 41) + (Minus + (Number 1) + (Times + (StrengthOf (Variable "$VA")) + (StrengthOf (Variable "$VB"))))) + (Times + (ConfidenceOf (Variable "$VA")) + (ConfidenceOf (Variable "$VB"))))) + +; Beta-reducation works as normal. The below will create an +; EvaluationLink with ConceptNode A and B in it, and will set the +; truth value according to the formula. +(define the-put-result + (cog-execute! + (PutLink + (VariableList (Variable "$VA") (Variable "$VB")) + (Evaluation + ; Compute TV = (1-sA*sB, cA*cB) + (PredicateFormula + (Minus + (Number 1) + (Times + (StrengthOf (Variable "$VA")) + (StrengthOf (Variable "$VB")))) + (Times + (ConfidenceOf (Variable "$VA")) + (ConfidenceOf (Variable "$VB")))) + (List + (Variable "$VA") (Variable "$VB"))) + (Set (List (Concept "A") (Concept "B")))))) + +; The scheme variable `the-put-result` contains a SetLink with the +; result in it. Lets unwrap it, so that `evelnk` is just the +; EvaluationLink. And tehn we play a little trick. +; (define evelnk (cog-outgoing-atom the-put-result 0)) + +; Change the truth value on the two concept nodes ... +; (Concept "A" (stv 0.3 0.5)) +; (Concept "B" (stv 0.4 0.5)) + +; Re-evaluate the EvaluationLink. Note the TV has been updated! +; (cog-evaluate! evelnk) + +; Do it again, for good luck! +; (Concept "A" (stv 0.1 0.99)) +; (Concept "B" (stv 0.1 0.99)) + +; Re-evaluate the EvaluationLink. The TV is again recomputed! +; (cog-evaluate! evelnk) From 815f69635900cdb9542f64a5f35391d555888441 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sun, 27 Jan 2019 23:00:13 -0600 Subject: [PATCH 17/27] Extened the unit test --- examples/atomspace/formulas.scm | 2 +- tests/atoms/FormulaUTest.cxxtest | 29 ++++++++++++++++++++++++++++- tests/atoms/formulas.scm | 19 +++++-------------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/examples/atomspace/formulas.scm b/examples/atomspace/formulas.scm index 1a4f1078e3..6252ba9f88 100644 --- a/examples/atomspace/formulas.scm +++ b/examples/atomspace/formulas.scm @@ -40,7 +40,7 @@ (Times (StrengthOf (Concept "A")) (StrengthOf (Concept "B")))) (Times (ConfidenceOf (Concept "A")) (ConfidenceOf (Concept "B"))))) -; The values do not need to be formulas; tehy can be hard-coded numbers. +; The values do not need to be formulas; they can be hard-coded numbers. (cog-evaluate! (PredicateFormula (Number 0.7) (Number 0.314))) diff --git a/tests/atoms/FormulaUTest.cxxtest b/tests/atoms/FormulaUTest.cxxtest index 459062165e..3fb440b2d8 100644 --- a/tests/atoms/FormulaUTest.cxxtest +++ b/tests/atoms/FormulaUTest.cxxtest @@ -50,7 +50,7 @@ public: void tearDown() {} void test_strength_of(); - + void test_formula(); }; #define MAXERR 1.0e-12 @@ -74,5 +74,32 @@ void FormulaUTest::test_strength_of() fvp = FloatValueCast(cof); TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.9), MAXERR); + ValuePtr pof = _eval.eval_v("(cog-execute! prod)"); + printf("Get product=%s\n", pof->to_string().c_str()); + TS_ASSERT_EQUALS(pof->get_type(), FLOAT_VALUE); + fvp = FloatValueCast(pof); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.48), MAXERR); + + logger().info("END TEST: %s", __FUNCTION__); +} + +void FormulaUTest::test_formula() +{ + logger().info("BEGIN TEST: %s", __FUNCTION__); + + TruthValuePtr tvp = _eval.eval_tv("(cog-evaluate! stv-const)"); + printf("Get stv-const=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + FloatValuePtr fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.7), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.314), MAXERR); + + tvp = _eval.eval_tv("(cog-evaluate! formula-stv)"); + printf("Get formula-stv=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.52), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.9), MAXERR); + logger().info("END TEST: %s", __FUNCTION__); } diff --git a/tests/atoms/formulas.scm b/tests/atoms/formulas.scm index 0e85582baa..0498bf1744 100644 --- a/tests/atoms/formulas.scm +++ b/tests/atoms/formulas.scm @@ -6,31 +6,22 @@ ; (use-modules (opencog) (opencog exec)) -; The StrengthOfLink returns a single floating-point number, -; the strength of a TruthValue. (define atom-a (Concept "A" (stv 0.8 1.0))) (define atom-b (Concept "B" (stv 0.6 0.9))) -; Multiple the strength of the TV's of two atoms. -(cog-execute! +; Multiply the strength of the TV's of two atoms. +(define prod (Times (StrengthOf (Concept "A")) (StrengthOf (Concept "B")))) -; Create a SimpleTruthValue with a non-trivial formula: -; It will be the TV := (1-sA*sB, cA*cB) where sA and sB are strenghts -; and cA, cB are confidence values. The PredicateFormulaLink assembles -; two floating-point values, and create a SimpleTruthValue out of them. -; -(cog-evaluate! +(define stv-const (PredicateFormula (Number 0.7) (Number 0.314))) + +(define formula-stv (PredicateFormula (Minus (Number 1) (Times (StrengthOf (Concept "A")) (StrengthOf (Concept "B")))) (Times (ConfidenceOf (Concept "A")) (ConfidenceOf (Concept "B"))))) -; The values do not need to be formulas; tehy can be hard-coded numbers. -(cog-evaluate! - (PredicateFormula (Number 0.7) (Number 0.314))) - ; The below computes a truth value, and attaches it to the ; EvaluationLink. (cog-evaluate! From 11c0ae916e7b15ab47a9be1a9cdefcf542afe5dc Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sun, 27 Jan 2019 23:11:18 -0600 Subject: [PATCH 18/27] Continue work on new unit test --- examples/atomspace/formulas.scm | 12 ++++++++++-- tests/atoms/FormulaUTest.cxxtest | 29 +++++++++++++++++++++++++++++ tests/atoms/formulas.scm | 4 ++-- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/examples/atomspace/formulas.scm b/examples/atomspace/formulas.scm index 6252ba9f88..4fff68365c 100644 --- a/examples/atomspace/formulas.scm +++ b/examples/atomspace/formulas.scm @@ -45,14 +45,22 @@ (PredicateFormula (Number 0.7) (Number 0.314))) ; The below computes a truth value, and attaches it to the -; EvaluationLink. -(cog-evaluate! +; EvaluationLink. Let's demo this in three parts: first define it, +; then evaluate it, then look at it. +; +(define my-ev-link (Evaluation (PredicateFormula (Number 0.7) (Number 0.314)) (List (Concept "A") (Concept "B")))) +; Evaluate ... +(cog-evaluate! my-ev-link) + +; Print. +(display my-ev-link) + ; More typically, one wishes to have a formula in the abstract, ; with variables in it, so that one can apply it in any one of ; a number of different situations. In the below, the variables diff --git a/tests/atoms/FormulaUTest.cxxtest b/tests/atoms/FormulaUTest.cxxtest index 3fb440b2d8..9032d1dc0d 100644 --- a/tests/atoms/FormulaUTest.cxxtest +++ b/tests/atoms/FormulaUTest.cxxtest @@ -51,6 +51,7 @@ public: void test_strength_of(); void test_formula(); + void test_evaluation(); }; #define MAXERR 1.0e-12 @@ -103,3 +104,31 @@ void FormulaUTest::test_formula() logger().info("END TEST: %s", __FUNCTION__); } + +void FormulaUTest::test_evaluation() +{ + logger().info("BEGIN TEST: %s", __FUNCTION__); + + TruthValuePtr tvp = _eval.eval_tv("(cog-tv my-ev-link)"); + printf("Get before-ev-stv=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + FloatValuePtr fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 1.0), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.0), MAXERR); + + tvp = _eval.eval_tv("(cog-evaluate! my-ev-link)"); + printf("Get eval-tv=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.75), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.628), MAXERR); + + tvp = _eval.eval_tv("(cog-tv my-ev-link)"); + printf("Get after-eval-stv=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.75), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.628), MAXERR); + + logger().info("END TEST: %s", __FUNCTION__); +} diff --git a/tests/atoms/formulas.scm b/tests/atoms/formulas.scm index 0498bf1744..fe46193967 100644 --- a/tests/atoms/formulas.scm +++ b/tests/atoms/formulas.scm @@ -24,9 +24,9 @@ ; The below computes a truth value, and attaches it to the ; EvaluationLink. -(cog-evaluate! +(define my-ev-link (Evaluation - (PredicateFormula (Number 0.7) (Number 0.314)) + (PredicateFormula (Number 0.75) (Number 0.628)) (List (Concept "A") (Concept "B")))) From 87911ad57890f17cf2f3b40e90ec23930409b273 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sun, 27 Jan 2019 23:20:42 -0600 Subject: [PATCH 19/27] Expand unit test --- tests/atoms/FormulaUTest.cxxtest | 51 ++++++++++++++++++++++++++++++++ tests/atoms/formulas.scm | 44 ++++++++++----------------- 2 files changed, 66 insertions(+), 29 deletions(-) diff --git a/tests/atoms/FormulaUTest.cxxtest b/tests/atoms/FormulaUTest.cxxtest index 9032d1dc0d..c8dbc5b437 100644 --- a/tests/atoms/FormulaUTest.cxxtest +++ b/tests/atoms/FormulaUTest.cxxtest @@ -52,6 +52,7 @@ public: void test_strength_of(); void test_formula(); void test_evaluation(); + void test_evalform(); }; #define MAXERR 1.0e-12 @@ -132,3 +133,53 @@ void FormulaUTest::test_evaluation() logger().info("END TEST: %s", __FUNCTION__); } + +void FormulaUTest::test_evalform() +{ + logger().info("BEGIN TEST: %s", __FUNCTION__); + + TruthValuePtr tvp = _eval.eval_tv("(cog-tv eval-formula)"); + printf("Get before-ev-form=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + FloatValuePtr fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 1.0), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.0), MAXERR); + + tvp = _eval.eval_tv("(cog-evaluate! eval-formula)"); + printf("Get eval-formula=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.52), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.9), MAXERR); + + tvp = _eval.eval_tv("(cog-tv eval-formula)"); + printf("Get after-eval-form=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.52), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.9), MAXERR); + + // ------------------ + tvp = _eval.eval_tv("(cog-tv eval-lambda)"); + printf("Get before-ev-lambda=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 1.0), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.0), MAXERR); + + tvp = _eval.eval_tv("(cog-evaluate! eval-lambda)"); + printf("Get eval-lambda=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.52), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.9), MAXERR); + + tvp = _eval.eval_tv("(cog-tv eval-lambda)"); + printf("Get after-eval-lambda=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.52), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.9), MAXERR); + + logger().info("END TEST: %s", __FUNCTION__); +} diff --git a/tests/atoms/formulas.scm b/tests/atoms/formulas.scm index fe46193967..7af9ad0d29 100644 --- a/tests/atoms/formulas.scm +++ b/tests/atoms/formulas.scm @@ -31,12 +31,8 @@ (Concept "A") (Concept "B")))) -; More typically, one wishes to have a formula in the abstract, -; with variables in it, so that one can apply it in any one of -; a number of different situations. In the below, the variables -; are automatically reduced with the Atoms in the ListLink, and -; then the formula is evaluated to obtain a TruthValue. -(cog-evaluate! +; Formula with variables +(define eval-formula (Evaluation ; Compute TV = (1-sA*sB, cA*cB) (PredicateFormula @@ -54,38 +50,28 @@ ; Optionally, you can wrap formulas with LambdaLinks. This doesn't ; really change anything; formulas work fine without LambdaLinks. -(cog-evaluate! +(define eval-lambda (Evaluation ; Compute TV = (1-sA*sB, cA*cB) (PredicateFormula - (Lambda (Minus - (Number 1) + (Lambda + ; Lambda without a decl, intentionally so. + ; (NopeVariableList (Variable "$X") (Variable "$Y")) + (Minus + (Number 1) + (Times + (StrengthOf (Variable "$X")) + (StrengthOf (Variable "$Y"))))) + (Lambda + (VariableList (Variable "$X") (Variable "$Y")) (Times - (StrengthOf (Variable "$X")) - (StrengthOf (Variable "$Y"))))) - (Lambda (Times - (ConfidenceOf (Variable "$X")) - (ConfidenceOf (Variable "$Y"))))) + (ConfidenceOf (Variable "$X")) + (ConfidenceOf (Variable "$Y"))))) (List (Concept "A") (Concept "B")))) -; The PedicateFormulaLink behaves just like any other algebraic -; expression with VariableNodes in it. When executed, it might -; reduce a bit, but that is all. -(cog-execute! - (PredicateFormula - (Plus (Number 41) - (Minus - (Number 1) - (Times - (StrengthOf (Variable "$VA")) - (StrengthOf (Variable "$VB"))))) - (Times - (ConfidenceOf (Variable "$VA")) - (ConfidenceOf (Variable "$VB"))))) - ; Beta-reducation works as normal. The below will create an ; EvaluationLink with ConceptNode A and B in it, and will set the ; truth value according to the formula. From d39e80944b210b1b6908e1727896a4c1692a21fa Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sun, 27 Jan 2019 23:42:59 -0600 Subject: [PATCH 20/27] Last of the unit test expansion --- tests/atoms/FormulaUTest.cxxtest | 57 ++++++++++++++++++++++++++++++++ tests/atoms/formulas.scm | 24 ++------------ 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/tests/atoms/FormulaUTest.cxxtest b/tests/atoms/FormulaUTest.cxxtest index c8dbc5b437..ba8c370c19 100644 --- a/tests/atoms/FormulaUTest.cxxtest +++ b/tests/atoms/FormulaUTest.cxxtest @@ -53,6 +53,7 @@ public: void test_formula(); void test_evaluation(); void test_evalform(); + void test_putlink(); }; #define MAXERR 1.0e-12 @@ -183,3 +184,59 @@ void FormulaUTest::test_evalform() logger().info("END TEST: %s", __FUNCTION__); } + +void FormulaUTest::test_putlink() +{ + logger().info("BEGIN TEST: %s", __FUNCTION__); + + Handle hset = _eval.eval_h("(cog-execute! put-link)"); + Handle hevo = hset->getOutgoingAtom(0); + + TruthValuePtr tvp = hevo->getTruthValue(); + printf("Putter result=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + FloatValuePtr fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.52), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.9), MAXERR); + + _eval.eval("(define putex (cog-execute! put-link))"); + _eval.eval("(define pevu (cog-outgoing-atom putex 0))"); + + // -------------- + _eval.eval("(Concept \"A\" (stv 0.3 0.5))"); + _eval.eval("(Concept \"B\" (stv 0.4 0.5))"); + + tvp = _eval.eval_tv("(cog-evaluate! pevu)"); + printf("New pevu=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.88), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.25), MAXERR); + + tvp = _eval.eval_tv("(cog-tv pevu)"); + printf("Pevu-tv=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.88), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.25), MAXERR); + + // -------------- + _eval.eval("(Concept \"A\" (stv 0.1 0.99))"); + _eval.eval("(Concept \"B\" (stv 0.1 0.99))"); + + tvp = _eval.eval_tv("(cog-evaluate! pevu)"); + printf("New pevu=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.99), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.9801), MAXERR); + + tvp = _eval.eval_tv("(cog-tv pevu)"); + printf("Pevu-tv=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.99), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.9801), MAXERR); + + logger().info("END TEST: %s", __FUNCTION__); +} diff --git a/tests/atoms/formulas.scm b/tests/atoms/formulas.scm index 7af9ad0d29..c84b6c49f6 100644 --- a/tests/atoms/formulas.scm +++ b/tests/atoms/formulas.scm @@ -75,8 +75,7 @@ ; Beta-reducation works as normal. The below will create an ; EvaluationLink with ConceptNode A and B in it, and will set the ; truth value according to the formula. -(define the-put-result - (cog-execute! +(define put-link (PutLink (VariableList (Variable "$VA") (Variable "$VB")) (Evaluation @@ -92,23 +91,4 @@ (ConfidenceOf (Variable "$VB")))) (List (Variable "$VA") (Variable "$VB"))) - (Set (List (Concept "A") (Concept "B")))))) - -; The scheme variable `the-put-result` contains a SetLink with the -; result in it. Lets unwrap it, so that `evelnk` is just the -; EvaluationLink. And tehn we play a little trick. -; (define evelnk (cog-outgoing-atom the-put-result 0)) - -; Change the truth value on the two concept nodes ... -; (Concept "A" (stv 0.3 0.5)) -; (Concept "B" (stv 0.4 0.5)) - -; Re-evaluate the EvaluationLink. Note the TV has been updated! -; (cog-evaluate! evelnk) - -; Do it again, for good luck! -; (Concept "A" (stv 0.1 0.99)) -; (Concept "B" (stv 0.1 0.99)) - -; Re-evaluate the EvaluationLink. The TV is again recomputed! -; (cog-evaluate! evelnk) + (Set (List (Concept "A") (Concept "B"))))) From f33ee7c8d5a57e846f62d7733fc1e744996329f9 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Mon, 28 Jan 2019 00:04:26 -0600 Subject: [PATCH 21/27] Allow DefinedPredicate to work, also --- examples/atomspace/formulas.scm | 22 ++++ opencog/atoms/execution/EvaluationLink.cc | 133 ++++++++++++---------- 2 files changed, 95 insertions(+), 60 deletions(-) diff --git a/examples/atomspace/formulas.scm b/examples/atomspace/formulas.scm index 4fff68365c..45930d9198 100644 --- a/examples/atomspace/formulas.scm +++ b/examples/atomspace/formulas.scm @@ -156,3 +156,25 @@ ; Re-evaluate the EvaluationLink. The TV is again recomputed! (cog-evaluate! evelnk) + +(DefineLink + (DefinedPredicate "has a reddish color") + (PredicateFormula + (Minus + (Number 1) + (Times + (StrengthOf (Variable "$X")) + (StrengthOf (Variable "$Y")))) + (Times + (ConfidenceOf (Variable "$X")) + (ConfidenceOf (Variable "$Y"))))) + +(Concept "A" (stv 0.9 0.98)) +(Concept "B" (stv 0.9 0.98)) + +(cog-evaluate! + (Evaluation + (DefinedPredicate "has a reddish color") + (List + (Concept "A") + (Concept "B")))) diff --git a/opencog/atoms/execution/EvaluationLink.cc b/opencog/atoms/execution/EvaluationLink.cc index b6da102666..616b2b1144 100644 --- a/opencog/atoms/execution/EvaluationLink.cc +++ b/opencog/atoms/execution/EvaluationLink.cc @@ -604,6 +604,73 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, // uses this function, so more refactoring would be needed #include "ExecutionOutputLink.h" +static TruthValuePtr do_formula(const Handle& predform, + const Handle& cargs, + bool silent) +{ + // Collect up two floating point values. + std::vector nums; + for (const Handle& h: predform->getOutgoingSet()) + { + // An ordinary number. + if (NUMBER_NODE == h->get_type()) + { + nums.push_back(NumberNodeCast(h)->get_value()); + continue; + } + + // In case the user wanted to wrap everything in a + // LambdaLink. I don't understand why this is needed, + // but it seems to make some people feel better, so + // we support it. + Handle flh(h); + if (LAMBDA_LINK == h->get_type()) + { + LambdaLinkPtr lam(LambdaLinkCast(h)); + Type atype = cargs->get_type(); + + // Set flp and fall through, where it is executed. + flh = lam->beta_reduce(atype == LIST_LINK ? + cargs->getOutgoingSet() + : HandleSeq(1, cargs)); + } + + // At this point, we expect a FunctionLink of some kind. + if (not nameserver().isA(flh->get_type(), FUNCTION_LINK)) + { + if (silent) + throw NotEvaluatableException(); + throw SyntaxException(TRACE_INFO, + "Expecting a FunctionLink"); + } + + // If the FunctionLink has free variables in it, + // reduce them with the provided arguments. + FunctionLinkPtr flp(FunctionLinkCast(flh)); + const FreeVariables& fvars = flp->get_vars(); + if (not fvars.empty()) + { + Type atype = cargs->get_type(); + flh = fvars.substitute_nocheck(flh, + atype == LIST_LINK ? + cargs->getOutgoingSet() + : HandleSeq(1, cargs)); + flp = FunctionLinkCast(flh); + } + + // Expecting a FunctionLink without variables. + ValuePtr v(flp->execute()); + FloatValuePtr fv(FloatValueCast(v)); + nums.push_back(fv->value()[0]); + } + + // XXX FIXME; if we are given more than two floats, then + // perhaps we should create some other kind of TruthValue? + // Maybe a distributional one ?? Or a CountTV ?? + return createSimpleTruthValue(nums); +} + + /// do_evaluate -- evaluate the GroundedPredicateNode of the EvaluationLink /// /// Expects "pn" to be a GroundedPredicateNode or a DefinedPredicateNode @@ -628,6 +695,11 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, dtype = defn->get_type(); } + if (PREDICATE_FORMULA_LINK == dtype) + { + return do_formula(defn, cargs, silent); + } + // If its not a LambdaLink, then I don't know what to do... if (LAMBDA_LINK != dtype) throw RuntimeException(TRACE_INFO, @@ -648,66 +720,7 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, // AtomSpace. if (PREDICATE_FORMULA_LINK == pntype) { - // Collect up two floating point values. - std::vector nums; - for (const Handle& h: pn->getOutgoingSet()) - { - // An ordinary number. - if (NUMBER_NODE == h->get_type()) - { - nums.push_back(NumberNodeCast(h)->get_value()); - continue; - } - - // In case the user wanted to wrap everything in a - // LambdaLink. I don't understand why this is needed, - // but it seems to make some people feel better, so - // we support it. - Handle flh(h); - if (LAMBDA_LINK == h->get_type()) - { - LambdaLinkPtr lam(LambdaLinkCast(h)); - Type atype = cargs->get_type(); - - // Set flp and fall through, where it is executed. - flh = lam->beta_reduce(atype == LIST_LINK ? - cargs->getOutgoingSet() - : HandleSeq(1, cargs)); - } - - // At this point, we expect a FunctionLink of some kind. - if (not nameserver().isA(flh->get_type(), FUNCTION_LINK)) - { - if (silent) - throw NotEvaluatableException(); - throw SyntaxException(TRACE_INFO, - "Expecting a FunctionLink"); - } - - // If the FunctionLink has free variables in it, - // reduce them with the provided arguments. - FunctionLinkPtr flp(FunctionLinkCast(flh)); - const FreeVariables& fvars = flp->get_vars(); - if (not fvars.empty()) - { - Type atype = cargs->get_type(); - flh = fvars.substitute_nocheck(flh, - atype == LIST_LINK ? - cargs->getOutgoingSet() - : HandleSeq(1, cargs)); - flp = FunctionLinkCast(flh); - } - - // Expecting a FunctionLink without variables. - ValuePtr v(flp->execute()); - FloatValuePtr fv(FloatValueCast(v)); - nums.push_back(fv->value()[0]); - } - - // XXX FIXME; if we are given more than two floats, then - // perhaps we should create some other kind of TruthValue? - // Maybe a distributional one ?? Or a CountTV ?? - return createSimpleTruthValue(nums); + return do_formula(pn, cargs, silent); } if (GROUNDED_PREDICATE_NODE != pntype) From b92dc2ecf2e218840bfec51e00915bf2ae88238c Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Mon, 28 Jan 2019 00:12:23 -0600 Subject: [PATCH 22/27] Expand unit test for DefinedPrediate --- examples/atomspace/formulas.scm | 2 ++ tests/atoms/FormulaUTest.cxxtest | 51 ++++++++++++++++++++++++++++++++ tests/atoms/formulas.scm | 25 ++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/examples/atomspace/formulas.scm b/examples/atomspace/formulas.scm index 45930d9198..cfaeaa7415 100644 --- a/examples/atomspace/formulas.scm +++ b/examples/atomspace/formulas.scm @@ -157,6 +157,7 @@ ; Re-evaluate the EvaluationLink. The TV is again recomputed! (cog-evaluate! evelnk) +; One can also use DefinedPredicates, to give the formula a name. (DefineLink (DefinedPredicate "has a reddish color") (PredicateFormula @@ -172,6 +173,7 @@ (Concept "A" (stv 0.9 0.98)) (Concept "B" (stv 0.9 0.98)) +; The will cause the formula to evaluate. (cog-evaluate! (Evaluation (DefinedPredicate "has a reddish color") diff --git a/tests/atoms/FormulaUTest.cxxtest b/tests/atoms/FormulaUTest.cxxtest index ba8c370c19..911e01f6ba 100644 --- a/tests/atoms/FormulaUTest.cxxtest +++ b/tests/atoms/FormulaUTest.cxxtest @@ -54,6 +54,7 @@ public: void test_evaluation(); void test_evalform(); void test_putlink(); + void test_define(); }; #define MAXERR 1.0e-12 @@ -240,3 +241,53 @@ void FormulaUTest::test_putlink() logger().info("END TEST: %s", __FUNCTION__); } + +void FormulaUTest::test_define() +{ + logger().info("BEGIN TEST: %s", __FUNCTION__); + + TruthValuePtr tvp = _eval.eval_tv("(cog-tv red-form)"); + printf("Get before red-form=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + FloatValuePtr fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 1.0), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.0), MAXERR); + + // -------------- + _eval.eval("(Concept \"A\" (stv 0.3 0.5))"); + _eval.eval("(Concept \"B\" (stv 0.4 0.5))"); + + tvp = _eval.eval_tv("(cog-evaluate! red-form)"); + printf("New red-form=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.88), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.25), MAXERR); + + tvp = _eval.eval_tv("(cog-tv red-form)"); + printf("red-form-tv=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.88), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.25), MAXERR); + + // -------------- + _eval.eval("(Concept \"A\" (stv 0.2 0.98))"); + _eval.eval("(Concept \"B\" (stv 0.2 0.98))"); + + tvp = _eval.eval_tv("(cog-evaluate! red-form)"); + printf("New red-form=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.96), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.9604), MAXERR); + + tvp = _eval.eval_tv("(cog-tv red-form)"); + printf("red-form-tv=%s\n", tvp->to_string().c_str()); + TS_ASSERT_EQUALS(tvp->get_type(), SIMPLE_TRUTH_VALUE); + fvp = FloatValueCast(ValueCast(tvp)); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[0] - 0.96), MAXERR); + TS_ASSERT_LESS_THAN(fabs(fvp->value()[1] - 0.9604), MAXERR); + + logger().info("END TEST: %s", __FUNCTION__); +} diff --git a/tests/atoms/formulas.scm b/tests/atoms/formulas.scm index c84b6c49f6..17a3767b42 100644 --- a/tests/atoms/formulas.scm +++ b/tests/atoms/formulas.scm @@ -92,3 +92,28 @@ (List (Variable "$VA") (Variable "$VB"))) (Set (List (Concept "A") (Concept "B"))))) + + +; One can also use DefinedPredicates, to give the formula a name. +(DefineLink + (DefinedPredicate "has a reddish color") + (PredicateFormula + (Minus + (Number 1) + (Times + (StrengthOf (Variable "$X")) + (StrengthOf (Variable "$Y")))) + (Times + (ConfidenceOf (Variable "$X")) + (ConfidenceOf (Variable "$Y"))))) + +(Concept "A" (stv 0.9 0.98)) +(Concept "B" (stv 0.9 0.98)) + +; The will cause the formula to evaluate. +(define red-form + (Evaluation + (DefinedPredicate "has a reddish color") + (List + (Concept "A") + (Concept "B")))) From ebb228193a4b8a31aeadafa43bc9359ab1c4fb2f Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Mon, 28 Jan 2019 12:37:16 -0600 Subject: [PATCH 23/27] Move a big block of code --- opencog/atoms/execution/EvaluationLink.cc | 144 +++++++++++----------- 1 file changed, 74 insertions(+), 70 deletions(-) diff --git a/opencog/atoms/execution/EvaluationLink.cc b/opencog/atoms/execution/EvaluationLink.cc index 616b2b1144..bff7896c67 100644 --- a/opencog/atoms/execution/EvaluationLink.cc +++ b/opencog/atoms/execution/EvaluationLink.cc @@ -98,8 +98,9 @@ EvaluationLink::EvaluationLink(const Link& l) "Expecting an EvaluationLink"); } -// Pattern matching hack. The pattern matcher returns sets of atoms; -// if that set contains a single number, then unwrap it. +/// Pattern matching hack. The pattern matcher returns sets of atoms; +/// if that set contains a single number, then unwrap it. +/// See issue #1502 which proposes to eliminate this SetLink hack. static NumberNodePtr unwrap_set(Handle h) { if (SET_LINK == h->get_type()) @@ -125,6 +126,8 @@ static NumberNodePtr unwrap_set(Handle h) return na; } +/// Extract a single floating-point double out of a value expected to +/// contain a number. static double get_numeric_value(const ValuePtr& pap) { Type t = pap->get_type(); @@ -147,7 +150,7 @@ static double get_numeric_value(const ValuePtr& pap) pap->to_string().c_str()); } -// Perform a GreaterThan check +/// Perform a GreaterThan check static TruthValuePtr greater(AtomSpace* as, const Handle& h) { const HandleSeq& oset = h->getOutgoingSet(); @@ -200,6 +203,74 @@ static TruthValuePtr equal(AtomSpace* as, const Handle& h, bool silent) return TruthValue::FALSE_TV(); } +/// +static TruthValuePtr do_formula(const Handle& predform, + const Handle& cargs, + bool silent) +{ + // Collect up two floating point values. + std::vector nums; + for (const Handle& h: predform->getOutgoingSet()) + { + // An ordinary number. + if (NUMBER_NODE == h->get_type()) + { + nums.push_back(NumberNodeCast(h)->get_value()); + continue; + } + + // In case the user wanted to wrap everything in a + // LambdaLink. I don't understand why this is needed, + // but it seems to make some people feel better, so + // we support it. + Handle flh(h); + if (LAMBDA_LINK == h->get_type()) + { + LambdaLinkPtr lam(LambdaLinkCast(h)); + Type atype = cargs->get_type(); + + // Set flp and fall through, where it is executed. + flh = lam->beta_reduce(atype == LIST_LINK ? + cargs->getOutgoingSet() + : HandleSeq(1, cargs)); + } + + // At this point, we expect a FunctionLink of some kind. + if (not nameserver().isA(flh->get_type(), FUNCTION_LINK)) + { + if (silent) + throw NotEvaluatableException(); + throw SyntaxException(TRACE_INFO, + "Expecting a FunctionLink"); + } + + // If the FunctionLink has free variables in it, + // reduce them with the provided arguments. + FunctionLinkPtr flp(FunctionLinkCast(flh)); + const FreeVariables& fvars = flp->get_vars(); + if (not fvars.empty()) + { + Type atype = cargs->get_type(); + flh = fvars.substitute_nocheck(flh, + atype == LIST_LINK ? + cargs->getOutgoingSet() + : HandleSeq(1, cargs)); + flp = FunctionLinkCast(flh); + } + + // Expecting a FunctionLink without variables. + ValuePtr v(flp->execute()); + FloatValuePtr fv(FloatValueCast(v)); + nums.push_back(fv->value()[0]); + } + + // XXX FIXME; if we are given more than two floats, then + // perhaps we should create some other kind of TruthValue? + // Maybe a distributional one ?? Or a CountTV ?? + return createSimpleTruthValue(nums); +} + + static bool is_evaluatable_sat(const Handle& satl) { if (1 != satl->get_arity()) @@ -604,73 +675,6 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, // uses this function, so more refactoring would be needed #include "ExecutionOutputLink.h" -static TruthValuePtr do_formula(const Handle& predform, - const Handle& cargs, - bool silent) -{ - // Collect up two floating point values. - std::vector nums; - for (const Handle& h: predform->getOutgoingSet()) - { - // An ordinary number. - if (NUMBER_NODE == h->get_type()) - { - nums.push_back(NumberNodeCast(h)->get_value()); - continue; - } - - // In case the user wanted to wrap everything in a - // LambdaLink. I don't understand why this is needed, - // but it seems to make some people feel better, so - // we support it. - Handle flh(h); - if (LAMBDA_LINK == h->get_type()) - { - LambdaLinkPtr lam(LambdaLinkCast(h)); - Type atype = cargs->get_type(); - - // Set flp and fall through, where it is executed. - flh = lam->beta_reduce(atype == LIST_LINK ? - cargs->getOutgoingSet() - : HandleSeq(1, cargs)); - } - - // At this point, we expect a FunctionLink of some kind. - if (not nameserver().isA(flh->get_type(), FUNCTION_LINK)) - { - if (silent) - throw NotEvaluatableException(); - throw SyntaxException(TRACE_INFO, - "Expecting a FunctionLink"); - } - - // If the FunctionLink has free variables in it, - // reduce them with the provided arguments. - FunctionLinkPtr flp(FunctionLinkCast(flh)); - const FreeVariables& fvars = flp->get_vars(); - if (not fvars.empty()) - { - Type atype = cargs->get_type(); - flh = fvars.substitute_nocheck(flh, - atype == LIST_LINK ? - cargs->getOutgoingSet() - : HandleSeq(1, cargs)); - flp = FunctionLinkCast(flh); - } - - // Expecting a FunctionLink without variables. - ValuePtr v(flp->execute()); - FloatValuePtr fv(FloatValueCast(v)); - nums.push_back(fv->value()[0]); - } - - // XXX FIXME; if we are given more than two floats, then - // perhaps we should create some other kind of TruthValue? - // Maybe a distributional one ?? Or a CountTV ?? - return createSimpleTruthValue(nums); -} - - /// do_evaluate -- evaluate the GroundedPredicateNode of the EvaluationLink /// /// Expects "pn" to be a GroundedPredicateNode or a DefinedPredicateNode From 01b4698cf3a5de70b64107126d99a90b6dc1952e Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Mon, 28 Jan 2019 12:52:40 -0600 Subject: [PATCH 24/27] Cleanup for readability --- opencog/atoms/execution/EvaluationLink.cc | 38 ++++++++++------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/opencog/atoms/execution/EvaluationLink.cc b/opencog/atoms/execution/EvaluationLink.cc index bff7896c67..afd21feb84 100644 --- a/opencog/atoms/execution/EvaluationLink.cc +++ b/opencog/atoms/execution/EvaluationLink.cc @@ -203,10 +203,16 @@ static TruthValuePtr equal(AtomSpace* as, const Handle& h, bool silent) return TruthValue::FALSE_TV(); } -/// -static TruthValuePtr do_formula(const Handle& predform, - const Handle& cargs, - bool silent) +static HandleSeq get_seq(const Handle& cargs) +{ + if (LIST_LINK == cargs->get_type()) return cargs->getOutgoingSet(); + return HandleSeq(1, cargs); +} + +/// Evalaute a formula defined by a PREDICATE_FORMULA_LINK +static TruthValuePtr eval_formula(const Handle& predform, + const Handle& cargs, + bool silent) { // Collect up two floating point values. std::vector nums; @@ -226,13 +232,8 @@ static TruthValuePtr do_formula(const Handle& predform, Handle flh(h); if (LAMBDA_LINK == h->get_type()) { - LambdaLinkPtr lam(LambdaLinkCast(h)); - Type atype = cargs->get_type(); - - // Set flp and fall through, where it is executed. - flh = lam->beta_reduce(atype == LIST_LINK ? - cargs->getOutgoingSet() - : HandleSeq(1, cargs)); + // Set flh and fall through, where it is executed. + flh = LambdaLinkCast(h)->beta_reduce(get_seq(cargs)); } // At this point, we expect a FunctionLink of some kind. @@ -250,11 +251,7 @@ static TruthValuePtr do_formula(const Handle& predform, const FreeVariables& fvars = flp->get_vars(); if (not fvars.empty()) { - Type atype = cargs->get_type(); - flh = fvars.substitute_nocheck(flh, - atype == LIST_LINK ? - cargs->getOutgoingSet() - : HandleSeq(1, cargs)); + flh = fvars.substitute_nocheck(flh, get_seq(cargs)); flp = FunctionLinkCast(flh); } @@ -701,7 +698,7 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, if (PREDICATE_FORMULA_LINK == dtype) { - return do_formula(defn, cargs, silent); + return eval_formula(defn, cargs, silent); } // If its not a LambdaLink, then I don't know what to do... @@ -713,10 +710,7 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, // Treat it as if it were a PutLink -- perform the // beta-reduction, and evaluate the result. LambdaLinkPtr lam(LambdaLinkCast(defn)); - Type atype = cargs->get_type(); - Handle reduct = lam->beta_reduce(atype == LIST_LINK ? - cargs->getOutgoingSet() - : HandleSeq(1, cargs)); + Handle reduct = lam->beta_reduce(get_args(cargs)); return do_evaluate(as, reduct, silent); } @@ -724,7 +718,7 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, // AtomSpace. if (PREDICATE_FORMULA_LINK == pntype) { - return do_formula(pn, cargs, silent); + return eval_formula(pn, cargs, silent); } if (GROUNDED_PREDICATE_NODE != pntype) From 37b090a3c804abe4270da3bf170c503060fb2ba5 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Mon, 28 Jan 2019 13:33:19 -0600 Subject: [PATCH 25/27] Use the correct exception type. --- opencog/atoms/execution/EvaluationLink.cc | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/opencog/atoms/execution/EvaluationLink.cc b/opencog/atoms/execution/EvaluationLink.cc index afd21feb84..c6efdefa08 100644 --- a/opencog/atoms/execution/EvaluationLink.cc +++ b/opencog/atoms/execution/EvaluationLink.cc @@ -155,7 +155,7 @@ static TruthValuePtr greater(AtomSpace* as, const Handle& h) { const HandleSeq& oset = h->getOutgoingSet(); if (2 != oset.size()) - throw RuntimeException(TRACE_INFO, + throw SyntaxException(TRACE_INFO, "GreaterThankLink expects two arguments"); Instantiator inst(as); @@ -176,7 +176,7 @@ static TruthValuePtr identical(const Handle& h) { const HandleSeq& oset = h->getOutgoingSet(); if (2 != oset.size()) - throw RuntimeException(TRACE_INFO, + throw SyntaxException(TRACE_INFO, "IdenticalLink expects two arguments"); if (oset[0] == oset[1]) @@ -190,7 +190,7 @@ static TruthValuePtr equal(AtomSpace* as, const Handle& h, bool silent) { const HandleSeq& oset = h->getOutgoingSet(); if (2 != oset.size()) - throw RuntimeException(TRACE_INFO, + throw SyntaxException(TRACE_INFO, "EqualLink expects two arguments"); Instantiator inst(as); @@ -661,7 +661,7 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, { if (2 != sna.size()) { - throw RuntimeException(TRACE_INFO, + throw SyntaxException(TRACE_INFO, "Incorrect arity for an EvaluationLink!"); } return do_evaluate(as, sna[0], sna[1], silent); @@ -703,14 +703,14 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, // If its not a LambdaLink, then I don't know what to do... if (LAMBDA_LINK != dtype) - throw RuntimeException(TRACE_INFO, + throw SyntaxException(TRACE_INFO, "Expecting definition to be a LambdaLink, got %s", defn->to_string().c_str()); // Treat it as if it were a PutLink -- perform the // beta-reduction, and evaluate the result. LambdaLinkPtr lam(LambdaLinkCast(defn)); - Handle reduct = lam->beta_reduce(get_args(cargs)); + Handle reduct = lam->beta_reduce(get_seq(cargs)); return do_evaluate(as, reduct, silent); } @@ -774,7 +774,8 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, return applier->apply_tv(schema.substr(pos), args); #else throw RuntimeException(TRACE_INFO, - "Cannot evaluate scheme GroundedPredicateNode!"); + "This binary does not have scheme support in it; " + "Cannot evaluate scheme GroundedPredicateNode!"); #endif /* HAVE_GUILE */ } @@ -790,7 +791,8 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, return applier.apply_tv(as, schema.substr(pos), args); #else throw RuntimeException(TRACE_INFO, - "Cannot evaluate python GroundedPredicateNode!"); + "This binary does not have python support in it; " + "Cannot evaluate python GroundedPredicateNode!"); #endif /* HAVE_CYTHON */ } @@ -819,7 +821,7 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, if (silent) throw NotEvaluatableException(); - throw RuntimeException(TRACE_INFO, + throw SyntaxException(TRACE_INFO, "Invalid return value from predicate %s\nArgs: %s", pn->to_string().c_str(), cargs->to_string().c_str()); From 913d2ecb836484cdbdc47ba8bb65db8e5ff1985e Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Mon, 28 Jan 2019 13:57:27 -0600 Subject: [PATCH 26/27] Wrap up the silent exceptions. --- opencog/atoms/execution/EvaluationLink.cc | 76 ++++++++++++----------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/opencog/atoms/execution/EvaluationLink.cc b/opencog/atoms/execution/EvaluationLink.cc index c6efdefa08..a2da93b095 100644 --- a/opencog/atoms/execution/EvaluationLink.cc +++ b/opencog/atoms/execution/EvaluationLink.cc @@ -98,6 +98,32 @@ EvaluationLink::EvaluationLink(const Link& l) "Expecting an EvaluationLink"); } +/// We get exceptions in two differet ways: (a) due to user error, +/// in which case we need to report the error to the user, and +/// (b) occasionally expected errors, which might occur during normal +/// processing, and should be ignored. The "normal" errors should not +/// be reported to the user; nor should they be printed to the log-file. +/// Using a try-catch block is enough to prevent them from being passed +/// to the user; but it is not enough to prevent them from printing. +/// Thus, we use a bool flag to not print. (It would be nice if C++ +/// offered a way to automate this in the catch-block, so that the +/// pesky "silent" flag was not needed.) +/// +/// DefaultPatternMatchCB.cc and also Instantiator.cc both catch +/// the NotEvaluatableException thrown here. Basically, these +/// know that they might be sending non-evaluatable atoms here, and +/// don't want to garbage up the log files with bogus errors. +/// +void throwSyntaxException(bool silent, const char* message...) +{ + if (silent) + throw NotEvaluatableException(); + va_list args; + va_start(args, message); + throw SyntaxException(TRACE_INFO, message, args); + va_end(args); +} + /// Pattern matching hack. The pattern matcher returns sets of atoms; /// if that set contains a single number, then unwrap it. /// See issue #1502 which proposes to eliminate this SetLink hack. @@ -238,12 +264,7 @@ static TruthValuePtr eval_formula(const Handle& predform, // At this point, we expect a FunctionLink of some kind. if (not nameserver().isA(flh->get_type(), FUNCTION_LINK)) - { - if (silent) - throw NotEvaluatableException(); - throw SyntaxException(TRACE_INFO, - "Expecting a FunctionLink"); - } + throwSyntaxException(silent, "Expecting a FunctionLink"); // If the FunctionLink has free variables in it, // reduce them with the provided arguments. @@ -585,13 +606,10 @@ TruthValuePtr EvaluationLink::do_eval_scratch(AtomSpace* as, nums.push_back(NumberNodeCast(h)->get_value()); continue; } + if (not nameserver().isA(h->get_type(), FUNCTION_LINK)) - { - if (silent) - throw NotEvaluatableException(); - throw SyntaxException(TRACE_INFO, - "Expecting a FunctionLink"); - } + throwSyntaxException(silent, "Expecting a FunctionLink"); + ValuePtr v(FunctionLinkCast(h)->execute()); FloatValuePtr fv(FloatValueCast(v)); nums.push_back(fv->value()[0]); @@ -623,22 +641,11 @@ TruthValuePtr EvaluationLink::do_eval_scratch(AtomSpace* as, return TruthValueCast(pap); } - // We get exceptions here in two differet ways: (a) due to user - // error, in which case we need to print an error, and (b) intentionally, - // e.g. when Instantiator calls us, knowing it will get an error, - // in which case, printing the exception message is a waste of CPU - // time... - // - // DefaultPatternMatchCB.cc and also Instantiator wants to catch - // the NotEvaluatableException thrown here. Basically, these - // know that they might be sending non-evaluatable atoms here, and - // don't want to garbage up the log files with bogus errors. - if (silent) - throw NotEvaluatableException(); - - throw SyntaxException(TRACE_INFO, + throwSyntaxException(silent, "Either incorrect or not implemented yet. Cannot evaluate %s", evelnk->to_string().c_str()); + + return TruthValuePtr(); // not reached } TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, @@ -814,18 +821,13 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, result = *res; free(res); } - if (nullptr == result) { - // If silent is true, return a simpler and non-logged - // exception, which may, in some contexts, be considerably - // faster than the one below. - if (silent) - throw NotEvaluatableException(); - throw SyntaxException(TRACE_INFO, - "Invalid return value from predicate %s\nArgs: %s", - pn->to_string().c_str(), - cargs->to_string().c_str()); - } + if (nullptr == result) + throwSyntaxException(silent, + "Invalid return value from predicate %s\nArgs: %s", + pn->to_string().c_str(), + cargs->to_string().c_str()); + return result; } From a552cffd000b29a9de469727dcd4a00068ad6bc6 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Mon, 28 Jan 2019 14:02:49 -0600 Subject: [PATCH 27/27] The exceptions here will never be silent. --- opencog/atoms/execution/EvaluationLink.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/opencog/atoms/execution/EvaluationLink.cc b/opencog/atoms/execution/EvaluationLink.cc index a2da93b095..e29a9dcaf2 100644 --- a/opencog/atoms/execution/EvaluationLink.cc +++ b/opencog/atoms/execution/EvaluationLink.cc @@ -237,8 +237,7 @@ static HandleSeq get_seq(const Handle& cargs) /// Evalaute a formula defined by a PREDICATE_FORMULA_LINK static TruthValuePtr eval_formula(const Handle& predform, - const Handle& cargs, - bool silent) + const Handle& cargs) { // Collect up two floating point values. std::vector nums; @@ -264,7 +263,7 @@ static TruthValuePtr eval_formula(const Handle& predform, // At this point, we expect a FunctionLink of some kind. if (not nameserver().isA(flh->get_type(), FUNCTION_LINK)) - throwSyntaxException(silent, "Expecting a FunctionLink"); + throw SyntaxException(TRACE_INFO, "Expecting a FunctionLink"); // If the FunctionLink has free variables in it, // reduce them with the provided arguments. @@ -598,6 +597,7 @@ TruthValuePtr EvaluationLink::do_eval_scratch(AtomSpace* as, } else if (PREDICATE_FORMULA_LINK == t) { + // A shortened, argument-free version of eval_formula() std::vector nums; for (const Handle& h: evelnk->getOutgoingSet()) { @@ -608,7 +608,7 @@ TruthValuePtr EvaluationLink::do_eval_scratch(AtomSpace* as, } if (not nameserver().isA(h->get_type(), FUNCTION_LINK)) - throwSyntaxException(silent, "Expecting a FunctionLink"); + throw SyntaxException(TRACE_INFO, "Expecting a FunctionLink"); ValuePtr v(FunctionLinkCast(h)->execute()); FloatValuePtr fv(FloatValueCast(v)); @@ -705,7 +705,7 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, if (PREDICATE_FORMULA_LINK == dtype) { - return eval_formula(defn, cargs, silent); + return eval_formula(defn, cargs); } // If its not a LambdaLink, then I don't know what to do... @@ -725,7 +725,7 @@ TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, // AtomSpace. if (PREDICATE_FORMULA_LINK == pntype) { - return eval_formula(pn, cargs, silent); + return eval_formula(pn, cargs); } if (GROUNDED_PREDICATE_NODE != pntype)