class Person_KE(KnowledgeEngine): @Rule(NOT(ConflictResolver(resolved=L(True))), AND(Person(name=L("name"), age=C('age')), Person(name=L("name"), age=N("age")))) def same_name(self, age): nonlocal executions executions.append(age)
class Test(KnowledgeEngine): """ Test KE """ @Rule(OR(Fact(something=L(1)), Fact(something=L(2)))) def rule1(self): """ First rule, something=1 and something=2""" pass
def test_matching_captured_different_facts_NOT_negative(): """ Declaring a NOT() using C/V should be a factlist-wide comparision. Negative test (returning no activation because there were matches (the NOT is therefore not executed). """ from pyknow.rule import Rule, NOT from pyknow.fact import Fact, L, V, C from pyknow.engine import KnowledgeEngine class Person(Fact): pass executions = [] class Person_KE(KnowledgeEngine): @Rule(Person(name=C("name")), NOT(Person(surname=V('name')))) def same_name(self, name): nonlocal executions executions.append(name) ke_ = Person_KE() ke_.deffacts(Person(name=L('name'), surname=L("NotAName"))) ke_.deffacts(Person(name=L('name'), surname=L("name"))) ke_.reset() ke_.run() assert executions == []
def test_or_operator(): """ Test OR operator """ from pyknow.engine import KnowledgeEngine from pyknow.rule import Rule, OR from pyknow.fact import Fact, L class Test(KnowledgeEngine): """ Test KE """ @Rule(OR(Fact(something=L(1)), Fact(something=L(2)))) def rule1(self): """ First rule, something=1 and something=2""" pass ke_ = Test() ke_.deffacts(Fact(something=L(1))) ke_.reset() assert len(ke_.agenda.activations) == 1 ke_ = Test() ke_.deffacts(Fact(something=L(2))) ke_.reset() assert len(ke_.agenda.activations) == 1 ke_ = Test() ke_.deffacts(Fact(something=L(3))) ke_.reset() assert len(ke_.agenda.activations) == 0
def test_facts_are_equal(): """ We may need to use EQUAL facts that are not the same object but has the same values """ from pyknow.fact import Fact, C, L assert Fact(a=L("foo")) == Fact(a=L("foo")) assert Fact(a=L("foo"), b=C("bar")) == Fact(a=L("foo"), b=C("bar"))
class Parent(KnowledgeEngine): @Rule(Fact(inherited=L(True))) def inherited(self): executions.append(2) @Rule(NOT(Fact(not_inherited=L(True)))) def not_inherited(self): executions.append(3)
def test_matching_captured_same_facts_AND(): """ CLIPs behaves like this:: (watch all) (deftemplate person (slot name) (slot surname) ) (defrule test_clips (foo (name ?thename)) (foo (surname ?thename)) => (printout t "found " ?valor_1 crlf )) (defrule test_clips (person (name ?thename)) (person (surname ?thename)) => (printout t "Found")) (deffacts thenames (person (name NotAName) (surname NotAName)) (person (name name) (surname NotAName)) ) (reset) (run) Result: FIRE 1 test_clips: f-1,f-2 FIRE 2 test_clips: f-1,f-1 """ from pyknow.rule import Rule from pyknow.fact import Fact, L, V, C from pyknow.engine import KnowledgeEngine class Person(Fact): pass executions = [] class Person_KE(KnowledgeEngine): @Rule(Person(name=C("name"), surname=V('name'))) def same_name(self, name): nonlocal executions executions.append(name) ke_ = Person_KE() ke_.deffacts(Person(name=L('NotAName'), surname=L("NotAName"))) ke_.deffacts(Person(name=L('name'), surname=L("NotAName"))) ke_.reset() ke_.run() assert executions == ["NotAName", "NotAName"]
class Person_KE(KnowledgeEngine): @Rule(Person(name=L("David"))) def david(self): self.declare(Person(name=L("Pepe"), apellido=L("stuff"))) @Rule(Person(name=L("Pepe"))) def pepe(self): nonlocal executed executed = True
def test_Fact_store_literal_value_and_keyset(): """ Check that, upon setting a fact value, we set its keyset and value """ from pyknow.fact import Fact, L value = {'a': L(1), 'b': L(2)} fact = Fact(**value) assert fact.value == value assert set(value.keys()) == fact.keyset
def test_KnowledgeEngine_reset(): """ Given a set of fixed facts, they're still there after a reset. Also, we have in account that InitialFact() is always there. And that if we add a normal fact after that, it's not persistent """ from pyknow.engine import KnowledgeEngine from pyknow.fact import Fact, L ke = KnowledgeEngine() ke.deffacts(Fact(foo=L(1))) ke.deffacts(Fact(foo=L(1), bar=L(2))) ke.reset() assert len(ke.facts.facts) == 3 ke = KnowledgeEngine() ke.deffacts(Fact(foo=L(1))) ke.declare(Fact(foo=L(9))) ke.deffacts(Fact(foo=L(1), bar=L(2))) ke.reset() assert len(ke.facts.facts) == 3 ke = KnowledgeEngine() ke.deffacts(Fact(foo=L(1))) ke.declare(Fact(foo=L(9))) ke.reset() assert len(ke.facts.facts) == 2
def test_tree_retract_matching(): """ Test tree not """ from pyknow.tree import KETree from pyknow.engine import KnowledgeEngine from pyknow.rule import Rule, NOT from pyknow.fact import Fact, L executions = [] global executions class Child(KnowledgeEngine): @Rule(Fact(always_run=L(True))) def always_run(self): executions.append(1) self.parent.retract_matching(Fact(initial=L(True))) self.parent.declare(Fact(inherited=L(True))) class Parent(KnowledgeEngine): @Rule(Fact(initial=L(True))) def shouldnot_run(self): # pytest.set_trace() print("Ran") executions.append(4) @Rule(Fact(inherited=L(True))) def inherited(self): executions.append(2) @Rule(NOT(Fact(not_inherited=L(True)))) def not_inherited(self): executions.append(3) tree_ = { 'node': Parent(), 'children': [{ 'node': Child(), 'children': [] }, { 'node': Child(), 'children': [] }] } tree_['node'].deffacts(Fact(initial=L(True))) tree_['children'][0]['node'].deffacts(Fact(always_run=L(True))) tree_['children'][0]['node'].deffacts(Fact(not_inherited=L(False))) KETree(tree_).run_sequential() print(executions) assert len(executions) == 3
def test_match_if_all_defined_is_present(kwargs): from pyknow.fact import Fact, L kwargs['ATLEAST1'] = 'VALUE' kwsuperset = kwargs.copy() kwsuperset.update({'OTHER1': 'VALUE'}) f0 = Fact(**{a: L(b) for a, b in kwargs.items()}) f1 = Fact(**{a: L(b) for a, b in kwsuperset.items()}) assert not f1.matches(f0, {}) assert f0.matches(f1, {})
def test_Rule_with_only_one_NOT_doesnt_match_if_fact_is_present(): from pyknow.rule import NOT, Rule from pyknow.factlist import FactList from pyknow.fact import Fact, L from pyknow.match import Capturation r = Rule(NOT(Fact(something=L(True)))) fl = FactList() fl.declare(Fact(something=L(True))) assert next(r.get_activations(fl, Capturation()), None) is None
class Test(KnowledgeEngine): @Rule(Fact(something=L(1)), Fact(something=L(2))) def rule1(self): nonlocal executions executions.append('rule1') @Rule(Fact(something=L(3))) def rule2(self): nonlocal executions executions.append('rule2')
def test_facts_produce_activations_without_capturation(): """ A fact produces activations if no capturations are provided """ from pyknow.fact import Fact, L from pyknow.factlist import FactList from pyknow.match import Capturation flist = FactList() flist.declare(Fact(a=L(1))) caps = list(Fact(a=L(1)).get_activations(flist, Capturation())) assert len(caps) == 1
def test_Rule_multiple_criteria_generates_activation_with_matching_facts(): from pyknow.rule import Rule from pyknow.factlist import FactList from pyknow.fact import Fact, L r = Rule(Fact(a=L(1)), Fact(b=L(2))) fl = FactList() fl.declare(Fact(a=L(1))) fl.declare(Fact(b=L(2))) activations = list(r.get_activations(fl)) assert len(activations) == 1 assert {0, 1} == set(activations[0].facts)
def test_Rule_and_AND_nesting(): from pyknow.rule import Rule, AND from pyknow.factlist import FactList from pyknow.fact import Fact, L r = Rule(AND(Fact(a=L(2)), Fact(b=L(1)))) fl = FactList() fl.declare(Fact(a=L(2))) fl.declare(Fact(b=L(1))) activations = list(r.get_activations(fl)) assert len(activations) == 1 assert {0, 1} == set(activations[0].facts)
class Parent(KnowledgeEngine): @Rule(Fact(initial=L(True))) def shouldnot_run(self): # pytest.set_trace() print("Ran") executions.append(4) @Rule(Fact(inherited=L(True))) def inherited(self): executions.append(2) @Rule(NOT(Fact(not_inherited=L(True)))) def not_inherited(self): executions.append(3)
class Test(KnowledgeEngine): """ Test KE """ @Rule(Fact(something=L(1)), Fact(something=L(2))) def rule1(self): """ First rule, something=1 and something=2""" nonlocal executions executions.append('rule1') @Rule(Fact(something=L(3))) def rule2(self): """ Second rule, only something=3 """ nonlocal executions executions.append('rule2')
def test_facts_produce_activations_that_are_Activations(): """ A fact produces activations that are Activation objects """ from pyknow.fact import Fact, L from pyknow.factlist import FactList from pyknow.match import Capturation from pyknow.activation import Activation flist = FactList() flist.declare(Fact(a=L(1))) caps = list(Fact(a=L(1)).get_activations(flist, Capturation())) assert len(caps) == 1 assert isinstance(caps[0], Activation) assert caps[0].facts == (0,)
def test_tree_NOT(): """ Test tree not """ from pyknow.tree import KETree from pyknow.engine import KnowledgeEngine from pyknow.rule import Rule, NOT from pyknow.fact import Fact, L executions = [] global executions class Child(KnowledgeEngine): @Rule(Fact(always_run=L(True))) def always_run(self): executions.append(1) self.parent.declare(Fact(inherited=L(True))) class Parent(KnowledgeEngine): @Rule(Fact(inherited=L(True))) def inherited(self): executions.append(2) @Rule(NOT(Fact(not_inherited=L(True)))) def not_inherited(self): executions.append(3) tree_ = { 'node': Parent(), 'children': [{ 'node': Child(), 'children': [] }, { 'node': Child(), 'children': [] }] } tree_['children'][0]['node'].deffacts(Fact(always_run=L(True))) # Parent does not inherit its children facts: tree_['children'][0]['node'].deffacts(Fact(not_inherited=L(True))) KETree(tree_).run_sequential() print(executions) assert len(executions) == 3
def test_matching_different_number_of_arguments(): from pyknow.rule import Rule from pyknow.fact import Fact, L from pyknow.engine import KnowledgeEngine class Person(Fact): pass executed = False class Person_KE(KnowledgeEngine): @Rule(Person(name=L("David"))) def david(self): self.declare(Person(name=L("Pepe"), apellido=L("stuff"))) @Rule(Person(name=L("Pepe"))) def pepe(self): nonlocal executed executed = True ke_ = Person_KE() ke_.deffacts(Person(name=L("David"))) ke_.reset() ke_.run() assert executed
def test_nested_declarations(): from pyknow.rule import Rule from pyknow.fact import Fact, L from pyknow.engine import KnowledgeEngine class Person(Fact): pass executed = False class Person_KE(KnowledgeEngine): @Rule(Person(name=L("David"))) def david(self): self.declare(Person(name=L("Pepe"))) @Rule(Person(name=L("Pepe"))) def pepe(self): nonlocal executed executed = True ke_ = Person_KE() ke_.deffacts(Person(name=L("David"))) ke_.reset() ke_.run() assert executed
def test_ke_inheritance(): from pyknow.rule import Rule from pyknow.fact import Fact, L from pyknow.engine import KnowledgeEngine executed = False class Person(Fact): pass class Base(KnowledgeEngine): @Rule(Person(name=L('pepe'))) def is_pepe(self): self.declare(Person(drinks=L("coffee"))) class Test(Base): @Rule(Person(drinks=L("coffee"))) def drinks_coffee(self): nonlocal executed executed = True ke_ = Test() ke_.deffacts(Person(name=L('pepe'))) ke_.reset() ke_.run() assert executed
def test_rules_are_executed_once(to_declare_random): from random import shuffle from pyknow.engine import KnowledgeEngine from pyknow.rule import Rule from pyknow.fact import Fact, L executions = [] class Test(KnowledgeEngine): @Rule(Fact(something=L(1)), Fact(something=L(2))) def rule1(self): nonlocal executions executions.append('rule1') @Rule(Fact(something=L(3))) def rule2(self): nonlocal executions executions.append('rule2') ke = Test() to_declare = list(set(to_declare_random + [1, 2, 3])) shuffle(to_declare) print(to_declare) for i in to_declare: ke.deffacts(Fact(something=L(i))) ke.reset() ke.run() assert executions.count('rule1') == 1 assert executions.count('rule2') == 1
def test_Rule_simple_testce(): from pyknow.rule import Rule from pyknow.fact import Fact, T, L from pyknow.factlist import FactList r = Rule(Fact(a=T(lambda c, x: x.startswith('D')))) fl = FactList() fl.declare(Fact(a=L("David"))) fl.declare(Fact(a=L("Penelope"))) activations = list(r.get_activations(fl)) assert len(activations) == 1 assert {0} == set(activations[0].facts)
def test_or_notmatching_operator(): """ Test OR operator """ from pyknow.engine import KnowledgeEngine from pyknow.rule import Rule, OR from pyknow.fact import Fact, L class Test(KnowledgeEngine): """ Test KE """ @Rule(OR(Fact(something=L(1)), Fact(something=L(2)))) def rule1(self): """ First rule, something=1 and something=2""" pass static = ((1, 3), (1, 3, 5)) for test in static: ke_ = Test() ke_.reset() for val in test: ke_.deffacts(Fact(none=L(val))) ke_.reset() assert len(ke_.agenda.activations) == 0 ke_.run()
def test_Rule_with_NOT_DEFINED(): from pyknow.rule import Rule, NOT from pyknow.factlist import FactList from pyknow.fact import Fact, InitialFact, L, W r = Rule(Fact(a=L(1)), NOT(Fact(b=W(True)))) fl = FactList() fl.declare(InitialFact()) fl.declare(Fact(a=L(1))) activations = r.get_activations(fl) assert len(list(activations)) == 1 fl.declare(Fact(b=L('SOMETHING'))) activations = r.get_activations(fl) assert len(list(activations)) == 0
def test_N_resolve(): """ Test N Resolve """ from pyknow.fact import Fact, N, L key = "key_test" context = {key: True} not_fact = N(key) against_fact = Fact(**{key: L(False)}) assert not_fact.resolve(against_fact, key, context)
def test_V_resolve(): """ Test V Resolve """ from pyknow.fact import Fact, V, L key = "key_test" context = {key: True} not_fact = V(key) against_fact = Fact(**{key: L(True)}) assert not_fact.resolve(against_fact, key, context)