-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rete Net construction, upto canonical graph forms
- Loading branch information
Showing
3 changed files
with
133 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,4 +10,5 @@ pyyaml | |
backports.cached_property | ||
jinja2 | ||
pydblite | ||
frozendict | ||
parameterized |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
from pydblite import Base as dBase | ||
from frozendict import frozendict | ||
from ..utils.collections import subdict | ||
from types import MethodType | ||
from . import rete_node | ||
from ..schema.entity import Entity | ||
|
||
# Here, core refers to the "core element" of a rete-node. | ||
# This may be an edge, a graph ,a pattern, a rule, etc. | ||
|
||
class ReteNet: | ||
|
||
methods = rete_node.initializers | ||
|
||
def __init__(self): | ||
self.nodes = dict() | ||
self.channels = dBase(':memory:') | ||
self.channels.create('source','target','chtype','misc') | ||
self.configure() | ||
|
||
def configure(self): | ||
for method in self.__class__.methods: | ||
self.bind_method(method) | ||
return self | ||
|
||
def add_node(self,node): | ||
assert isinstance(node,rete_node.ReteNode) | ||
if node.core not in self.nodes: | ||
self.nodes[node.core] = node | ||
|
||
# register node | ||
return self | ||
|
||
def add_channel(self,source,target,chtype,**kwargs): | ||
assert isinstance(source,rete_node.ReteNode) | ||
assert isinstance(target,rete_node.ReteNode) | ||
kwargs = frozendict(kwargs) if kwargs else None | ||
channel = dict(source=source,target=target,chtype=chtype,misc=kwargs) | ||
if not self.channels(**channel): | ||
self.channels.insert(**channel) | ||
|
||
# register channel | ||
return self | ||
|
||
def bind_method(self,method): | ||
m = MethodType(method, self) | ||
assert method.__name__ not in dir(self) | ||
setattr(self,method.__name__,m) | ||
return self | ||
|
||
def list_methods(self): | ||
return [x for x in dir(self) if x[0:2]!='__' and isinstance(getattr(self,x),MethodType)] | ||
|
||
def pprint(self): | ||
s = [] | ||
for x in self.nodes: | ||
s.append('Node '+str(x)) | ||
for channel in self.channels: | ||
s.append(f"Channel {str(channel['source'].core)} -> {str(channel['target'].core)}, {channel['chtype']}, {channel['misc']}") | ||
return '\n'.join(s) | ||
|
||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
from ..schema.base import BaseClass | ||
from collections import deque | ||
from itertools import dropwhile | ||
|
||
from ..graph.graph_partitioning import partition_canonical_form | ||
from frozendict import frozendict | ||
|
||
class ReteNode: | ||
|
||
def __init__(self,core=None,**data): | ||
self.core = core | ||
self.data = frozendict(data) if data else None | ||
self.slots = deque() | ||
self.cache = None | ||
|
||
def initialize_start(net): | ||
net.add_node(ReteNode('start')) | ||
return net | ||
|
||
def initialize_base_subclass(net,_class): | ||
classes = ['start'] + list(reversed(_class.__mro__[:_class.__mro__.index(BaseClass)])) | ||
classpairs = [(c1,c2) for c1,c2 in zip(classes[:-1],classes[1:]) if c2 not in net.nodes] | ||
for c1,c2 in classpairs: | ||
net.add_node(ReteNode(c2)) | ||
n1,n2 = [net.nodes[x] for x in [c1,c2]] | ||
net.add_channel(n1,n2,'passthrough') | ||
return net | ||
|
||
def initialize_single_edge(net,canonical_form,group): | ||
assert len(canonical_form.edges)==1 | ||
_class = min(canonical_form.classes,key=lambda c: len(c.__mro__)) | ||
if _class not in net.nodes: | ||
net.initialize_base_subclass(_class) | ||
net.add_node(ReteNode(canonical_form,group=group)) | ||
n1,n2 = [net.nodes[x] for x in [_class,canonical_form]] | ||
net.add_channel(n1,n2,'passthrough') | ||
return net | ||
|
||
def initialize_canonical_form(net,canonical_form,group): | ||
if canonical_form in net.nodes: | ||
return net | ||
assert len(canonical_form.edges) >= 1 | ||
if len(canonical_form.edges)==1: | ||
net.initialize_single_edge(canonical_form,group) | ||
return net | ||
net.add_node(ReteNode(canonical_form,group=group)) | ||
for mapping,labeling,grouping in partition_canonical_form(canonical_form,group): | ||
net.initialize_canonical_form(labeling,grouping) | ||
n1,n2 = [net.nodes[x] for x in [labeling,canonical_form]] | ||
net.add_channel(n1,n2,'merge',mapping=mapping) | ||
return net | ||
|
||
def initialize_pattern(net,pattern,group): | ||
return net | ||
|
||
def initialize_rule(net,rule): | ||
return net | ||
|
||
def initialize_RBM(net,rbm): | ||
return net | ||
|
||
initializers = [ | ||
initialize_start, | ||
initialize_base_subclass, | ||
initialize_single_edge, | ||
initialize_canonical_form | ||
] |