def test_conflictsetchange_valid_adds_to_memory(): from pyknow.fact import Fact from pyknow.matchers.rete.nodes import ConflictSetNode from pyknow.matchers.rete.token import Token, TokenInfo from pyknow.rule import Rule csn = ConflictSetNode(Rule()) f = Fact(test='data') f.__factid__ = 1 csn.activate(Token.valid(f, {'mycontextdata': 'data'})) assert TokenInfo([f], {'mycontextdata': 'data'}) in csn.memory
def test_notnode_left_activate_valid_non_matching(TestNode): from pyknow.matchers.rete.nodes import NotNode from pyknow.matchers.rete.token import Token from pyknow.fact import Fact nn = NotNode(lambda l, r: False) nn.right_memory.append(Token.valid(Fact(test='data1')).to_info()) nn.right_memory.append(Token.valid(Fact(test='data2')).to_info()) tn1 = TestNode() tn2 = TestNode() nn.add_child(tn1, tn1.activate) nn.add_child(tn2, tn2.activate) token = Token.valid(Fact(test='data')) nn.activate_left(token) assert tn1.added == [token] assert tn2.added == [token] assert nn.left_memory[token.to_info()] == 0
def test_notnode_right_activate_valid_match_just_one(TestNode): from pyknow.matchers.rete.nodes import NotNode from pyknow.matchers.rete.token import Token from pyknow.fact import Fact nn = NotNode(lambda l, r: True) tn1 = TestNode() tn2 = TestNode() nn.add_child(tn1, tn1.activate) nn.add_child(tn2, tn2.activate) token = Token.valid(Fact(test='data')) nn.left_memory[token.to_info()] = 0 nn.activate_right(token) assert Token.invalid(Fact(test='data')) in tn1.added assert Token.invalid(Fact(test='data')) in tn2.added assert nn.left_memory[token.to_info()] == 1
def test_token_initialization_types(): """ Token.tag must be a Tag Token.data can be: - A Fact - An interable of Facts """ from pyknow.matchers.rete.token import Token from pyknow.fact import Fact with pytest.raises(TypeError): Token(None, Fact()) with pytest.raises(TypeError): Token(Token.TagType.VALID, None) with pytest.raises(TypeError): Token(Token.TagType.VALID, [Fact(), None]) with pytest.raises(TypeError): Token(Token.TagType.VALID, [Fact()], []) # THIS MUST NOT RAISE Token(Token.TagType.VALID, Fact(), {}) Token(Token.TagType.VALID, [Fact(), Fact()], {})
def test_conflictsetchange_get_activations_data(): from pyknow.matchers.rete.nodes import ConflictSetNode from pyknow.matchers.rete.token import Token from pyknow.rule import Rule from pyknow.fact import Fact rule = Rule() csn = ConflictSetNode(rule) f = Fact(first=1) f.__factid__ = 1 csn.activate(Token.valid(f, {'data': 'test'})) added, removed = csn.get_activations() assert len(added) == 1 assert len(removed) == 0 assert list(added)[0].rule is rule assert f in list(added)[0].facts assert list(added)[0].context == {'data': 'test'}
def test_factlist_retract(): """ Test retract method """ from pyknow.factlist import FactList from pyknow.fact import Fact flist = FactList() assert getattr(flist, "_fidx") == 0 assert not flist.facts flist.declare(Fact()) assert getattr(flist, "_fidx") == 1 assert isinstance(flist.facts[0], Fact) assert flist.retract(0) == 0 assert not flist.facts
def test_ordinarymatchnode_left_activate_invalid_remove_from_left_memory(): from pyknow.matchers.rete.nodes import OrdinaryMatchNode from pyknow.matchers.rete.token import Token from pyknow.fact import Fact omn = OrdinaryMatchNode(lambda l, r: True) fact = Fact(test='data') token = Token.valid(fact) omn.left_memory.append(token.to_info()) omn.activate_left(Token.invalid(fact)) assert not omn.left_memory
def test_default_is_and(): """ Test that AND is the default operator """ from collections import defaultdict from pyknow.engine import KnowledgeEngine from pyknow.rule import Rule from pyknow.fact import Fact, L executions = [] 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') ke_ = Test() to_declare = [] for i in range(1, 10): to_declare.append(L(i)) to_declare = dict(enumerate(to_declare)) for k, n in to_declare.items(): ke_.deffacts(Fact(something=n)) ke_.reset() results = defaultdict(list) for activation in ke_.agenda.activations: results[''.join([str(to_declare[a - 1].resolve()) for a in activation.facts])].append(1) assert dict(results) == {'3': [1], '12': [1]} assert len(ke_.agenda.activations) == 2 ke_.run() assert executions.count('rule1') == 1 assert executions.count('rule2') == 1
def test_featuretesternode_pass_fact_to_matcher(): from pyknow.matchers.rete.nodes import FeatureTesterNode from pyknow.matchers.rete.token import Token from pyknow.fact import Fact fact = Fact(this_is_my_fact=True) def _matcher(f): nonlocal fact assert f is fact ftn = FeatureTesterNode(_matcher) ftn.activate(Token.valid(fact))
def test_ordinarymatchnode_left_activate_valid_store_in_left_memory(): from pyknow.matchers.rete.nodes import OrdinaryMatchNode from pyknow.matchers.rete.token import Token, TokenInfo from pyknow.fact import Fact omn = OrdinaryMatchNode(lambda l, r: True) assert not omn.left_memory fact = Fact(test='data') token = Token.valid(fact) omn.activate_left(token) assert TokenInfo({fact}, {}) in omn.left_memory
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_KnowledgeEngine_get_activations_returns_activations(): from pyknow.engine import KnowledgeEngine from pyknow.rule import Rule from pyknow.fact import Fact, L class Test(KnowledgeEngine): # pylint: disable=too-few-public-methods @Rule(Fact(a=L(1))) def test(self): # pylint: disable=no-self-use pass ke = Test() ke.deffacts(Fact(a=L(1))) ke.reset() activations = list(ke.get_activations()) assert len(activations) == 1
def test_featuretesternode_pass_when_callable_fail(TestNode): from pyknow.matchers.rete.nodes import FeatureTesterNode from pyknow.matchers.rete.token import Token from pyknow.fact import Fact ftn = FeatureTesterNode(lambda f: False) tn1 = TestNode() tn2 = TestNode() token = Token.valid(Fact(test=True)) ftn.add_child(tn1, tn1.activate) ftn.add_child(tn2, tn2.activate) ftn.activate(token) assert tn1.added == tn2.added == []
def test_featuretesternode_pass_when_callable_modify_context(TestNode): from pyknow.matchers.rete.nodes import FeatureTesterNode from pyknow.matchers.rete.token import Token from pyknow.fact import Fact ftn = FeatureTesterNode(lambda f: {'something': True}) tn1 = TestNode() tn2 = TestNode() token = Token.valid(Fact(test=True), {'something': False}) ftn.add_child(tn1, tn1.activate) ftn.add_child(tn2, tn2.activate) ftn.activate(token) newtoken = token.copy() newtoken.context['something'] = True assert tn1.added == tn2.added == []
def test_token_initialization_conversions(): """ Token.data is modified in this way: - If is a Fact: Converted to a Set containing this Fact. - An interable of Facts: Converter to a set of Facts. """ from pyknow.matchers.rete.token import Token from pyknow.fact import Fact t1 = Token(Token.TagType.VALID, Fact()) assert t1.data == {Fact()} t2 = Token(Token.TagType.VALID, [Fact(a=1), Fact(b=2)]) assert t2.data == {Fact(a=1), Fact(b=2)}
def test_rule_with_NOT_testce(): from pyknow.rule import Rule, NOT from pyknow.factlist import FactList from pyknow.fact import Fact, InitialFact, L, T r = Rule(Fact(a=L(1)), NOT(Fact(b=T(lambda c, x: x.startswith('D'))))) 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('David'))) activations = r.get_activations(fl) assert len(list(activations)) == 0 fl = FactList() fl.declare(InitialFact()) fl.declare(Fact(a=L(1))) fl.declare(Fact(b=L('Penelope'))) activations = r.get_activations(fl) assert len(list(activations)) == 1
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)))
def test_T_resolve(): """ Test T Resolve """ from pyknow.fact import Fact, T, L assert T(lambda c, x: x).resolve(Fact(foo=L("foo")), "foo", {}) == "foo"
class Test(KnowledgeEngine): # pylint: disable=too-few-public-methods @Rule(Fact(a=L(1))) def test(self): # pylint: disable=no-self-use pass
def test_fact_freeze_mutable_values(): f = Fact([1, 2, 3]) assert f[0] == (1, 2, 3)
def test_empty_fact_match_all(kwargs): """ Empty fact matches against all. InitialFact should be this case """ from pyknow.fact import Fact, L fact = Fact(**{a: L(b) for a, b in kwargs.items()}) assert Fact().matches(fact, {})
def test_fact_setitem_does_not_raise_before_declare(): f = Fact() f[0] = 1 assert f[0] == 1
def test_ordinarymatchnode_left_activate_invalid_build_new_tokens(TestNode): from pyknow.matchers.rete.nodes import OrdinaryMatchNode from pyknow.matchers.rete.token import Token from pyknow.fact import Fact omn = OrdinaryMatchNode(lambda l, r: True) tn1 = TestNode() tn2 = TestNode() omn.add_child(tn1, tn1.activate) omn.add_child(tn2, tn2.activate) rt1 = Token.valid(Fact(rightdata='rightdata1')) rt2 = Token.valid(Fact(rightdata='rightdata2')) omn.right_memory.append(rt1.to_info()) omn.right_memory.append(rt2.to_info()) token = Token.invalid(Fact(leftdata='leftdata')) omn.activate_left(token) assert tn1.added == [Token.invalid([Fact(leftdata='leftdata'), Fact(rightdata='rightdata1')]), Token.invalid([Fact(leftdata='leftdata'), Fact(rightdata='rightdata2')])] assert tn2.added == [Token.invalid([Fact(leftdata='leftdata'), Fact(rightdata='rightdata1')]), Token.invalid([Fact(leftdata='leftdata'), Fact(rightdata='rightdata2')])]
def always_run(self): executions.append(1) self.parent.declare(Fact(inherited=L(True)))
def test_Rule_nesting_issubclass(): """ This actually tests that nesting a Rule is permitted. Rule nesting can be useful for meta-stuff, and """ from pyknow.rule import Rule from pyknow.factlist import FactList from pyknow.fact import Fact, L r1 = Rule(Fact(a=L(1)), Fact(b=L(2)), Fact(c=L(3))) r2 = Rule(Fact(a=L(1)), Rule(Fact(b=L(2)), Fact(c=L(3)))) r3 = Rule(Fact(a=L(1)), Rule(Fact(b=L(2)), Rule(Fact(c=L(3))))) r4 = Rule(Rule(Fact(a=L(1))), Rule(Fact(b=L(2))), Rule(Fact(c=L(3)))) r5 = Rule(Rule(Fact(a=L(1)), Fact(b=L(2)), Fact(c=L(3)))) fl = FactList() fl.declare(Fact(a=L(1))) fl.declare(Fact(b=L(2))) fl.declare(Fact(c=L(3))) for r in (r1, r2, r3, r4, r5): activations = list(r.get_activations(fl)) assert len(activations) == 1 assert {0, 1, 2} == set(activations[0].facts)
def always_run(self): executions.append(1) self.parent.retract_matching(Fact(initial=L(True))) self.parent.declare(Fact(inherited=L(True)))