Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature multifieldwildcard #55

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions docs/examples/blocks.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# ======================================================\n",
"# Blocks World Program\n",
"#\n",
"# This program was introduced in Chapter 8.\n",
"#\n",
"# CLIPS Version 6.0 Example\n",
"#\n",
"# To execute, merely load, reset and run.\n",
"# ======================================================"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"from pyknow import *"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"class Goal(Fact):\n",
" pass\n",
"\n",
"class Stack(Fact):\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"class Blocks(KnowledgeEngine):\n",
" @DefFacts()\n",
" def initial_state(self):\n",
" yield Stack(\"A\", \"B\", \"C\")\n",
" yield Stack(\"D\", \"E\", \"F\")\n",
" yield Goal(move=\"C\", on_top_of=\"E\")\n",
" yield Stack()\n",
" \n",
" @Rule(\n",
" AS.goal << Goal(move=MATCH.block1, on_top_of=MATCH.block2),\n",
" AS.stack1 << Stack(MATCH.block1, +MATCH.rest1),\n",
" AS.stack2 << Stack(MATCH.block2, +MATCH.rest2)\n",
" )\n",
" def move_directly(self, goal, block1, block2, stack1, stack2, rest1, rest2):\n",
" self.retract(goal)\n",
" self.retract(stack1)\n",
" self.retract(stack2)\n",
" self.declare(Stack(*rest1))\n",
" self.declare(Stack(block1, block2, *rest2))\n",
" print(block1, \"moved on top of\", block2)\n",
" \n",
" @Rule(\n",
" AS.goal << Goal(move=MATCH.block1, on_top_of=\"floor\"),\n",
" AS.stack1 << Stack(MATCH.block1, +MATCH.rest)\n",
" )\n",
" def move_to_floor(self, goal, stack1, block1, rest):\n",
" self.retract(goal)\n",
" self.retract(stack1)\n",
" self.declare(Stack(block1))\n",
" self.declare(Stack(*rest))\n",
" print(block1, \"moved on top of floor\")\n",
"\n",
" @Rule(\n",
" Goal(move=MATCH.block1),\n",
" Stack(MATCH.top, +MATCH.others),\n",
" TEST(lambda block1, others: block1 in others)\n",
" )\n",
" def clear_upper_block(self, top):\n",
" self.declare(Goal(move=top, on_top_of=\"floor\"))\n",
" \n",
" @Rule(\n",
" Goal(on_top_of=MATCH.block1),\n",
" Stack(MATCH.top, +MATCH.others),\n",
" TEST(lambda block1, others: block1 in others)\n",
" )\n",
" def clear_lower_block(self, top):\n",
" self.declare(Goal(move=top, on_top_of=\"floor\"))"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"D moved on top of floor\n",
"A moved on top of floor\n",
"B moved on top of floor\n",
"C moved on top of E\n"
]
}
],
"source": [
"ke=Blocks()\n",
"ke.reset()\n",
"ke.run()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
10 changes: 8 additions & 2 deletions pyknow/fieldconstraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,19 @@ def __repr__(self): # pragma: no cover

class W(Bindable, FieldConstraint):
"""Wildcard Field Constraint"""
def __new__(cls, __bind__=None):
def __new__(cls, __bind__=None, __multi__=False):
obj = super(W, cls).__new__(cls)
obj.__bind__ = __bind__
obj.__multi__ = __multi__
return obj

def __repr__(self): # pragma: no cover
return "W()" if self.__bind__ is None else "W(%r)" % self.__bind__
sign = "+" if self.__multi__ else ""
body = ("W(%r)" % self.__bind__ if self.__bind__ else "W()")
return sign + body

def __pos__(self):
return W(__bind__=self.__bind__, __multi__=True)


class P(Bindable, FieldConstraint):
Expand Down
29 changes: 25 additions & 4 deletions pyknow/matchers/rete/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@


CheckFunction = namedtuple('CheckFunction',
['key_a', 'key_b', 'expected', 'check'])
['key_a',
'key_b',
'multi',
'expected',
'check'])


class TypeCheck(Check, namedtuple('_TypeCheck', ['fact_type'])):
Expand Down Expand Up @@ -60,7 +64,11 @@ def __str__(self): # pragma: no cover

class FeatureCheck(Check,
namedtuple('_FeatureCheck',
['what', 'how', 'check', 'expected'])):
['what',
'how',
'multi',
'check',
'expected'])):

_instances = dict()

Expand All @@ -77,6 +85,7 @@ def __new__(cls, what, how):
cls,
what,
how,
check_function.multi,
check_function.check,
check_function.expected)

Expand All @@ -97,7 +106,13 @@ def __call__(self, data, is_fact=True):
return False
else:
try:
record = data[self.what]
if self.multi:
numidx = sorted(
x for x in data.keys()
if isinstance(x, int) and x >= self.what)
record = tuple(data[k] for k in numidx)
else:
record = data[self.what]
except (IndexError, KeyError, TypeError):
return False
else:
Expand Down Expand Up @@ -131,6 +146,7 @@ def equal_literal(actual, expected):

return CheckFunction(key_a=L,
key_b=(pce.value, pce.__bind__),
multi=False,
expected=pce,
check=equal_literal)

Expand All @@ -151,6 +167,7 @@ def match_predicate(actual, expected):

return CheckFunction(key_a=P,
key_b=key_b,
multi=False,
expected=pce,
check=match_predicate)

Expand All @@ -163,7 +180,8 @@ def wildcard_match(actual, expected):
return {expected.__bind__: actual}

return CheckFunction(key_a=W,
key_b=pce.__bind__,
key_b=(pce.__bind__, pce.__multi__),
multi=pce.__multi__,
expected=pce,
check=wildcard_match)

Expand All @@ -182,6 +200,7 @@ def not_equal(actual, expected):

return CheckFunction(key_a=NOTFC,
key_b=key_b,
multi=False,
expected=key_b,
check=not_equal)

Expand Down Expand Up @@ -210,6 +229,7 @@ def and_match(actual, expected):

return CheckFunction(key_a=ANDFC,
key_b=key_b,
multi=False,
expected=key_b,
check=and_match)

Expand All @@ -227,6 +247,7 @@ def or_match(actual, expected):

return CheckFunction(key_a=ORFC,
key_b=key_b,
multi=False,
expected=key_b,
check=or_match)

Expand Down
2 changes: 1 addition & 1 deletion pyknow/shortcuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class _MATCH:
MATCH.something
"""
def __getattr__(self, name):
return (name << W())
return name << W()


MATCH = _MATCH()
Expand Down