Coder Social home page Coder Social logo

pyknow's People

Contributors

johnnywalls avatar jparedes-buguroo avatar nilp0inter avatar xayon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyknow's Issues

watch() raises KeyError

STEPS TO REPRODUCE:

Using https://github.com/buguroo/pyknow/blob/develop/docs/examples/maximum.ipynb

watch(watchers.RULES, watchers.FACTS, watchers.ACTIVATIONS)
m.run()
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-4-b1cd45e85340> in <module>
----> 1 watch(watchers.RULES, watchers.FACTS, watchers.ACTIVATIONS)
      2 m.run()

~/Documents/github/pyknow/pyknow/pyknow/watchers.py in watch(level, *what)
     39 
     40     for watcher_name in what:
---> 41         watcher = globals()[watcher_name]
     42         watcher.setLevel(level)
     43 

KeyError: <Logger pyknow.watchers.RULES (WARNING)>

Load rule set dynamically in KnowledgeEngine

Hi,
I am new to PyKnow and I am attempting to make a functionnality that will allow me to load the set of Rules from an external file.
I tried to use set_attr but without success the interpreter doesn't like the RULE decorator. Do you know a manner to do it properly ?
Thank you in advance for your time and consideration

## Extern file 
  @Rule(Light(color='green'))
     def green_light(self):
        print("Walk")
    
     @Rule(Light(color='red'))
     def red_light(self):
         print("Don't walk")
###########################
from pyknow import *

class Light(Fact):
    """Info about the traffic light."""
    pass


class RobotCrossStreet(KnowledgeEngine):
  # no rules hardcoded 


if __name__ == '__main__':
    engine = RobotCrossStreet()
    engine.reset()
    engine.declare(Light(color="green"))
    engine.run()

Performance question

I have implemented following test application for measuring performance of pyknow.

import time
from pyknow import *

class Engine(KnowledgeEngine):
    
    def __init__(self, fact_count, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fact_count = fact_count
        self.matched = 0
        self.not_matched = 0

    @DefFacts()
    def _init(self):
        for i in range(self.fact_count):
            yield Fact(i,i%10)
 
    @Rule(Fact(MATCH.a, MATCH.a))
    def rule1(self):
        self.matched += 1
        
    @Rule(Fact(MATCH.a, ~MATCH.a))
    def rule2(self):
        self.not_matched += 1

def run(fact_count):
    start = time.time()
    e = Engine(fact_count)
    e.reset()
    e.run()
    end = time.time()
    print(end - start)

When I run it with following fact counts:

run(10)
run(100)
run(500)
run(1000)
run(10000)

I am getting these results for execution time in seconds:

0.00998544692993164
0.08772945404052734
1.528554916381836
5.943406105041504
742.3850135803223

The problem is that the execution time becomes a bottleneck very quickly in my case when there are around 10000 facts. This was simplified example (and in principle this could be easily implemented without pyknow) but I would think that 10000 facts should not be a big issue when the rules are so simple.

If I remove @Rule(Fact(MATCH.a, ~MATCH.a)) which is fired more often than the other one, then execution time stays quite short (max 11 seconds).

So is this known issue or do I just misunderstanding something?

ORFC misjudge when first option is True, and second option is keep going

hi, buguroo.
I have another question about the pyknow.
When I use "&" it can work well and won't go while first option is True.
However . I use "|" it can't work well, while first option is True.

from pyknow import *

class User(Fact):
    pass

class BaseRule(KnowledgeEngine):
    array = []

    @DefFacts()
    def get_facts(self):
        yield User(d=None)

    @Rule(User(d=P(lambda x: x is not None) & P(lambda x: x >= 20)))
    def pas(self):
        print('pass')

    @Rule(User(d=P(lambda x: x is None) | P(lambda x: x >= 20)))
    def err(self):
        print('error')

engine=BaseRule()
engine.reset()
engine.run()

thanks

Not import pyknow in shell of Python 2, 3 and 3.6

Hello,

I can install by pip the module, but in the shell of python 2, I can not import the module. I received:

>>> import pyknow
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/pyknow/__init__.py", line 2, in <module>
    from .engine import KnowledgeEngine
  File "/usr/local/lib/python2.7/dist-packages/pyknow/engine.py", line 10, in <module>
    from pyknow import abstract
  File "/usr/local/lib/python2.7/dist-packages/pyknow/abstract.py", line 8
    class Matcher(metaclass=abc.ABCMeta):

In python3:

>>> import pyknow
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'pyknow'
 in python3.6:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'pyknow'
Error in sys.excepthook:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/apport_python_hook.py", line 63, in apport_excepthook
    from apport.fileutils import likely_packaged, get_recent_crashes
  File "/usr/lib/python3/dist-packages/apport/__init__.py", line 5, in <module>
    from apport.report import Report
  File "/usr/lib/python3/dist-packages/apport/report.py", line 30, in <module>
    import apport.fileutils
  File "/usr/lib/python3/dist-packages/apport/fileutils.py", line 23, in <module>
    from apport.packaging_impl import impl as packaging
  File "/usr/lib/python3/dist-packages/apport/packaging_impl.py", line 23, in <module>
    import apt
  File "/usr/lib/python3/dist-packages/apt/__init__.py", line 23, in <module>
    import apt_pkg
ModuleNotFoundError: No module named 'apt_pkg'

Original exception was:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'pyknow'
>>> 

Thank you for your effort and kind regards.

Return of value

Hi Buguroo,

Now I am using Pyknow to do a calculation, and can i ask how to return a value out of pyknow so I can use as part of web application?

Current only print is working.

Thanks

Thank you so much!

Thank you so much!
This completely fixed my problem. My initialization time went from 12 minutes to 1 second, and my reset time also went from 12 minutes to 1 second. The time to run the engine, declare facts, and reset the engine after it has been run once have all improved from approximately 30 seconds to near instantaneous.
Thank you again and have a nice day!

Originally posted by @ab612 in #7 (comment)

Matching list content using `CONTAINS`

Is there a way for a rule to match against the content of a list (contains or in). In the following example, if parameters is a list and I want to match a bottle that's mentioned in that list, how do i do it?

@freeze.register(list)
def freeze_list(l):
    return ";".join(l)

class Bottle(Fact):
    name = Field(str, default=None)

class Action(Fact):
    name = Field(str, default=None)
    parameters = Field(list, default=[])

@Rule(AND(Bottle(name=MATCH.name),
              Action(parameters=MATCH.params & CONTAINS(MATCH.name))))

I'm getting the following error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/mbehery/workspace/science/behery2019graph/scripts/world_parser.py", line 95, in <module>
    class Kitchen(KnowledgeEngine):
  File "/home/mbehery/workspace/science/behery2019graph/scripts/world_parser.py", line 158, in Kitchen
    Action(parameters=MATCH.params & CONTAINS(MATCH.name))))
  File "/usr/local/lib/python3.6/site-packages/pyknow/operator.py", line 45, in __from_operator2
    "A ConditionalElement can't be used as an operator condition.")
TypeError: A ConditionalElement can't be used as an operator condition.

Note: If I match against a string it's working as follows:

@Rule(AND(Bottle(name=MATCH.name),
              Action(parameters=MATCH.params & CONTAINS("bottle"))))

Little issue with PyPi

Hello,
Your project doesn't exist anymore on PyPi. I don't know if it's wanted or not, but I let this here. I'm trying to build container and Pip crash when it come to install PyKnow from PyPi. And there is a 404 on the project page.
It would be easier if you can do something about it, if not I'll build the container without and do a manual pull from this repo inside the container.
2
Capture

Rules called on already retracted facts

Rules are being called on already retracted facts.

P = [[None, 2, None, 6, None, 8, None, None, None],
     [5, 8, None, None, None, 9, 7, None, None],
     [None, None, None, None, 4, None, None, None, None],
     [3, 7, None, None, None, None, 5, None, None],
     [6, None, None, None, None, None, None, None, 4],
     [None, None, 8, None, None, None, None, 1, 3],
     [None, None, None, None, 2, None, None, None, None],
     [None, None, 9, 8, None, None, None, 3, 6],
     [None, None, None, 3, None, 6, None, 9, None]]


class Possible(Fact):
    pass


class Solver(KnowledgeEngine):
    @DefFacts()
    def init_puzzle(self):
        for x, row in enumerate(P):
            for y, cell in enumerate(row):
                block = ((y // 3) * 3) + (x // 3)
                if cell is None:
                    yield Fact(value=None, y=y, x=x, block=block)
                    for i in range(1, 10):
                        yield Possible(value=i, y=y, x=x, block=block)
                else:
                    yield Fact(value=cell, y=y, x=x, block=block)


    @Rule(Fact(value=~L(None) & MATCH.v, y=MATCH.y),
          AS.p << Possible(value=MATCH.v, y=MATCH.y))
    def discarded_by_column(self, p):
        self.retract(p)

    @Rule(Fact(value=~L(None) & MATCH.v, x=MATCH.x),
          AS.p << Possible(value=MATCH.v, x=MATCH.x))
    def discarded_by_row(self, p):
        self.retract(p)

    @Rule(Fact(value=~L(None) & MATCH.v, block=MATCH.b),
          AS.p << Possible(value=MATCH.v, block=MATCH.b))
    def discarded_by_block(self, p):
        self.retract(p)

    @Rule(AS.cell << Fact(value=None, x=MATCH.x, y=MATCH.y, block=MATCH.b),
          Possible(value=MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b),
          NOT(Possible(value=~MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b)))
    def only_one_possible(self, cell, v):
        self.retract(cell)
        self.declare(Fact(value=v, x=cell['x'], y=cell['y'], block=cell['block']))

    @Rule(AS.cell << Fact(value=None, x=MATCH.x, y=MATCH.y, block=MATCH.b),
          Possible(value=MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b),
          NOT(Possible(value=MATCH.v, x=~MATCH.x, y=~MATCH.y, block=MATCH.b)))
    def unique_candidate_block(self, cell, v):
        self.retract(cell)
        self.declare(Fact(value=v, x=cell['x'], y=cell['y'], block=cell['block']))

    @Rule(AS.cell << Fact(value=None, x=MATCH.x, y=MATCH.y, block=MATCH.b),
          Possible(value=MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b),
          NOT(Possible(value=MATCH.v, x=~MATCH.x, y=MATCH.y, block=~MATCH.b)))
    def unique_candidate_col(self, cell, v):
        self.retract(cell)
        self.declare(Fact(value=v, x=cell['x'], y=cell['y'], block=cell['block']))

    @Rule(AS.cell << Fact(value=None, x=MATCH.x, y=MATCH.y, block=MATCH.b),
          Possible(value=MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b),
          NOT(Possible(value=MATCH.v, x=MATCH.x, y=~MATCH.y, block=~MATCH.b)))
    def unique_candidate_row(self, cell, v):
        self.retract(cell)
        self.declare(Fact(value=v, x=cell['x'], y=cell['y'], block=cell['block']))

    @Rule(Fact(value=~L(None) & MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b),
          AS.p << Possible(value=~MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b))
    def remove_other_candidates(self, p):
        self.retract(p)


watch('RULES', 'FACTS')
s = Solver()
s.reset()
s.run()

This code raises the following exception:

INFO:pyknow.watchers.FACTS: <== <f-130>: Possible(value=2, y=1, x=2, block=0)
INFO:pyknow.watchers.RULES:FIRE 531 unique_candidate_col: <f-130>, <f-128>
Traceback (most recent call last):
  File "sudoku.py", line 95, in <module>
    s.run()
  File "/home/nil/.local/share/virtualenvs/sudoku-VtHvu9jk/lib/python3.7/site-packages/pyknow/engine.py", line 168, in run
    for k, v in activation.context.items()
  File "/home/nil/.local/share/virtualenvs/sudoku-VtHvu9jk/lib/python3.7/site-packages/pyknow/rule.py", line 87, in __call__
    return self._wrapped(*args, **kwargs)
  File "sudoku.py", line 76, in unique_candidate_col
    self.retract(cell)
  File "/home/nil/.local/share/virtualenvs/sudoku-VtHvu9jk/lib/python3.7/site-packages/pyknow/engine.py", line 124, in retract
    self.facts.retract(idx_or_declared_fact)
  File "/home/nil/.local/share/virtualenvs/sudoku-VtHvu9jk/lib/python3.7/site-packages/pyknow/factlist.py", line 111, in retract
    raise IndexError('Fact not found.')
IndexError: Fact not found.

A similar exception is raised if the method modify is used instead of retract+declare.

Nested Facts

Suppose I have a Message(Fact) and message can contain both an attachment and a body, both of which share properties, e.g. is_spam.
It would be lovely to model my facts starting with a att = Text(spam=0), and bdy = Text(spam=1) and then create a Message(att=att, bdy=bdy) fact.

Is this behavior supported/recommended?

Got a AttributeError: 'Rule' object has no attribute '__name__'

Background, I am new to Pyknow. I want to build a expert system to auto calculate a cell quality score based on some parameters. The score algorithm should be loaded from a json data, such like:

	{
		"PN": [{
			"condition": "pn",
			"symbol": "=",
			"value": "2PN",
			"score": "20",
			"weight": "1.0"
		}, {
			"condition": "time",
			"symbol": "<=",
			"value": "12",
			"score": "20",
			"weight": "1.0"
		}],
		"4C": [{
			"condition": "cell",
			"symbol": "=",
			"value": "4C",
			"score": "20",
			"weight": "1.0"
		}, {
			"condition": "even",
			"symbol": "=",
			"value": true,
			"score": "20",
			"weight": "1.0"
		}, {
			"condition": "fragment",
			"symbol": "=",
			"value": "<5%",
			"score": "20",
			"weight": "1.0"
		}, {
			"condition": "fragment",
			"symbol": "=",
			"value": "5%-10%",
			"score": "15",
			"weight": "1.0"
		}, {
			"condition": "fragment",
			"symbol": "=",
			"value": "10%-20%",
			"score": "10",
			"weight": "1.0"
		}, {
			"condition": "time",
			"symbol": "<=",
			"value": "36",
			"score": "20",
			"weight": "1.0"
		}]
	}

Here are my codes for this,

	from pyknow import *
	from functools import partial

	class EmbryoScore(KnowledgeEngine):
	    score = 0
	    @classmethod
	    def removeAllRules(cls):
	        for m in [attr for attr in cls.__dict__ if attr.startswith('rule')]:
	            delattr(cls, m)

	def parse_json_rules(rule_json):
	    import json
	    rules = json.loads(rule_json)
	    sal = 100
	    index = 0
	    for stage in rules:
	        for rule_item in rules[stage]:
	            rule = {'condition': rule_item['condition']}
	            rule['stage'] = stage
	            if rule_item['symbol'] not in ('=', '<', '<=', '>', '>='):
	                continue
	            def _cond(value, symbol):
	                if symbol == '=': return EQ(value)
	                if symbol == '<': return LT(value)
	                if symbol == '<=': return LE(value)
	                if symbol == '>': return GT(value)
	                if symbol == '>=': return GE(value)

	            def _add(self, **kwargs):
	                if 'score' not in kwargs or 'weight' not in kwargs:
	                    raise ValueError('No score or weight specified')
	                s, w = int(kwargs['score']), float(kwargs['weight'])
	                self.score += s * w
	        
	            R = Rule(AND(Fact(**rule, value=MATCH.value & 
                        _cond(rule_item['value'], rule_item['symbol']))),
	                    salience=sal)(partial(_add, score=rule_item['score'], 
                        weight=rule_item['weight']))
	            setattr(EmbryoScore, f'rule{index}', R)
	            index += 1
	            sal -= 1

	def init_engine(rule_json):
	    EmbryoScore.removeAllRules()
	    parse_json_rules(rule_json)
	    engine = EmbryoScore()
	    engine.reset()
	    return engine

	import unittest

	class ScoreTest(unittest.TestCase):
		def test(self):
			engine = init_engine(rule_json)
			engine.declare(Fact(condition='pn', stage='PN', value='2PN'))
			engine.declare(Fact(stage='4C', condition='cell', value='4C'))
			engine.declare(Fact(stage='4C', condition='fragment', value='10%-20%'))
			engine.declare(Fact(stage='4C', condition='time', value='32'))
			engine.run()
			self.assertEqual(engine.score, 70)


	if __name__ == '__main__':
	    unittest.main()

It raises a AttributeError: 'Rule' object has no attribute 'name' when engine.run() on engine.py line 162

	watchers.RULES.info(
         "FIRE %s %s: %s",
         execution,
         activation.rule.__name__,
         ", ".join(str(f) for f in activation.facts))

I can't find out what the problem is. Wonder if I can get some help here. THANKS.

Do not call `__declare` for each fact but only once.

For each yielded fact on DefFacts constructs the engine is calling __declare while it can be called only once with a composed generator, resulting in only one __update_agenda call instead of n.

pyknow/pyknow/engine.py

Lines 189 to 192 in 1aed48f

# Declare all deffacts
for deffact in self.get_deffacts():
for fact in deffact():
self.__declare(fact)

This is related to #12.

what can i do if i want the return value of the P function ?

Hi! @nilp0inter, thank you very much for your library, when i use this library i have some questions that i need your help.

here is my code

from pyknow import *

def rule_hk(pat_args=None,arg_dict=None):

    for k,v in arg_dict.items():
        if pat_args in v:
            return True,'some message: {}'.format(v)

    return False,'nothing happend'

class Greetings(KnowledgeEngine):

    def __init__(self):
        super().__init__()

    @property
    def input_facts(self):
        return self._facts

    @input_facts.setter
    def input_facts(self, facts):
        self._facts = facts

    @DefFacts()
    def _initial_action(self):
        for fact in self._facts:
            yield fact

    def excute(self, facts):
        self.input_facts = facts
        self.reset()
        self.run()

def load_rule_to_engine(rule_dict_list):
    for index, rule_dict in enumerate(rule_dict_list):
        R = Rule(rule_dict)(return_invalid_msg(ruledata=rule_dict))
        setattr(Greetings, f'rule{index}', R)

def return_invalid_msg(ruledata):
    def pk_d(self, **kwargs):
        print('bingo!')
        return ruledata

    return pk_d

x = 1

arg_dict = {
    'a':[1,2,3],
    'b':[7,8,9]
}

Rules = [Fact(hk=P(lambda x: rule_hk(pat_args=x, arg_dict=arg_dict)))]

load_rule_to_engine(Rules)

engine = Greetings()

fact = Fact(hk=1)

facts = [ fact ]

engine.excute(facts)

I want the P return value 'some message:' ๏ผŒbut it seems that the return value will not be returned after the rule is executed. The return value just seems to be used to determine whether the rule is hitting. Can P returnvalue appear in return_invalid_msg function? How can i do ๏ผŸ(very sorry for my bad english)

Thanks๏ผ

Improve complexity of `update_agenda`.

update_agenda should work in place on activations instead of making copies and comparing every time is called.

# Resolve conflicts using the appropiate strategy.
new_activations = deque(self._update_agenda(agenda, added, removed))
if new_activations != agenda.activations:
agenda.activations = new_activations

Related to #12.

Suggest to loosen the dependency on schema

Hi, your project pyknow requires "schema==0.6.7" in its dependency. After analyzing the source code, we found that the following versions of schema can also be suitable without affecting your project, i.e., schema 0.5.0, 0.6.0, 0.6.1, 0.6.2, 0.6.3, 0.6.4, 0.6.5, 0.6.6, 0.6.8. Therefore, we suggest to loosen the dependency on schema from "schema==0.6.7" to "schema>=0.5.0,<=0.6.8" to avoid any possible conflict for importing more packages or for downstream projects that may use pyknow.

May I pull a request to further loosen the dependency on schema?

By the way, could you please tell us whether such dependency analysis may be potentially helpful for maintaining dependencies easier during your development?



We also give our detailed analysis as follows for your reference:

Your project pyknow directly uses 1 APIs from package schema.

schema.Schema.__init__

Beginning from the 1 APIs above, -1 functions are then indirectly called, including -1 schema's internal APIs and 0 outsider APIs. The specific call graph is listed as follows (neglecting some repeated function occurrences).

[/buguroo/pyknow]
+--schema.Schema.__init__

We scan schema's versions and observe that during its evolution between any version from [0.5.0, 0.6.0, 0.6.1, 0.6.2, 0.6.3, 0.6.4, 0.6.5, 0.6.6, 0.6.8] and 0.6.7, the changing functions (diffs being listed below) have none intersection with any function or API we mentioned above (either directly or indirectly called by this project).

diff: 0.6.7(original) 0.5.0
['schema.schema.SchemaError.code', 'schema.schema.Const.validate', 'schema.test_schema.test_dict_key_error', 'schema.test_schema.test_strictly', 'schema.schema.Optional.__hash__', 'schema.schema.SchemaForbiddenKeyError', 'schema.schema.And.validate', 'schema.schema.SchemaUnexpectedTypeError', 'schema.schema.Regex.validate', 'schema.test_schema.test_inheritance', 'schema.test_schema.test_schema_repr', 'schema.test_schema.test_ignore_extra_keys_validation_and_return_keys', 'schema.test_schema.test_schema_error_handling', 'schema.schema.Const', 'schema.test_schema.test_dict', 'schema.test_schema.test_ignore_extra_keys', 'schema.schema.Optional', 'schema.schema.Use', 'schema.test_schema.test_issue_9_prioritized_key_comparison_in_dicts', 'schema.schema.Regex.__init__', 'schema.test_schema.test_and_error_handling', 'schema.schema.Regex.__repr__', 'schema.test_schema.test_or_error_handling', 'schema.test_schema.ve', 'schema.schema.And.__init__', 'schema.test_schema.test_issue_56_cant_rely_on_callables_to_have_name', 'schema.test_schema.test_regex', 'schema.schema.Or', 'schema.test_schema.test_optional_key_convert_failed_randomly_while_with_another_optional_object', 'schema.schema.And', 'schema.schema.Or.validate', 'schema.schema.Forbidden.__init__', 'schema.test_schema.test_issue_9_prioritized_key_comparison', 'schema.test_schema.test_missing_keys_exception_with_non_str_dict_keys', 'schema.test_schema.test_validate_list', 'schema.test_schema.test_dict_keys', 'schema.test_schema.test_copy', 'schema.schema.SchemaMissingKeyError', 'schema.test_schema.se', 'schema.test_schema.test_complex', 'schema.test_schema.test_validate_object', 'schema.test_schema.test_dict_forbidden_keys', 'schema.schema.SchemaError', 'schema.test_schema.test_dict_subtypes', 'schema.schema.Forbidden', 'schema.schema.SchemaWrongKeyError', 'schema.test_schema.test_or', 'schema.test_schema.test_use_error_handling', 'schema.test_schema.test_validate_file', 'schema.test_schema.test_error_reporting', 'schema.schema.Regex', 'schema.test_schema.test_schema', 'schema.schema.Use.validate', 'schema.test_schema.test_nice_errors', 'schema.schema.Schema._dict_key_priority', 'schema.test_schema.test_exception_handling_with_bad_validators', 'schema.test_schema.test_and', 'schema.test_schema.test_dict_optional_keys', 'schema.schema.Schema.validate', 'schema.schema.Schema', 'schema.test_schema.test_issue_83_iterable_validation_return_type', 'schema.test_schema.test_list_tuple_set_frozenset', 'schema.test_schema.test_test', 'schema.test_schema.test_dict_optional_defaults', 'schema.schema.Optional.__eq__', 'schema.schema.SchemaError.__init__', 'schema.test_schema.test_use_json']

diff: 0.6.7(original) 0.6.0
['schema.schema.SchemaError.code', 'schema.schema.Const.validate', 'schema.test_schema.test_dict_key_error', 'schema.test_schema.test_strictly', 'schema.schema.Optional.__hash__', 'schema.schema.SchemaForbiddenKeyError', 'schema.schema.And.validate', 'schema.schema.SchemaUnexpectedTypeError', 'schema.schema.Regex.validate', 'schema.test_schema.test_inheritance', 'schema.test_schema.test_schema_repr', 'schema.test_schema.test_ignore_extra_keys_validation_and_return_keys', 'schema.test_schema.test_schema_error_handling', 'schema.schema.Const', 'schema.test_schema.test_dict', 'schema.test_schema.test_ignore_extra_keys', 'schema.schema.Optional', 'schema.schema.Use', 'schema.test_schema.test_issue_9_prioritized_key_comparison_in_dicts', 'schema.test_schema.test_and_error_handling', 'schema.test_schema.test_or_error_handling', 'schema.test_schema.ve', 'schema.schema.And.__init__', 'schema.test_schema.test_issue_56_cant_rely_on_callables_to_have_name', 'schema.test_schema.test_regex', 'schema.schema.Or', 'schema.test_schema.test_optional_key_convert_failed_randomly_while_with_another_optional_object', 'schema.schema.And', 'schema.schema.Or.validate', 'schema.schema.Forbidden.__init__', 'schema.test_schema.test_issue_9_prioritized_key_comparison', 'schema.test_schema.test_missing_keys_exception_with_non_str_dict_keys', 'schema.test_schema.test_validate_list', 'schema.test_schema.test_dict_keys', 'schema.test_schema.test_copy', 'schema.schema.SchemaMissingKeyError', 'schema.test_schema.se', 'schema.test_schema.test_complex', 'schema.test_schema.test_validate_object', 'schema.test_schema.test_dict_forbidden_keys', 'schema.schema.SchemaError', 'schema.test_schema.test_dict_subtypes', 'schema.schema.Forbidden', 'schema.schema.SchemaWrongKeyError', 'schema.test_schema.test_or', 'schema.test_schema.test_use_error_handling', 'schema.test_schema.test_validate_file', 'schema.test_schema.test_error_reporting', 'schema.schema.Regex', 'schema.test_schema.test_schema', 'schema.schema.Use.validate', 'schema.test_schema.test_nice_errors', 'schema.schema.Schema._dict_key_priority', 'schema.test_schema.test_exception_handling_with_bad_validators', 'schema.test_schema.test_and', 'schema.test_schema.test_dict_optional_keys', 'schema.schema.Schema.validate', 'schema.schema.Schema', 'schema.test_schema.test_issue_83_iterable_validation_return_type', 'schema.test_schema.test_list_tuple_set_frozenset', 'schema.test_schema.test_test', 'schema.test_schema.test_dict_optional_defaults', 'schema.schema.Optional.__eq__', 'schema.schema.SchemaError.__init__', 'schema.test_schema.test_use_json']

diff: 0.6.7(original) 0.6.1
['schema.schema.SchemaError.code', 'schema.schema.Const.validate', 'schema.test_schema.test_dict_key_error', 'schema.test_schema.test_strictly', 'schema.schema.Optional.__hash__', 'schema.schema.SchemaForbiddenKeyError', 'schema.schema.And.validate', 'schema.schema.SchemaUnexpectedTypeError', 'schema.schema.Regex.validate', 'schema.test_schema.test_inheritance', 'schema.test_schema.test_schema_repr', 'schema.test_schema.test_ignore_extra_keys_validation_and_return_keys', 'schema.test_schema.test_schema_error_handling', 'schema.schema.Const', 'schema.test_schema.test_dict', 'schema.test_schema.test_ignore_extra_keys', 'schema.schema.Optional', 'schema.schema.Use', 'schema.test_schema.test_issue_9_prioritized_key_comparison_in_dicts', 'schema.test_schema.test_and_error_handling', 'schema.test_schema.test_or_error_handling', 'schema.test_schema.ve', 'schema.schema.And.__init__', 'schema.test_schema.test_issue_56_cant_rely_on_callables_to_have_name', 'schema.test_schema.test_regex', 'schema.schema.Or', 'schema.test_schema.test_optional_key_convert_failed_randomly_while_with_another_optional_object', 'schema.schema.And', 'schema.schema.Or.validate', 'schema.schema.Forbidden.__init__', 'schema.test_schema.test_issue_9_prioritized_key_comparison', 'schema.test_schema.test_missing_keys_exception_with_non_str_dict_keys', 'schema.test_schema.test_validate_list', 'schema.test_schema.test_dict_keys', 'schema.test_schema.test_copy', 'schema.schema.SchemaMissingKeyError', 'schema.test_schema.se', 'schema.test_schema.test_complex', 'schema.test_schema.test_validate_object', 'schema.test_schema.test_dict_forbidden_keys', 'schema.schema.SchemaError', 'schema.test_schema.test_dict_subtypes', 'schema.schema.Forbidden', 'schema.schema.SchemaWrongKeyError', 'schema.test_schema.test_or', 'schema.test_schema.test_use_error_handling', 'schema.test_schema.test_validate_file', 'schema.test_schema.test_error_reporting', 'schema.schema.Regex', 'schema.test_schema.test_schema', 'schema.schema.Use.validate', 'schema.test_schema.test_nice_errors', 'schema.schema.Schema._dict_key_priority', 'schema.test_schema.test_exception_handling_with_bad_validators', 'schema.test_schema.test_and', 'schema.test_schema.test_dict_optional_keys', 'schema.schema.Schema.validate', 'schema.schema.Schema', 'schema.test_schema.test_issue_83_iterable_validation_return_type', 'schema.test_schema.test_list_tuple_set_frozenset', 'schema.test_schema.test_test', 'schema.test_schema.test_dict_optional_defaults', 'schema.schema.Optional.__eq__', 'schema.schema.SchemaError.__init__', 'schema.test_schema.test_use_json']

diff: 0.6.7(original) 0.6.2
['schema.schema.SchemaError.code', 'schema.schema.Const.validate', 'schema.test_schema.test_dict_key_error', 'schema.test_schema.test_strictly', 'schema.schema.Optional.__hash__', 'schema.schema.SchemaForbiddenKeyError', 'schema.schema.And.validate', 'schema.schema.SchemaUnexpectedTypeError', 'schema.schema.Regex.validate', 'schema.test_schema.test_inheritance', 'schema.test_schema.test_schema_repr', 'schema.test_schema.test_ignore_extra_keys_validation_and_return_keys', 'schema.test_schema.test_schema_error_handling', 'schema.schema.Const', 'schema.test_schema.test_dict', 'schema.test_schema.test_ignore_extra_keys', 'schema.schema.Optional', 'schema.schema.Use', 'schema.test_schema.test_issue_9_prioritized_key_comparison_in_dicts', 'schema.test_schema.test_and_error_handling', 'schema.test_schema.test_or_error_handling', 'schema.test_schema.ve', 'schema.schema.And.__init__', 'schema.test_schema.test_issue_56_cant_rely_on_callables_to_have_name', 'schema.test_schema.test_regex', 'schema.schema.Or', 'schema.test_schema.test_optional_key_convert_failed_randomly_while_with_another_optional_object', 'schema.schema.And', 'schema.schema.Or.validate', 'schema.schema.Forbidden.__init__', 'schema.test_schema.test_issue_9_prioritized_key_comparison', 'schema.test_schema.test_missing_keys_exception_with_non_str_dict_keys', 'schema.test_schema.test_validate_list', 'schema.test_schema.test_dict_keys', 'schema.test_schema.test_copy', 'schema.schema.SchemaMissingKeyError', 'schema.test_schema.se', 'schema.test_schema.test_complex', 'schema.test_schema.test_validate_object', 'schema.test_schema.test_dict_forbidden_keys', 'schema.schema.SchemaError', 'schema.test_schema.test_dict_subtypes', 'schema.schema.Forbidden', 'schema.schema.SchemaWrongKeyError', 'schema.test_schema.test_or', 'schema.test_schema.test_use_error_handling', 'schema.test_schema.test_validate_file', 'schema.test_schema.test_error_reporting', 'schema.schema.Regex', 'schema.test_schema.test_schema', 'schema.schema.Use.validate', 'schema.test_schema.test_nice_errors', 'schema.schema.Schema._dict_key_priority', 'schema.test_schema.test_exception_handling_with_bad_validators', 'schema.test_schema.test_and', 'schema.test_schema.test_dict_optional_keys', 'schema.schema.Schema.validate', 'schema.schema.Schema', 'schema.test_schema.test_issue_83_iterable_validation_return_type', 'schema.test_schema.test_list_tuple_set_frozenset', 'schema.test_schema.test_test', 'schema.test_schema.test_dict_optional_defaults', 'schema.schema.Optional.__eq__', 'schema.schema.SchemaError.__init__', 'schema.test_schema.test_use_json']

diff: 0.6.7(original) 0.6.3
['schema.schema.Const.validate', 'schema.test_schema.test_dict_key_error', 'schema.schema.Optional.__hash__', 'schema.schema.SchemaForbiddenKeyError', 'schema.schema.And.validate', 'schema.test_schema.test_inheritance', 'schema.test_schema.test_ignore_extra_keys_validation_and_return_keys', 'schema.schema.Const', 'schema.test_schema.test_ignore_extra_keys', 'schema.schema.Optional', 'schema.schema.And.__init__', 'schema.schema.Or', 'schema.schema.And', 'schema.schema.Or.validate', 'schema.schema.Forbidden.__init__', 'schema.test_schema.test_copy', 'schema.test_schema.test_dict_forbidden_keys', 'schema.schema.SchemaError', 'schema.schema.Forbidden', 'schema.schema.Schema._dict_key_priority', 'schema.test_schema.test_dict_optional_keys', 'schema.schema.Schema.validate', 'schema.schema.Schema', 'schema.test_schema.test_test', 'schema.schema.Optional.__eq__', 'schema.schema.SchemaError.__init__']

diff: 0.6.7(original) 0.6.4
['schema.schema.Const.validate', 'schema.test_schema.test_dict_key_error', 'schema.schema.Optional.__hash__', 'schema.schema.SchemaForbiddenKeyError', 'schema.schema.And.validate', 'schema.test_schema.test_inheritance', 'schema.test_schema.test_ignore_extra_keys_validation_and_return_keys', 'schema.schema.Const', 'schema.test_schema.test_ignore_extra_keys', 'schema.schema.Optional', 'schema.schema.And.__init__', 'schema.schema.Or', 'schema.schema.And', 'schema.schema.Or.validate', 'schema.schema.Forbidden.__init__', 'schema.test_schema.test_copy', 'schema.test_schema.test_dict_forbidden_keys', 'schema.schema.SchemaError', 'schema.schema.Forbidden', 'schema.schema.Schema._dict_key_priority', 'schema.test_schema.test_dict_optional_keys', 'schema.schema.Schema.validate', 'schema.schema.Schema', 'schema.test_schema.test_test', 'schema.schema.Optional.__eq__', 'schema.schema.SchemaError.__init__']

diff: 0.6.7(original) 0.6.5
['schema.schema.Const.validate', 'schema.test_schema.test_dict_key_error', 'schema.schema.Optional.__hash__', 'schema.schema.SchemaForbiddenKeyError', 'schema.schema.And.validate', 'schema.test_schema.test_inheritance', 'schema.test_schema.test_ignore_extra_keys_validation_and_return_keys', 'schema.schema.Const', 'schema.test_schema.test_ignore_extra_keys', 'schema.schema.Optional', 'schema.schema.And.__init__', 'schema.schema.Or', 'schema.schema.And', 'schema.schema.Or.validate', 'schema.schema.Forbidden.__init__', 'schema.test_schema.test_dict_forbidden_keys', 'schema.schema.Forbidden', 'schema.schema.Schema._dict_key_priority', 'schema.test_schema.test_dict_optional_keys', 'schema.schema.Schema.validate', 'schema.schema.Schema', 'schema.test_schema.test_test', 'schema.schema.Optional.__eq__']

diff: 0.6.7(original) 0.6.6
['schema.schema.Schema._dict_key_priority', 'schema.schema.Optional', 'schema.schema.Const.validate', 'schema.test_schema.test_dict_optional_keys', 'schema.schema.SchemaMissingKeyError', 'schema.schema.Optional.__hash__', 'schema.schema.Schema.validate', 'schema.schema.SchemaForbiddenKeyError', 'schema.schema.Schema', 'schema.test_schema.test_dict_forbidden_keys', 'schema.test_schema.test_test', 'schema.schema.Forbidden.__init__', 'schema.schema.Forbidden', 'schema.schema.Const', 'schema.schema.Optional.__eq__']

diff: 0.6.7(original) 0.6.8
['schema.schema.SchemaMissingKeyError', 'schema.schema.Or', 'schema.schema.Schema', 'schema.schema.Or.validate', 'schema.schema.Schema.is_valid']

Therefore, we believe that it is quite safe to loose your dependency on schema from "schema==0.6.7" to "schema>=0.5.0,<=0.6.8". This will improve the applicability of pyknow and reduce the possibility of any further dependency conflict with other projects.

Explanation Module

Wondering if there's any way to implement an explanation module to show reasoning to the end user and if there's any utility in the framework to make it straightforward.

Include dependencies within the project

Our dependencies (schema and frozendict) are stable and small enough to be included within the source code of pyknow.

Including dependencies along with the project source eliminate danger of interdependency version conflict and guarantee that the exact dependency version is tested with the project.

Exporting and importing the rules to and from PyKnow

Hi,
I want to export and import the rules from some JSON format or XML format to PyKnow engine.
I wan to create the UI on top of it to allow user to edit the rules and facts. I was thinking of writing custom parser on top of the rules files
Any help will be appreciated.
Thanks

externalizing rules say, into XML?

Dear Authors,

I find this module is very promising and I loved the way it works. I had a quick question as to how I can externalize the rules into file. The idea is to extend he flexibility to the user so that he can help himself to decide what rule criteria and matching inference should be...

Any directions in that respect? BTW, I would love to see this fantastic toolkit get some more attention in the python community... I am more than happy to help in anyway I can.

Thanks,
Tharma

python 2.7 compatability issue (abc.ABCMeta)

from pyknow import *
results in a SyntaxError in python 2.7.6

File "/usr/local/lib/python2.7/dist-packages/pyknow/abstract.py", line 8
    class Matcher(metaclass=abc.ABCMeta):
                           ^
SyntaxError: invalid syntax

please let me know if this is considered a bug (i.e is python 2.7 supported)

thanks for your assistance,

sinai

Current time

Hi,

I am using PyKnow in my school, the problem I have is how to get the current time in Pyknow. I am using Jupyter notebook

Thanks!

Can I user < or > in @Rule?

it seems I can only do such things like this:

class RobotCrossStreet(KnowledgeEngine):
    @Rule('num' << NumberClass(amount=1000) ))
    def cautious(self, num):
         if(num>100)
               doge(num)
         else
               dolte(num)

Implement the template system

The template system will allow Fact subclasses to have a schema. A schema is a set of fields names and associates types, allowing the engine to validate facts structure.

Log each activation only if is needed

pyknow/pyknow/engine.py

Lines 143 to 148 in 1aed48f

for idx, act in enumerate(self.agenda.activations):
watchers.AGENDA.debug(
"%d: %r %r",
idx,
act.rule.__name__,
", ".join(str(f) for f in act.facts))

Place an if statement before this loop to not traverse the entire activation list in case debug is not enable. This will reduce complexity here from O(n) to O(1).

Related to #12 .

class Fact unsupport unhashable type

I want to use an array in class Fact as a value, But it raise an TypeError

  File "/usr/local/lib/python3.6/site-packages/pyknow/engine.py", line 206, in __declare
    last_inserted = self.facts.declare(fact)
  File "/usr/local/lib/python3.6/site-packages/pyknow/factlist.py", line 69, in declare
    fact_id = self._get_fact_id(fact)
  File "/usr/local/lib/python3.6/site-packages/pyknow/factlist.py", line 47, in _get_fact_id
    for k, v in fact.items()
TypeError: unhashable type: 'list'

it seems that python3 frozenset() unsupport unhashable type
How can I avoid it , thx~

Improve DeepthStrategy complexity.

This method of sorting activations is terribly inefficient:

sorted_activations = sorted(
enumerate(activations),
key=lambda x: (x[1].rule.salience,
sorted((f['__factid__'] for f in x[1].facts),
reverse=True)),
reverse=True)
return (act for _, act in sorted_activations)

A better method maybe is maintaining the list sorted, perhaps using the bisect module.

This is related to #12.

Long execution time

Hi! I am trying to use pyknow to my application to fire the rule according to my facts. I have followed the pyknow rule creation process to create my rules. But the problem is when I run the application, it is taking the infinite time to execute. For example, I ran the application last night and it is still in running process now. But if I just remove pyknow it is running and working. I am doing it in pycharm and not getting the idea why is it taking so much time. I have found that it is not because of the rules what I written, it is because of the import line code " from pyknow import * ". Could you please suggest me why this is happening?

Fix freeze() and unfreeze()

  • freeze() does not recursivelly process frozen objects
  • unfreeze() does not recursivelly process unfrozen objects

Long Initialization Time

Hello,

Thank you for developing this module. I was very excited when I first found it. Currently, I am trying to develop a forecasting tool using it. However, I've noticed that the creation and initial reset of a Knowledge Engine takes a lot of time. About 25 minuets for 10 fact classes and around 30 rules (on a very average computer). Is this to be expected? Please let me know. I have a feeling I'm missing something.

Thank you & Keep up the good work!
Madison S

Is it needed for creating a shared OrdinaryMatchNode?

I checked the source code for creating OrdinaryMatchNode at https://github.com/buguroo/pyknow/blob/develop/pyknow/matchers/rete/utils.py#L167-L186

And compared it to the paper's figure 2.2(a) (matched for C1^C2).

Is it needed for creating a shared OrdinaryMatchNode If two conjunctive normal form has the same facts?

for example,
Rule(OR(AND(Fact(a=1), Fact(b=2), Fact(c=3)), AND(Fact(a=1), Fact(b=2), Fact(c=4))))
It seems that two OrdinaryMatchNode node for (Fact(a=1), Fact(b=2)) will be created.
Is it right?
or should we need a same OrdinaryMatchNode for (Fact(a=1), Fact(b=2))?

retract not working

Hi I'm trying to use pyknow. I'm able to declare facts with no problems, but I'm unable to retract them
I'm using the following code:

class bound(Fact):
    obj = Field(str, default=None)
    manip = Field(str, default="arm")\


class Kitchen(KnowledgeEngine):
    """ some code goes here """
    pass

kitchen = Kitchen()
kitchen.reset()
kitchen.declare(bound(obj="bottle", manip="arm"))
kitchen.retract(bound(obj="bottle", manip="arm"))

But I'm getting an IndexError

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.6/site-packages/pyknow/engine.py", line 124, in retract
    self.facts.retract(idx_or_declared_fact)
  File "/usr/local/lib/python3.6/site-packages/pyknow/factlist.py", line 111, in retract
    raise IndexError('Fact not found.')
IndexError: Fact not found.

If I retract using the index it's working fine, but the documentation says:

Both, the index and the fact can be used with retract

Am I missing something here?
Thanks :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.