diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 0000000..7f453c0 --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,39 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Python application + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest diff --git a/pytholog/fact.py b/pytholog/fact.py index 42c1bf9..4bedaea 100644 --- a/pytholog/fact.py +++ b/pytholog/fact.py @@ -34,4 +34,7 @@ def __repr__ (self) : def __lt__(self, other): return self.lh.terms[self.lh.index] < other.lh.terms[other.lh.index] + + def __eq__(self, other): + return self.fact == other.fact \ No newline at end of file diff --git a/pytholog/knowledge_base.py b/pytholog/knowledge_base.py index cd5bc88..f1cc0b5 100644 --- a/pytholog/knowledge_base.py +++ b/pytholog/knowledge_base.py @@ -22,6 +22,23 @@ def __init__(self, name = None): KnowledgeBase.__id += 1 self.name = name self._cache = {} + + def rem_kn(self, kn): + for i in kn: + i = Fact(i) + g = [Goal(Fact(r.to_string())) for r in i.rhs] + if i.lh.predicate in self.db: + self.db[i.lh.predicate]["facts"].pop(i) + self.db[i.lh.predicate]["terms"].pop(i.terms) + self.db[i.lh.predicate]["goals"].pop(g) + + # delete it if it is now empty + if not len(self.db[i.lh.predicate]["facts"]) \ + and not len(self.db[i.lh.predicate]["terms"]) \ + and not len(self.db[i.lh.predicate]["goals"]): + del self.db[i.lh.predicate] + + self.clear_cache() ## the main function that adds new entries or append existing ones ## it creates "facts", "goals" and "terms" buckets for each predicate @@ -30,12 +47,17 @@ def add_kn(self, kn): i = Fact(i) ## rhs are stored as Expr here we change class to Goal g = [Goal(Fact(r.to_string())) for r in i.rhs] + if i.lh.predicate in self.db: self.db[i.lh.predicate]["facts"].push(i) self.db[i.lh.predicate]["terms"].push(i.terms) self.db[i.lh.predicate]["goals"].push(g) - #self.db[i.lh.predicate]["terms"].append(i.terms) + # self.db[i.lh.predicate]["terms"].append(i.terms) + indx, look_up = term_checker(Expr(i.to_string())) + if look_up in self._cache: + del self._cache[look_up] else: + # initialize the knowledge base self.db[i.lh.predicate] = {} self.db[i.lh.predicate]["facts"] = FactHeap() self.db[i.lh.predicate]["facts"].push(i) @@ -45,6 +67,9 @@ def add_kn(self, kn): self.db[i.lh.predicate]["terms"].push(i.terms) #self.db[i.lh.predicate]["goals"] = [g] #self.db[i.lh.predicate]["terms"] = [i.terms] + + def add(self, predicate, *values): + return self.add_kn([Fact(f"{predicate}({', '.join(str(x) for x in values)})")]) def __call__(self, args): self.add_kn(args) @@ -53,6 +78,9 @@ def __call__(self, args): ## it is only to be user intuitive readable method def query(self, expr, cut = False, show_path = False): return rule_query(self, expr, cut, show_path) + + def q(self, predicate, *values): + return rule_query(self, Expr(f"{predicate}({', '.join(str(x) for x in values)})")) def rule_search(self, expr): if expr.predicate not in self.db: diff --git a/pytholog/pq.py b/pytholog/pq.py index 3415940..1bcb243 100644 --- a/pytholog/pq.py +++ b/pytholog/pq.py @@ -27,6 +27,12 @@ def __init__(self): def push(self, item): insort(self._container, item) # in by sort + + def pop(self, item): + if item in self._container: + self._container.remove(item) + else: + print("item not in container") def __getitem__(self, item): return self._container[item]