def invokeRule(priorAnswers, bodyLiteralIterator, sip, otherargs, priorBooleanGoalSuccess=False, step=None, debug=False, buildProof=False): """ Continue invokation of rule using (given) prior answers and list of remaining body literals (& rule sip). If prior answers is a list, computation is split disjunctively [..] By combining the answers to all these subqueries, we generate answers for the original query involving the rule head Can also takes a PML step and updates it as it navigates the top-down proof tree (passing it on and updating it where necessary) """ assert not buildProof or step is not None proofLevel, memoizeMemory, sipCollection, factGraph, derivedPreds, processedRules = otherargs remainingBodyList = [i for i in bodyLiteralIterator] lazyGenerator = lazyGeneratorPeek(priorAnswers, 2) if lazyGenerator.successful: #There are multiple answers in this step, we need to call invokeRule #recursively for each answer, returning the first positive attempt success = False rt = None _step = None ansNo = 0 for priorAns in lazyGenerator: ansNo += 1 try: if buildProof: newStep = InferenceStep(step.parent, step.rule, source=step.source) newStep.antecedents = [ant for ant in step.antecedents] else: newStep = None for rt,_step in\ invokeRule([priorAns], iter([i for i in remainingBodyList]), sip, otherargs, priorBooleanGoalSuccess, newStep, debug = debug, buildProof = buildProof): if rt: yield rt, _step except RuleFailure, e: pass if not success: #None of prior answers were successful #indicate termination of rule processing raise RuleFailure( "Unable to solve either of %s against remainder of rule: %s" % (ansNo, remainingBodyList))
def invokeRule(priorAnswers, bodyLiteralIterator, sip, otherargs, priorBooleanGoalSuccess=False, step=None, debug=False, buildProof=False): """ Continue invokation of rule using (given) prior answers and list of remaining body literals (& rule sip). If prior answers is a list, computation is split disjunctively [..] By combining the answers to all these subqueries, we generate answers for the original query involving the rule head Can also takes a PML step and updates it as it navigates the top-down proof tree (passing it on and updating it where necessary) """ assert not buildProof or step is not None proofLevel, memoizeMemory, sipCollection, \ factGraph, derivedPreds, processedRules = otherargs remainingBodyList = [i for i in bodyLiteralIterator] lazyGenerator = lazyGeneratorPeek(priorAnswers, 2) if lazyGenerator.successful: # There are multiple answers in this step, we need to call invokeRule # recursively for each answer, returning the first positive attempt success = False rt = None _step = None ansNo = 0 for priorAns in lazyGenerator: ansNo += 1 try: if buildProof: newStep = InferenceStep(step.parent, step.rule, source=step.source) newStep.antecedents = [ant for ant in step.antecedents] else: newStep = None for rt, _step in\ invokeRule([priorAns], iter([i for i in remainingBodyList]), sip, otherargs, priorBooleanGoalSuccess, newStep, debug=debug, buildProof=buildProof): if rt: yield rt, _step except RuleFailure: pass if not success: # None of prior answers were successful # indicate termination of rule processing raise RuleFailure( "Unable to solve either of %s against remainder of rule: %s" % ( ansNo, remainingBodyList)) # yield False, _InferenceStep(step.parent, step.rule, # source=step.source) else: lazyGenerator = lazyGeneratorPeek(lazyGenerator) projectedBindings = lazyGenerator.successful and first( lazyGenerator) or {} # First we check if we can combine a large group of subsequent body literals # into a single query # if we have a template map then we use it to further # distinguish which builtins can be solved via # cumulative SPARQl query - else we solve # builtins one at a time def sparqlResolvable(literal): if isinstance(literal, Uniterm): return not literal.naf and GetOp(literal) not in derivedPreds else: return isinstance(literal, N3Builtin) and \ literal.uri in factGraph.templateMap def sparqlResolvableNoTemplates(literal): if isinstance(literal, Uniterm): return not literal.naf and GetOp(literal) not in derivedPreds else: return False conjGroundLiterals = list( itertools.takewhile( hasattr(factGraph, 'templateMap') and sparqlResolvable or sparqlResolvableNoTemplates, remainingBodyList)) bodyLiteralIterator = iter(remainingBodyList) if len(conjGroundLiterals) > 1: # If there are literals to combine *and* a mapping from rule # builtins to SPARQL FILTER templates .. basePredicateVars = set( reduce(lambda x, y: x + y, [list(GetVariables(arg, secondOrder=True)) for arg in conjGroundLiterals])) if projectedBindings: openVars = basePredicateVars.intersection(projectedBindings) else: # We don't have any given bindings, so we need to treat # the body as an open query openVars = basePredicateVars queryConj = EDBQuery([copy.deepcopy(lit) for lit in conjGroundLiterals], factGraph, openVars, projectedBindings) query, answers = queryConj.evaluate(debug) if isinstance(answers, bool): combinedAnswers = {} rtCheck = answers else: if projectedBindings: combinedAnswers = (mergeMappings1To2(ans, projectedBindings, makeImmutable=True) for ans in answers) else: combinedAnswers = (MakeImmutableDict(ans) for ans in answers) combinedAnsLazyGenerator = lazyGeneratorPeek(combinedAnswers) rtCheck = combinedAnsLazyGenerator.successful if not rtCheck: raise RuleFailure( "No answers for combined SPARQL query: %s" % query) else: # We have solved the previous N body literals with a single # conjunctive query, now we need to make each of the literals # an antecedent to a 'query' step. if buildProof: queryStep = InferenceStep(None, source='some RDF graph') # FIXME: subquery undefined queryStep.groundQuery = subquery queryStep.bindings = {} # combinedAnswers[-1] # FIXME: subquery undefined queryHash = URIRef( "tag:[email protected]:Queries#" + makeMD5Digest(subquery)) queryStep.identifier = queryHash for subGoal in conjGroundLiterals: subNs = NodeSet(subGoal.toRDFTuple(), identifier=BNode()) subNs.steps.append(queryStep) step.antecedents.append(subNs) queryStep.parent = subNs for rt, _step in invokeRule( isinstance(answers, bool) and [ projectedBindings] or combinedAnsLazyGenerator, iter(remainingBodyList[len(conjGroundLiterals):]), sip, otherargs, isinstance(answers, bool), step, debug=debug, buildProof=buildProof): yield rt, _step else: # Continue processing rule body condition # one literal at a time try: bodyLiteral = next( bodyLiteralIterator) if py3compat.PY3 else bodyLiteralIterator.next() # if a N3 builtin, execute it using given bindings for boolean answer # builtins are moved to end of rule when evaluating rules via # sip if isinstance(bodyLiteral, N3Builtin): lhs = bodyLiteral.argument rhs = bodyLiteral.result lhs = isinstance( lhs, Variable) and projectedBindings[lhs] or lhs rhs = isinstance( rhs, Variable) and projectedBindings[rhs] or rhs assert lhs is not None and rhs is not None if bodyLiteral.func(lhs, rhs): if debug: print("Invoked %s(%s, %s) -> True" % ( bodyLiteral.uri, lhs, rhs)) # positive answer means we can continue processing the # rule body if buildProof: ns = NodeSet(bodyLiteral.toRDFTuple(), identifier=BNode()) step.antecedents.append(ns) for rt, _step in invokeRule( [projectedBindings], bodyLiteralIterator, sip, otherargs, step, priorBooleanGoalSuccess, debug=debug, buildProof=buildProof): yield rt, _step else: if debug: print("Successfully invoked %s(%s, %s) -> False" % ( bodyLiteral.uri, lhs, rhs)) raise RuleFailure("Failed builtin invokation %s(%s, %s)" % (bodyLiteral.uri, lhs, rhs)) else: # For every body literal, subqueries are generated according # to the sip sipArcPred = URIRef(GetOp(bodyLiteral) + '_' + '_'.join(GetArgs(bodyLiteral))) assert len(list(IncomingSIPArcs(sip, sipArcPred))) < 2 subquery = copy.deepcopy(bodyLiteral) subquery.ground(projectedBindings) for N, x in IncomingSIPArcs(sip, sipArcPred): # That is, each subquery contains values for the bound arguments # that are passed through the sip arcs entering the node # corresponding to that literal # Create query out of body literal and apply # sip-provided bindings subquery = copy.deepcopy(bodyLiteral) subquery.ground(projectedBindings) if literalIsGround(subquery): # subquery is ground, so there will only be boolean answers # we return the conjunction of the answers for the current # subquery answer = False ns = None answers = first( itertools.dropwhile( lambda item: not item[0], SipStrategy( subquery.toRDFTuple(), sipCollection, factGraph, derivedPreds, MakeImmutableDict(projectedBindings), processedRules, network=step is not None and step.parent.network or None, debug=debug, buildProof=buildProof, memoizeMemory=memoizeMemory, proofLevel=proofLevel))) if answers: answer, ns = answers if not answer and not bodyLiteral.naf or \ (answer and bodyLiteral.naf): # negative answer means the invokation of the rule fails # either because we have a positive literal and there # is no answer for the subgoal or the literal is # negative and there is an answer for the subgoal raise RuleFailure( "No solutions solving ground query %s" % subquery) else: if buildProof: if not answer and bodyLiteral.naf: ns.naf = True step.antecedents.append(ns) # positive answer means we can continue processing the rule body # either because we have a positive literal and answers # for subgoal or a negative literal and no answers for the # the goal for rt, _step in invokeRule( [projectedBindings], bodyLiteralIterator, sip, otherargs, True, step, debug=debug): yield rt, _step else: _answers = \ SipStrategy( subquery.toRDFTuple(), sipCollection, factGraph, derivedPreds, MakeImmutableDict( projectedBindings), processedRules, network=step is not None and step.parent.network or None, debug=debug, buildProof=buildProof, memoizeMemory=memoizeMemory, proofLevel=proofLevel) # solve (non-ground) subgoal def collectAnswers(_ans): for ans, ns in _ans: if isinstance(ans, dict): try: map = mergeMappings1To2( ans, projectedBindings, makeImmutable=True) yield map except: pass combinedAnswers = collectAnswers(_answers) answers = lazyGeneratorPeek(combinedAnswers) if not answers.successful \ and not bodyLiteral.naf \ or (bodyLiteral.naf and answers.successful): raise RuleFailure( "No solutions solving ground query %s" % subquery) else: # Either we have a positive subgoal and answers # or a negative subgoal and no answers if buildProof: if answers.successful: goals = set([g for a, g in answers]) assert len(goals) == 1 step.antecedents.append(goals.pop()) else: newNs = NodeSet( bodyLiteral.toRDFTuple(), network=step.parent.network, identifier=BNode(), naf=True) step.antecedents.append(newNs) for rt, _step in invokeRule( answers, bodyLiteralIterator, sip, otherargs, priorBooleanGoalSuccess, step, debug=debug, buildProof=buildProof): yield rt, _step except StopIteration: # Finished processing rule if priorBooleanGoalSuccess: yield projectedBindings and projectedBindings or True, step elif projectedBindings: # Return the most recent (cumulative) answers and the given # step yield projectedBindings, step else: raise RuleFailure( "Finished processing rule unsuccessfully")
buildProof = buildProof): if rt: yield rt, _step except RuleFailure, e: pass if not success: #None of prior answers were successful #indicate termination of rule processing raise RuleFailure( "Unable to solve either of %s against remainder of rule: %s" % (ansNo, remainingBodyList)) # yield False,_InferenceStep(step.parent,step.rule,source=step.source) else: lazyGenerator = lazyGeneratorPeek(lazyGenerator) projectedBindings = lazyGenerator.successful and first( lazyGenerator) or {} #First we check if we can combine a large group of subsequent body literals #into a single query #if we have a template map then we use it to further #distinguish which builtins can be solved via #cumulative SPARQl query - else we solve #builtins one at a time def sparqlResolvable(literal): if isinstance(literal, Uniterm): return not literal.naf and GetOp(literal) not in derivedPreds else: return isinstance(literal,N3Builtin) and \ literal.uri in factGraph.templateMap
def invokeRule(priorAnswers, bodyLiteralIterator, sip, otherargs, priorBooleanGoalSuccess=False, step=None, debug=False, buildProof=False): """ Continue invokation of rule using (given) prior answers and list of remaining body literals (& rule sip). If prior answers is a list, computation is split disjunctively [..] By combining the answers to all these subqueries, we generate answers for the original query involving the rule head Can also takes a PML step and updates it as it navigates the top-down proof tree (passing it on and updating it where necessary) """ assert not buildProof or step is not None proofLevel, memoizeMemory, sipCollection, \ factGraph, derivedPreds, processedRules = otherargs remainingBodyList = [i for i in bodyLiteralIterator] lazyGenerator = lazyGeneratorPeek(priorAnswers, 2) if lazyGenerator.successful: # There are multiple answers in this step, we need to call invokeRule # recursively for each answer, returning the first positive attempt success = False rt = None _step = None ansNo = 0 for priorAns in lazyGenerator: ansNo += 1 try: if buildProof: newStep = InferenceStep(step.parent, step.rule, source=step.source) newStep.antecedents = [ant for ant in step.antecedents] else: newStep = None for rt, _step in\ invokeRule([priorAns], iter([i for i in remainingBodyList]), sip, otherargs, priorBooleanGoalSuccess, newStep, debug=debug, buildProof=buildProof): if rt: yield rt, _step except RuleFailure: pass if not success: # None of prior answers were successful # indicate termination of rule processing raise RuleFailure( "Unable to solve either of %s against remainder of rule: %s" % (ansNo, remainingBodyList)) # yield False, _InferenceStep(step.parent, step.rule, source=step.source) else: lazyGenerator = lazyGeneratorPeek(lazyGenerator) projectedBindings = lazyGenerator.successful and first( lazyGenerator) or {} # First we check if we can combine a large group of subsequent body literals # into a single query # if we have a template map then we use it to further # distinguish which builtins can be solved via # cumulative SPARQl query - else we solve # builtins one at a time def sparqlResolvable(literal): if isinstance(literal, Uniterm): return not literal.naf and GetOp(literal) not in derivedPreds else: return isinstance(literal, N3Builtin) and \ literal.uri in factGraph.templateMap def sparqlResolvableNoTemplates(literal): if isinstance(literal, Uniterm): return not literal.naf and GetOp(literal) not in derivedPreds else: return False conjGroundLiterals = list( itertools.takewhile( hasattr(factGraph, 'templateMap') and sparqlResolvable or \ sparqlResolvableNoTemplates, remainingBodyList)) bodyLiteralIterator = iter(remainingBodyList) if len(conjGroundLiterals) > 1: # If there are literals to combine *and* a mapping from rule # builtins to SPARQL FILTER templates .. basePredicateVars = set( reduce(lambda x, y: x + y, [ list(GetVariables(arg, secondOrder=True)) for arg in conjGroundLiterals ])) if projectedBindings: openVars = basePredicateVars.intersection(projectedBindings) else: # We don't have any given bindings, so we need to treat # the body as an open query openVars = basePredicateVars queryConj = EDBQuery( [copy.deepcopy(lit) for lit in conjGroundLiterals], factGraph, openVars, projectedBindings) query, answers = queryConj.evaluate(debug) if isinstance(answers, bool): combinedAnswers = {} rtCheck = answers else: if projectedBindings: combinedAnswers = (mergeMappings1To2(ans, projectedBindings, makeImmutable=True) for ans in answers) else: combinedAnswers = (MakeImmutableDict(ans) for ans in answers) combinedAnsLazyGenerator = lazyGeneratorPeek(combinedAnswers) rtCheck = combinedAnsLazyGenerator.successful if not rtCheck: raise RuleFailure("No answers for combined SPARQL query: %s" % query) else: # We have solved the previous N body literals with a single # conjunctive query, now we need to make each of the literals # an antecedent to a 'query' step. if buildProof: queryStep = InferenceStep(None, source='some RDF graph') queryStep.groundQuery = subquery queryStep.bindings = {} # combinedAnswers[-1] queryHash = URIRef( "tag:[email protected]:Queries#" + \ makeMD5Digest(subquery)) queryStep.identifier = queryHash for subGoal in conjGroundLiterals: subNs = NodeSet(subGoal.toRDFTuple(), identifier=BNode()) subNs.steps.append(queryStep) step.antecedents.append(subNs) queryStep.parent = subNs for rt, _step in invokeRule( isinstance(answers, bool) and [projectedBindings] or combinedAnsLazyGenerator, iter(remainingBodyList[len(conjGroundLiterals):]), sip, otherargs, isinstance(answers, bool), step, debug=debug, buildProof=buildProof): yield rt, _step else: # Continue processing rule body condition # one literal at a time try: bodyLiteral = next( bodyLiteralIterator ) if py3compat.PY3 else bodyLiteralIterator.next() # if a N3 builtin, execute it using given bindings for boolean answer # builtins are moved to end of rule when evaluating rules via sip if isinstance(bodyLiteral, N3Builtin): lhs = bodyLiteral.argument rhs = bodyLiteral.result lhs = isinstance( lhs, Variable) and projectedBindings[lhs] or lhs rhs = isinstance( rhs, Variable) and projectedBindings[rhs] or rhs assert lhs is not None and rhs is not None if bodyLiteral.func(lhs, rhs): if debug: print("Invoked %s(%s, %s) -> True" % (bodyLiteral.uri, lhs, rhs)) # positive answer means we can continue processing the rule body if buildProof: ns = NodeSet(bodyLiteral.toRDFTuple(), identifier=BNode()) step.antecedents.append(ns) for rt, _step in invokeRule([projectedBindings], bodyLiteralIterator, sip, otherargs, step, priorBooleanGoalSuccess, debug=debug, buildProof=buildProof): yield rt, _step else: if debug: print("Successfully invoked %s(%s, %s) -> False" % (bodyLiteral.uri, lhs, rhs)) raise RuleFailure( "Failed builtin invokation %s(%s, %s)" % (bodyLiteral.uri, lhs, rhs)) else: # For every body literal, subqueries are generated according # to the sip sipArcPred = URIRef(GetOp(bodyLiteral) + \ '_' + '_'.join(GetArgs(bodyLiteral))) assert len(list(IncomingSIPArcs(sip, sipArcPred))) < 2 subquery = copy.deepcopy(bodyLiteral) subquery.ground(projectedBindings) for N, x in IncomingSIPArcs(sip, sipArcPred): #That is, each subquery contains values for the bound arguments #that are passed through the sip arcs entering the node #corresponding to that literal #Create query out of body literal and apply sip-provided bindings subquery = copy.deepcopy(bodyLiteral) subquery.ground(projectedBindings) if literalIsGround(subquery): #subquery is ground, so there will only be boolean answers #we return the conjunction of the answers for the current #subquery answer = False ns = None answers = first( itertools.dropwhile( lambda item: not item[0], SipStrategy( subquery.toRDFTuple(), sipCollection, factGraph, derivedPreds, MakeImmutableDict(projectedBindings), processedRules, network=step is not None and \ step.parent.network or None, debug=debug, buildProof=buildProof, memoizeMemory=memoizeMemory, proofLevel=proofLevel))) if answers: answer, ns = answers if not answer and not bodyLiteral.naf or \ (answer and bodyLiteral.naf): #negative answer means the invokation of the rule fails #either because we have a positive literal and there #is no answer for the subgoal or the literal is #negative and there is an answer for the subgoal raise RuleFailure( "No solutions solving ground query %s" % subquery) else: if buildProof: if not answer and bodyLiteral.naf: ns.naf = True step.antecedents.append(ns) #positive answer means we can continue processing the rule body #either because we have a positive literal and answers #for subgoal or a negative literal and no answers for the #the goal for rt, _step in invokeRule([projectedBindings], bodyLiteralIterator, sip, otherargs, True, step, debug=debug): yield rt, _step else: _answers = \ SipStrategy(subquery.toRDFTuple(), sipCollection, factGraph, derivedPreds, MakeImmutableDict(projectedBindings), processedRules, network=step is not None and \ step.parent.network or None, debug=debug, buildProof=buildProof, memoizeMemory=memoizeMemory, proofLevel=proofLevel) # solve (non-ground) subgoal def collectAnswers(_ans): for ans, ns in _ans: if isinstance(ans, dict): try: map = mergeMappings1To2( ans, projectedBindings, makeImmutable=True) yield map except: pass combinedAnswers = collectAnswers(_answers) answers = lazyGeneratorPeek(combinedAnswers) if not answers.successful \ and not bodyLiteral.naf \ or (bodyLiteral.naf and answers.successful): raise RuleFailure( "No solutions solving ground query %s" % subquery) else: # Either we have a positive subgoal and answers # or a negative subgoal and no answers if buildProof: if answers.successful: goals = set([g for a, g in answers]) assert len(goals) == 1 step.antecedents.append(goals.pop()) else: newNs = NodeSet( bodyLiteral.toRDFTuple(), network=step.parent.network, identifier=BNode(), naf=True) step.antecedents.append(newNs) for rt, _step in invokeRule( answers, bodyLiteralIterator, sip, otherargs, priorBooleanGoalSuccess, step, debug=debug, buildProof=buildProof): yield rt, _step except StopIteration: #Finished processing rule if priorBooleanGoalSuccess: yield projectedBindings and projectedBindings or True, step elif projectedBindings: #Return the most recent (cumulative) answers and the given step yield projectedBindings, step else: raise RuleFailure( "Finished processing rule unsuccessfully")