from terms import URI, Variable, Fact, Exivar from helpers import removedups, getOccurences, keysToList, sortVars, Memory, convertBNodeToFact, isListVarNode, INCLUDES from prooftrace import ProofTrace from sets import Set from helpers import FIRST, REST, TYPE, INCLUDES, LOG_SEMANTICS as SEMANTICS, IMPLIES, NIL import time import copy import exception from N3Loader import formulas py_lists = dict() #add initial facts that match the lists at the very beginning - shouldnt screw up anything class AlphaNode: def __init__(self, pattern): self.pattern = pattern #print "pattern", pattern self.ind = Memory() ###ind=memory, or ind=index self.betaNodes = list() self.vars = [v for v in pattern if isinstance(v, Variable)] #hack vlatko 10/24/2006t: #special case for log:includes: get all variables in RHS and add them if self.pattern[1]== INCLUDES: self.vars.extend(list(self.pattern[2].getVars())) #end hack sortVars(self.vars) self.svars = list() self.dependents = list() self.dependsOn = list() def clear(self): self.ind = Memory() #####Make this a Pattern method -- multiple arity patterns def getvar(self, var, fact): """I return the value of var in fact according to our pattern.""" if var in self.pattern: pos = self.pattern.index(var) return fact[pos] else: raise exception.UnboundRuleVariable(var, self.pattern) def getbindings(self, row, useBuiltin=None): """I return a set of bindings for row, where row is a set of values from my AlphaMemory (note that rows that do not have constants, only variable values.)""" bindings = dict() key = removedups(self.svars + self.vars) for i, val in enumerate(key): bindings[val] = row[i] #print "key alpha:",key #print "row alpha:",row return bindings def getkey(self): return removedups(self.svars + self.vars) def getrest(self, fact, sharedVars): """I return the unshared variable values for a given fact according to our pattern. Note that right now we need to pass the *shared* variable list as a parameter, but in our Rete network we would just make that an attribute of every ANode in compilation.""" vals = list() for i, v in enumerate(self.pattern): if isinstance(v, Variable) and v not in sharedVars: vals.append(fact[i]) return vals def setupIndex(self): if not self.svars: self.svars = [self.vars[0]] # need to account for 0 var case # len(shared) <= len(self.vars) # We need to remove dups. # unshared and shared are *disjoint*, so only need to remove # dups in each self.unshared = list(removedups([v for v in self.vars if v not in self.svars])) ####Make this a Pattern method def match(self, fact): """Determines whether the fact matches the node's pattern""" bindings = dict() for p, f in zip(self.pattern, fact): if not isinstance(p, Variable): if p != f: return False elif p not in bindings: bindings[p] = f elif bindings[p] != f: return False return bindings def addAll(self, facts): for f in facts: self.add(f) def add(self, fact): bindings = self.match(fact) if bindings: #make sure key has shared vars first key = removedups(self.svars + self.vars) return self.index(self.ind, bindings, key, fact) else: return False def getJustification(fact): """I take a fact and return its justification set (another set of facts).""" if fact.s in self.ind: if fact.p in self.ind[fact.s]: val = self.ind[fact.s][fact.p] if fact.o in val[fact.o]: return val[fact.o] return None def exists(self, fact, bindings): """Check the index for the presence of the fact (as expressed as a set of bindings returned by match)""" key = self.svars + self.unshared return self.__exists(bindings, self.ind, key) def __exists(self, bindings, ind, key): # vars are in reverse shared/unshared sorted if key: # Still comparison work to be done cur = key.pop(0) if bindings[cur] in ind: return self.existshelp(bindings, ind[bindings[cur]], key) else: return False else: # We succeded in getting to the bottom of the index return True def clear(self): """I clear the memory of this AlphaNode. This is only called from unit tests.""" self.ind = Memory() def index(self, ind, bindings, key, factAdded=None): if key: # Still work to be done cur = bindings[key.pop(0)] #pop(0) pops the first item off if cur not in ind: # So we know the fact doesn't exist if key: ind[cur] = Memory() # just a dictionary -- intended for sorted join return self.index(ind[cur], bindings, key, factAdded) else: # At the bottom, and the fact still doesn't exist # Create justification set, used for proof tracing, and stick it # as the inner most value in the memory pt = ProofTrace() pt.addPremise(factAdded) ind[cur] = tuple(pt) return True #it was added else: if key: # Perhaps the fact does exist return self.index(ind[cur], bindings, key, factAdded) else: # It definitely exists. return False def __repr__(self): return """AlphaNode(%s)(Mem: %s)""" %(str(self.pattern), str(self.ind)) class BetaNode: """A Beta node is a *join* of two other nodes. The other nodes maybe any mix of alphas and betas. An alpha may be joined against itself. The Beta has a set of *shared variables*, that is, variables that appear in both nodes (there may be none).""" def __init__(self, lnode, rnode): from builtins import builtinp self.lnode = lnode self.rnode = rnode #####Make this opaque if isinstance(lnode, BetaNode): self.lvars = lnode.pattern elif isinstance(lnode, AlphaNode): self.lvars = lnode.vars self.rvars = rnode.vars # Detect builtins if isinstance(rnode, AlphaNode): if builtinp(self.rnode): self.svars = lnode.svars else: # store svars in lexical order self.svars = [v for v in self.lvars if v in self.rvars] else: self.svars = [v for v in self.lvars if v in self.rvars] sortVars(self.svars) #destructively sort vars self.parents = list() self.children = list() self.pattern = removedups(self.svars + self.lvars + self.rvars) # Redundant var, will be refactored out self.vars = self.pattern self.builtinInput = None self.ind = Memory() self.inferredFacts = Set() # a pointer to the rule node (which contains the rhs) self.rule = None ####This is 'match' and hence should be a pattern method def getbindings(self, row, useBuiltin=None): bindings = dict() #check for builtins if useBuiltin: key = useBuiltin.indexKey() else: if isinstance(self.lnode, AlphaNode): key = removedups(self.lnode.svars + self.lnode.vars + self.rnode.vars) elif isinstance(self.lnode, BetaNode): key = removedups(self.lnode.pattern + self.rnode.vars) for i, v in enumerate(key): bindings[v] = row[i] return bindings def getkey(self): if isinstance(self.lnode, AlphaNode): key = removedups(self.lnode.svars + self.lnode.vars + self.rnode.vars) elif isinstance(self.lnode, BetaNode): key = removedups(self.lnode.pattern + self.rnode.vars) return key def add(self, row, justification=None): # store results in regular order key = copy.copy(self.pattern) bindings = self.getbindings(row) if bindings: return self.index(self.ind, row, bindings, key, justification) return False def index(self, ind, row, bindings, key, justification=None): if key: # Still work to be done cur = bindings[key.pop(0)] #pop(0) pops the first item off if cur not in ind: # So we know the fact doesn't exist if key: ind[cur] = Memory() return self.index(ind[cur], row, bindings, key, justification) else: # At the bottom, and the factd still doesn't exist if justification: ind[cur] = tuple(justification) else: ind[cur] = tuple() return True #it was added else: if key: # Perhaps the fact does exist return self.index(ind[cur], row, bindings, key, justification) else: # It definitely exists. return False def hasBuiltins(self): from builtins import builtinp, funcBuiltinp """Return True if I have builtins connected to me, otherwise False. Also set a pointer to the builtins""" # If left and right are builtins, they will have the same input node (does this hold for func. builtins, too?) if builtinp(self.lnode): self.builtinInput = self.lnode.getInputNode() if builtinp(self.rnode): self.builtinInput = self.rnode.getInputNode() return bool(self.builtinInput) def bindPossibleVar(self, possVar, bindings): if isinstance(possVar, Variable): return bindings[possVar] else: return possVar def evalBuiltins(self, returnBindings=False): t = time.time() """I evaluate my attached builtins nodes.""" from builtins import builtinp, funcBuiltinp #Three cases to handle: left is a builtin and right is a plain anode, #left is a plain anode and right is a builtin, or both the left #and right are builtins evaldRows = list() builtinNodes = list() if builtinp(self.lnode): builtinNodes.append(self.lnode) if builtinp(self.rnode): builtinNodes.append(self.rnode) add_rows = list() for bi in builtinNodes: inputNode = bi.getInputNode() builtinInput = keysToList(inputNode.ind) #hack vlatko 10/24/2006t: #what happens if input is empty? sometimes we still can eval the builtins #example: