def sparqlResolvableNoTemplates(literal): predTerm = GetOp(literal) if isinstance(literal, Uniterm): return not literal.naf and (predTerm not in derivedPreds or (predTerm in hybridPredicates and not predTerm.find('_derived') + 1)) else: return False
def sparqlResolvableNoTemplates(literal): predTerm = GetOp(literal) if isinstance(literal,Uniterm): return not literal.naf and ( predTerm not in derivedPreds or ( predTerm in hybridPredicates and not predTerm.find('_derived') + 1 )) else: return False
def sparqlResolvable(literal): predTerm = GetOp(literal) if not isinstance(literal, AdornedUniTerm) and isinstance( literal, Uniterm): return not literal.naf and (predTerm not in derivedPreds or (predTerm in hybridPredicates and not predTerm.find('_derived') + 1)) else: return isinstance(literal, N3Builtin) and \ literal.uri in factGraph.templateMap
def sparqlResolvable(literal): predTerm = GetOp(literal) if not isinstance(literal, AdornedUniTerm) and isinstance(literal, Uniterm): return not literal.naf and ( predTerm not in derivedPreds or ( predTerm in hybridPredicates and not predTerm.find('_derived') + 1 )) else: return isinstance(literal,N3Builtin) and \ literal.uri in factGraph.templateMap
def compareAdornedPredToRuleHead(aPred, head, hybridPreds2Replace): """ If p_a is an unmarked adorned predicate, then for each rule that has p in its head, .. """ headPredicateTerm = GetOp(head) aPredTerm = GetOp(aPred) assert isinstance(head, Uniterm) if head.getArity() == aPred.getArity(): return headPredicateTerm == aPredTerm or isinstance( headPredicateTerm, Variable) or (IsHybridPredicate(aPred, hybridPreds2Replace) and aPredTerm[:-8] == headPredicateTerm) return False
def buildMagicBody(N, prevPredicates, adornedHead, derivedPreds, noMagic=[]): unboundHead = 'b' in adornedHead.adornment if unboundHead: body = [adornedHead.makeMagicPred()] else: # If there are no bound argument positions to pass magic values with, # we propagate values in the full relation body = [] for prevAPred in prevPredicates: op = GetOp(prevAPred) if op in N or isinstance(op, Variable): # If qj, j<i, is in N, we add qj to the body of the magic rule # Note, if the atom has a variable for the predicate, treat it as a base # predicate occurrence body.append(prevAPred) if op in derivedPreds \ and isinstance(prevAPred, AdornedUniTerm) \ and prevAPred.adornment.count('b') > 0: # If qj is a derived predicate and its adornment contains at least # one b, we also add the corresponding magic predicate to the body if op in noMagic: body.append(prevAPred) else: body.append(prevAPred.makeMagicPred()) return body
def RDFTuplesToSPARQL(conjunct, edb, isGround=False, vars=[], symmAtomicInclusion=False): """ Takes a conjunction of Horn literals and returns the corresponding SPARQL query """ queryType = isGround and "ASK" or "SELECT %s" % (' '.join( [v.n3() for v in vars])) queryShell = len(conjunct) > 1 and "%s {\n%s\n}" or "%s { %s }" if symmAtomicInclusion: if vars: var = vars.pop() prefix = "%s a ?KIND" % var.n3() else: prefix = "%s a ?KIND" % first( [first(iterCondition(lit)).arg[0].n3() for lit in conjunct]) conjunct = (i.formulae[0] if isinstance(i, And) else i for i in conjunct) subquery = queryShell % ( queryType, "%s\nFILTER(%s)" % (prefix, ' ||\n'.join( ['?KIND = %s' % edb.qname(GetOp(lit)) for lit in conjunct]))) else: subquery = queryShell % (queryType, ' .\n'.join( ['\t' + tripleToTriplePattern(edb, lit) for lit in conjunct])) return subquery
def unprocessedPreds(aPredCol): rt = [] for p in aPredCol: if not p.marked: rt.append(p) if p not in goalDict: goalDict.setdefault(GetOp(p), set()).add(p) return rt
def SetupMetaInterpreter(tBoxGraph, goal, useThingRule=True): from FuXi.LP.BackwardFixpointProcedure import BackwardFixpointProcedure from FuXi.Rete.Magic import SetupDDLAndAdornProgram from FuXi.Horn.PositiveConditions import BuildUnitermFromTuple from FuXi.Rete.TopDown import PrepareSipCollection from FuXi.DLP import LloydToporTransformation, makeRule from FuXi.Rete.SidewaysInformationPassing import GetOp owlThingAppears = False if useThingRule and OWL.Thing in tBoxGraph.all_nodes(): owlThingAppears = True completionRules = HornFromN3(StringIO(RULES)) if owlThingAppears: completionRules.formulae.extend( HornFromN3(StringIO(CONDITIONAL_THING_RULE))) reducedCompletionRules = set() for rule in completionRules: for clause in LloydToporTransformation(rule.formula): rule = makeRule(clause, {}) # log.debug(rule) # PrettyPrintRule(rule) reducedCompletionRules.add(rule) network = SetupRuleStore(makeNetwork=True)[-1] SetupDDLAndAdornProgram( tBoxGraph, reducedCompletionRules, [goal], derivedPreds=derivedPredicates, ignoreUnboundDPreds=True, hybridPreds2Replace=hybridPredicates) lit = BuildUnitermFromTuple(goal) op = GetOp(lit) lit.setOperator(URIRef(op + u'_derived')) goal = lit.toRDFTuple() sipCollection = PrepareSipCollection(reducedCompletionRules) tBoxGraph.templateMap = {} bfp = BackwardFixpointProcedure( tBoxGraph, network, derivedPredicates, goal, sipCollection, hybridPredicates=hybridPredicates, debug=True) bfp.createTopDownReteNetwork(True) log.debug(reducedCompletionRules) rt = bfp.answers(debug=True) log.debug(rt) log.debug(bfp.metaInterpNetwork) bfp.metaInterpNetwork.reportConflictSet(True, sys.stderr) for query in bfp.edbQueries: log.debug("Dispatched query against dataset: ", query.asSPARQL()) log.debug(list(bfp.goalSolutions))
def IsAtomicInclusionAxiomRHS(rule, dPreds): """ This is an atomic inclusion axiom with a variable (or bound) RHS: uniPred(?ENTITY) """ bodyList = list(iterCondition(rule.formula.body)) body = first(bodyList) return GetOp(body) not in dPreds and \ len(bodyList) == 1 and \ body.op == RDF.type
def PrepareSipCollection(adornedRuleset): """ Takes adorned ruleset and returns an RDF dataset formed from the sips associated with each adorned rule as named graphs. Also returns a mapping from the head predicates of each rule to the rules that match it - for efficient retrieval later """ headToRule = {} graphs = [] secondOrderRules = set() for rule in adornedRuleset: ruleHead = GetOp(rule.formula.head) if isinstance(ruleHead, Variable): #We store second order rules (i.e., rules whose head is a #predicate occurrence whose predicate symbol is a variable) aside secondOrderRules.add(rule) headToRule.setdefault(ruleHead, set()).add(rule) if hasattr(rule, 'sip'): graphs.append(rule.sip) # Second order rules are mapped from a None key (in order # to indicate they are wildcards) headToRule[None] = secondOrderRules if not graphs: return graph = ReadOnlyGraphAggregate(graphs) graph.headToRule = headToRule return graph
def DerivedPredicateIterator(factsOrBasePreds, ruleset, strict=DDL_STRICTNESS_FALLBACK_DERIVED, defaultPredicates=None): if not defaultPredicates: defaultPredicates = [], [] defaultBasePreds, defaultDerivedPreds = defaultPredicates basePreds = [ GetOp(buildUniTerm(fact)) for fact in factsOrBasePreds if fact[1] != LOG.implies ] processed = {True: set(), False: set()} derivedPreds = set() uncertainPreds = set() ruleBodyPreds = set() ruleHeads = set() for rule in ruleset: if rule.formula.body: for idx, term in enumerate( itertools.chain(iterCondition(rule.formula.head), iterCondition(rule.formula.body))): # iterate over terms from head to end of body op = GetOp(term) if op not in processed[idx > 0]: # not processed before if idx > 0: # body literal ruleBodyPreds.add(op) else: # head literal ruleHeads.add(op) if strict in DDL_MUST_CHECK and \ not (op not in basePreds or idx > 0): # checking DDL well formedness and # op is a base predicate *and* a head literal (derived) if strict in DDL_FALLBACK: mark = strict == DDL_STRICTNESS_FALLBACK_DERIVED and \ 'derived' or 'base' if strict == DDL_STRICTNESS_FALLBACK_DERIVED and \ op not in defaultBasePreds: # a clashing predicate is marked as derived due # to level of strictness derivedPreds.add(op) elif strict == DDL_STRICTNESS_FALLBACK_BASE and \ op not in defaultDerivedPreds: # a clashing predicate is marked as base dur # to level of strictness defaultBasePreds.append(op) import warnings warnings.warn( "predicate symbol of %s is in both IDB and EDB. Marking as %s" % (term, mark)) else: raise SyntaxError( "%s is a member of a derived predicate and a base predicate." % term) if op in basePreds: # base predicates are marked for later validation uncertainPreds.add(op) else: if idx == 0 and not isinstance(op, Variable): # head literal with proper predicate symbol # identify as a derived predicate derivedPreds.add(op) elif not isinstance(op, Variable): # body literal with proper predicate symbol # mark for later validation uncertainPreds.add(op) processed[idx > 0].add(op) for pred in uncertainPreds: # for each predicate marked as 'uncertain' # do further checking if (pred not in ruleBodyPreds and not isinstance(pred, Variable)) or\ pred in ruleHeads: # pred is not in a body literal and is a proper predicate symbol # or it is a rule head -> mark as a derived predicate derivedPreds.add(pred) for pred in derivedPreds: if not pred in defaultBasePreds: yield pred
def conjunctiveSipStrategy(self, goalsRemaining, factGraph, bindings=None): """ Given a conjunctive set of triples, invoke sip-strategy passing on intermediate solutions to facilitate 'join' behavior """ bindings = bindings if bindings else {} try: tp = next(goalsRemaining) assert isinstance(bindings, dict) dPred = self.derivedPredicateFromTriple(tp) if dPred is None: baseEDBQuery = EDBQuery([BuildUnitermFromTuple(tp)], self.edb, bindings=bindings) if self.DEBUG: print("Evaluating TP against EDB:%s" % baseEDBQuery.asSPARQL()) query, rt = baseEDBQuery.evaluate() # _vars = baseEDBQuery.returnVars for item in rt: bindings.update(item) for ansDict in self.conjunctiveSipStrategy( goalsRemaining, factGraph, bindings): yield ansDict else: queryLit = BuildUnitermFromTuple(tp) currentOp = GetOp(queryLit) queryLit.setOperator(currentOp) query = EDBQuery([queryLit], self.edb, bindings=bindings) if bindings: tp = first(query.formulae).toRDFTuple() if self.DEBUG: print("Goal/Query: ", query.asSPARQL()) SetupDDLAndAdornProgram( self.edb, self.idb, [tp], derivedPreds=self.derivedPredicates, ignoreUnboundDPreds=True, hybridPreds2Replace=self.hybridPredicates) if self.hybridPredicates: lit = BuildUnitermFromTuple(tp) op = GetOp(lit) if op in self.hybridPredicates: lit.setOperator(URIRef(op + u'_derived')) tp = lit.toRDFTuple() sipCollection = PrepareSipCollection(self.edb.adornedProgram) if self.DEBUG and sipCollection: for sip in SIPRepresentation(sipCollection): print(sip) pprint(list(self.edb.adornedProgram), sys.stderr) elif self.DEBUG: print("No SIP graph.") for nextAnswer, ns in self.invokeDecisionProcedure( tp, factGraph, bindings, self.DEBUG, sipCollection): nonGroundGoal = isinstance(nextAnswer, dict) if nonGroundGoal or nextAnswer: #Either we recieved bindings from top-down evaluation #or we (successfully) proved a ground query if not nonGroundGoal: #Attempt to prove a ground query, return the response rt = nextAnswer else: #Recieved solutions to 'open' query, merge with given bindings #and continue rt = mergeMappings1To2(bindings, nextAnswer) #either answers were provided (the goal wasn't grounded) or #the goal was ground and successfully proved for ansDict in self.conjunctiveSipStrategy( goalsRemaining, factGraph, rt): yield ansDict except StopIteration: yield bindings
def sparql_query(self, queryString, queryObj, graph, dataSetBase, extensionFunctions, initBindings={}, initNs={}, DEBUG=False): """ The default 'native' SPARQL implementation is based on sparql-p's expansion trees layered on top of the read-only RDF APIs of the underlying store """ from rdflib.sparql.Algebra import TopEvaluate from rdflib.QueryResult import QueryResult from rdflib import plugin from rdflib.sparql.bison.Query import AskQuery _expr = self.isaBaseQuery(None, queryObj) if isinstance(queryObj.query, AskQuery) and \ _expr.name == 'BGP': # isinstance(_expr, BasicGraphPattern): #This is a ground, BGP, involving IDB and can be solved directly #using top-down decision procedure #First separate out conjunct into EDB and IDB predicates #(solving the former first) from FuXi.SPARQL import EDBQuery groundConjunct = [] derivedConjunct = [] for s, p, o, func in _expr.patterns: if self.derivedPredicateFromTriple((s, p, o)) is None: groundConjunct.append(BuildUnitermFromTuple((s, p, o))) else: derivedConjunct.append(BuildUnitermFromTuple((s, p, o))) if groundConjunct: baseEDBQuery = EDBQuery(groundConjunct, self.edb) subQuery, ans = baseEDBQuery.evaluate(DEBUG) assert isinstance(ans, bool), ans if groundConjunct and not ans: askResult = False else: askResult = True for derivedLiteral in derivedConjunct: goal = derivedLiteral.toRDFTuple() #Solve ground, derived goal directly SetupDDLAndAdornProgram( self.edb, self.idb, [goal], derivedPreds=self.derivedPredicates, ignoreUnboundDPreds=True, hybridPreds2Replace=self.hybridPredicates) if self.hybridPredicates: lit = BuildUnitermFromTuple(goal) op = GetOp(lit) if op in self.hybridPredicates: lit.setOperator(URIRef(op + u'_derived')) goal = lit.toRDFTuple() sipCollection = PrepareSipCollection( self.edb.adornedProgram) if self.DEBUG and sipCollection: for sip in SIPRepresentation(sipCollection): print(sip) pprint(list(self.edb.adornedProgram)) elif self.DEBUG: print("No SIP graph.") rt, node = first( self.invokeDecisionProcedure(goal, self.edb, {}, self.DEBUG, sipCollection)) if not rt: askResult = False break return plugin.get('SPARQLQueryResult', QueryResult)(askResult) else: rt = TopEvaluate(queryObj, graph, initBindings, DEBUG=self.DEBUG, dataSetBase=dataSetBase, extensionFunctions=extensionFunctions) return plugin.get('SPARQLQueryResult', QueryResult)(rt)
def SipStrategy(query, sipCollection, factGraph, derivedPreds, bindings={}, processedRules=None, network=None, debug=False, buildProof=False, memoizeMemory=None, proofLevel=1): """ Accordingly, we define a sip-strategy for computing the answers to a query expressed using a set of Datalog rules, and a set of sips, one for each adornment of a rule head, as follows... Each evaluation uses memoization (via Python decorators) but also relies on well-formed rewrites for using semi-naive bottom up method over large SPARQL data. """ memoizeMemory = memoizeMemory and memoizeMemory or {} queryLiteral = BuildUnitermFromTuple(query) processedRules = processedRules and processedRules or set() if bindings: #There are bindings. Apply them to the terms in the query queryLiteral.ground(bindings) if debug: print("%sSolving" % ('\t' * proofLevel), queryLiteral, bindings) # Only consider ground triple pattern isomorphism with matching bindings goalRDFStatement = queryLiteral.toRDFTuple() if queryLiteral in memoizeMemory: if debug: print("%sReturning previously calculated results for " % \ ('\t' * proofLevel), queryLiteral) for answers in memoizeMemory[queryLiteral]: yield answers elif AlphaNode(goalRDFStatement).alphaNetworkHash( True, skolemTerms=list(bindings.values())) in \ [AlphaNode(r.toRDFTuple()).alphaNetworkHash(True, skolemTerms=list(bindings.values())) for r in processedRules if AdornLiteral(goalRDFStatement).adornment == \ r.adornment]: if debug: print("%s Goal already processed..." % \ ('\t' * proofLevel)) else: isGround = literalIsGround(queryLiteral) if buildProof: ns = NodeSet(goalRDFStatement, network=network, identifier=BNode()) else: ns = None # adornedProgram = factGraph.adornedProgram queryPred = GetOp(queryLiteral) if sipCollection is None: rules = [] else: #For every rule head matching the query, we invoke the rule, #thus determining an adornment, and selecting a sip to follow rules = sipCollection.headToRule.get(queryPred, set()) if None in sipCollection.headToRule: #If there are second order rules, we add them #since they are a 'wildcard' rules.update(sipCollection.headToRule[None]) #maintained list of rules that haven't been processed before and #match the query validRules = [] #each subquery contains values for the bound arguments that are passed #through the sip arcs entering the node corresponding to that literal. For #each subquery generated, there is a set of answers. answers = [] # variableMapping = {} #Some TBox queries can be 'joined' together into SPARQL queries against #'base' predicates via an RDF dataset #These atomic concept inclusion axioms can be evaluated together #using a disjunctive operator at the body of a horn clause #where each item is a query of the form uniPredicate(?X): #Or( uniPredicate1(?X1), uniPredicate2(?X), uniPredicate3(?X), ..) #In this way massive, conjunctive joins can be 'mediated' #between the stated facts and the top-down solver @parameterizedPredicate([i for i in derivedPreds]) def IsAtomicInclusionAxiomRHS(rule, dPreds): """ This is an atomic inclusion axiom with a variable (or bound) RHS: uniPred(?ENTITY) """ bodyList = list(iterCondition(rule.formula.body)) body = first(bodyList) return GetOp(body) not in dPreds and \ len(bodyList) == 1 and \ body.op == RDF.type atomicInclusionAxioms = list(filter(IsAtomicInclusionAxiomRHS, rules)) if atomicInclusionAxioms and len(atomicInclusionAxioms) > 1: if debug: print("\tCombining atomic inclusion axioms: ") pprint(atomicInclusionAxioms, sys.stderr) if buildProof: factStep = InferenceStep(ns, source='some RDF graph') ns.steps.append(factStep) axioms = [rule.formula.body for rule in atomicInclusionAxioms] #attempt to exaustively apply any available substitutions #and determine if query if fully ground vars = [ v for v in GetArgs(queryLiteral, secondOrder=True) if isinstance(v, Variable) ] openVars, axioms, _bindings = \ normalizeBindingsAndQuery(vars, bindings, axioms) if openVars: # mappings = {} #See if we need to do any variable mappings from the query literals #to the literals in the applicable rules query, rt = EDBQuery(axioms, factGraph, openVars, _bindings).evaluate( debug, symmAtomicInclusion=True) if buildProof: factStep.groundQuery = subquery for ans in rt: if buildProof: factStep.bindings.update(ans) memoizeMemory.setdefault(queryLiteral, set()).add( (prepMemiozedAns(ans), ns)) yield ans, ns else: #All the relevant derivations have been explored and the result #is a ground query we can directly execute against the facts if buildProof: factStep.bindings.update(bindings) query, rt = EDBQuery(axioms, factGraph, _bindings).evaluate( debug, symmAtomicInclusion=True) if buildProof: factStep.groundQuery = subquery memoizeMemory.setdefault(queryLiteral, set()).add( (prepMemiozedAns(rt), ns)) yield rt, ns rules = filter(lambda i: not IsAtomicInclusionAxiomRHS(i), rules) for rule in rules: #An exception is the special predicate ph; it is treated as a base #predicate and the tuples in it are those supplied for qb by unification. headBindings = getBindingsFromLiteral(goalRDFStatement, rule.formula.head) # comboBindings = dict([(k, v) for k, v in itertools.chain( # bindings.items(), # headBindings.items())]) varMap = rule.formula.head.getVarMapping(queryLiteral) if headBindings and\ [term for term in rule.formula.head.getDistinguishedVariables(True) if varMap.get(term, term) not in headBindings]: continue # subQueryAnswers = [] # dontStop = True # projectedBindings = comboBindings.copy() if debug: print("%sProcessing rule" % \ ('\t' * proofLevel), rule.formula) if debug and sipCollection: print("Sideways Information Passing (sip) graph for %s: " % queryLiteral) print(sipCollection.serialize(format='n3')) for sip in SIPRepresentation(sipCollection): print(sip) try: # Invoke the rule if buildProof: step = InferenceStep(ns, rule.formula) else: step = None for rt, step in\ invokeRule([headBindings], iter(iterCondition(rule.formula.body)), rule.sip, (proofLevel + 1, memoizeMemory, sipCollection, factGraph, derivedPreds, processedRules.union([ AdornLiteral(query)])), step=step, debug=debug): if rt: if isinstance(rt, dict): #We received a mapping and must rewrite it via #correlation between the variables in the rule head #and the variables in the original query (after applying #bindings) varMap = rule.formula.head.getVarMapping( queryLiteral) if varMap: rt = MakeImmutableDict( refactorMapping(varMap, rt)) if buildProof: step.bindings = rt else: if buildProof: step.bindings = headBindings validRules.append(rule) if buildProof: ns.steps.append(step) if isGround: yield True, ns else: memoizeMemory.setdefault(queryLiteral, set()).add( (prepMemiozedAns(rt), ns)) yield rt, ns except RuleFailure: # Clean up failed antecedents if buildProof: if ns in step.antecedents: step.antecedents.remove(ns) if not validRules: #No rules matching, query factGraph for answers successful = False if buildProof: factStep = InferenceStep(ns, source='some RDF graph') ns.steps.append(factStep) if not isGround: subquery, rt = EDBQuery([queryLiteral], factGraph, [ v for v in GetArgs(queryLiteral, secondOrder=True) if isinstance(v, Variable) ], bindings).evaluate(debug) if buildProof: factStep.groundQuery = subquery for ans in rt: successful = True if buildProof: factStep.bindings.update(ans) memoizeMemory.setdefault(queryLiteral, set()).add( (prepMemiozedAns(ans), ns)) yield ans, ns if not successful and queryPred not in derivedPreds: #Open query didn't return any results and the predicate #is ostensibly marked as derived predicate, so we have failed memoizeMemory.setdefault(queryLiteral, set()).add( (False, ns)) yield False, ns else: #All the relevant derivations have been explored and the result #is a ground query we can directly execute against the facts if buildProof: factStep.bindings.update(bindings) subquery, rt = EDBQuery([queryLiteral], factGraph, bindings).evaluate(debug) if buildProof: factStep.groundQuery = subquery memoizeMemory.setdefault(queryLiteral, set()).add( (prepMemiozedAns(rt), ns)) yield rt, ns
def sparqlResolvableNoTemplates(literal): if isinstance(literal, Uniterm): return not literal.naf and GetOp(literal) not in derivedPreds else: return False
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")
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 IdentifyDerivedPredicates(ddlMetaGraph, tBox, ruleset=None): """ See: http://code.google.com/p/fuxi/wiki/DataDescriptionLanguage# """ dPreds = set() basePreds = set() DDL = Namespace( 'http://code.google.com/p/fuxi/wiki/DataDescriptionLanguage#') if ruleset: for rule in ruleset: dPreds.add(GetOp(rule.formula.head)) for derivedClassList in ddlMetaGraph.subjects(predicate=RDF.type, object=DDL.DerivedClassList): dPreds.update(Collection(ddlMetaGraph, derivedClassList)) for derivedClassList in ddlMetaGraph.subjects( predicate=RDF.type, object=DDL.DerivedPropertyList): dPreds.update(Collection(ddlMetaGraph, derivedClassList)) derivedPropPrefixes = [] basePropPrefixes = [] for derivedPropPrefixList in ddlMetaGraph.subjects( predicate=RDF.type, object=DDL.DerivedPropertyPrefix): derivedPropPrefixes.extend( Collection(ddlMetaGraph, derivedPropPrefixList)) for basePropPrefixList in ddlMetaGraph.subjects( predicate=RDF.type, object=DDL.BasePropertyPrefix): basePropPrefixes.extend(Collection(ddlMetaGraph, basePropPrefixList)) for prop in tBox.query(OWL_PROPERTIES_QUERY): if first(filter(lambda prefix: prop.startswith(prefix), derivedPropPrefixes)) and \ (prop, RDF.type, OWL_NS.AnnotationProperty) not in tBox: dPreds.add(prop) if first(filter(lambda prefix: prop.startswith(prefix), basePropPrefixes)) and \ (prop, RDF.type, OWL_NS.AnnotationProperty) not in tBox and \ prop not in dPreds: basePreds.add(prop) derivedClassPrefixes = [] for derivedClsPrefixList in ddlMetaGraph.subjects( predicate=RDF.type, object=DDL.DerivedClassPrefix): derivedClassPrefixes.extend( Collection(ddlMetaGraph, derivedClsPrefixList)) baseClassPrefixes = [] for baseClsPrefixList in ddlMetaGraph.subjects(predicate=RDF.type, object=DDL.BaseClassPrefix): baseClassPrefixes.extend(Collection(ddlMetaGraph, baseClsPrefixList)) for cls in tBox.subjects(predicate=RDF.type, object=OWL_NS.Class): if first( filter(lambda prefix: cls.startswith(prefix), baseClassPrefixes)): if cls not in dPreds: basePreds.add(cls) if first( filter(lambda prefix: cls.startswith(prefix), derivedClassPrefixes)): if cls not in basePreds: dPreds.add(cls) nsBindings = dict([(prefix, nsUri) for prefix, nsUri in itertools.chain( tBox.namespaces(), ddlMetaGraph.namespaces()) if prefix]) for queryNode in ddlMetaGraph.subjects(predicate=RDF.type, object=DDL.DerivedClassQuery): query = first(ddlMetaGraph.objects(queryNode, RDF.value)) for cls in tBox.query(query, initNs=nsBindings): dPreds.add(cls) for baseClsList in ddlMetaGraph.subjects(predicate=RDF.type, object=DDL.BaseClassList): basePreds.update(Collection(ddlMetaGraph, baseClsList)) dPreds.difference_update(basePreds) return dPreds
def SetupDDLAndAdornProgram(factGraph, rules, GOALS, derivedPreds=None, strictCheck=DDL_STRICTNESS_FALLBACK_DERIVED, defaultPredicates=None, ignoreUnboundDPreds=False, hybridPreds2Replace=None): if not defaultPredicates: defaultPredicates = [], [] # _dPredsProvided = bool(derivedPreds) if not derivedPreds: _derivedPreds = DerivedPredicateIterator( factGraph, rules, strict=strictCheck, defaultPredicates=defaultPredicates) if not isinstance(derivedPreds, (set, list)): derivedPreds = list(_derivedPreds) else: derivedPreds.extend(_derivedPreds) hybridPreds2Replace = hybridPreds2Replace or [] adornedProgram = AdornProgram(factGraph, rules, GOALS, derivedPreds, ignoreUnboundDPreds, hybridPreds2Replace=hybridPreds2Replace) if adornedProgram != set([]): rt = reduce(lambda l, r: l + r, [ list(iterCondition(clause.formula.body)) for clause in adornedProgram ]) else: rt = set() for hybridPred, adornment in [ (t, a) for t, a in set([(URIRef(GetOp(term).split('_derived')[0] ) if GetOp(term).find('_derived') + 1 else GetOp(term), ''.join(term.adornment)) for term in rt if isinstance(term, AdornedUniTerm)]) if t in hybridPreds2Replace ]: #If there are hybrid predicates, add rules that derived their IDB counterpart #using information from the adorned queries to determine appropriate arity #and adornment hybridPred = URIRef(hybridPred) hPred = URIRef(hybridPred + u'_derived') if len(adornment) == 1: # p_derived^{a}(X) :- p(X) body = BuildUnitermFromTuple((Variable('X'), RDF.type, hybridPred)) head = BuildUnitermFromTuple((Variable('X'), RDF.type, hPred)) else: # p_derived^{a}(X, Y) :- p(X, Y) body = BuildUnitermFromTuple( (Variable('X'), hybridPred, Variable('Y'))) head = BuildUnitermFromTuple((Variable('X'), hPred, Variable('Y'))) _head = AdornedUniTerm(head, list(adornment)) rule = AdornedRule(Clause(And([body]), _head.clone())) rule.sip = Graph() adornedProgram.add(rule) if factGraph is not None: factGraph.adornedProgram = adornedProgram return adornedProgram
def MagicSetTransformation(factGraph, rules, GOALS, derivedPreds=None, strictCheck=DDL_STRICTNESS_FALLBACK_DERIVED, noMagic=None, defaultPredicates=None): """ Takes a goal and a ruleset and returns an iterator over the rulest that corresponds to the magic set transformation: """ noMagic = noMagic and noMagic or [] magicPredicates = set() # replacement = {} adornedProgram = SetupDDLAndAdornProgram( factGraph, rules, GOALS, derivedPreds=derivedPreds, strictCheck=strictCheck, defaultPredicates=defaultPredicates) newRules = [] for rule in adornedProgram: if rule.isSecondOrder(): import warnings warnings.warn("Second order rule no supported by GMS: %s" % rule, RuntimeWarning) magicPositions = {} #Generate magic rules for idx, pred in enumerate(iterCondition(rule.formula.body)): # magicBody = [] if isinstance(pred, AdornedUniTerm): # and pred not in magicPredicates: # For each rule r in Pad, and for each occurrence of an adorned # predicate p a in its body, we generate a magic rule defining magic_p a prevPreds = [ item for _idx, item in enumerate(rule.formula.body) if _idx < idx ] if 'b' not in pred.adornment: import warnings warnings.warn( "adorned predicate w/out any bound arguments (%s in %s)" % (pred, rule.formula), RuntimeWarning) if GetOp(pred) not in noMagic: magicPred = pred.makeMagicPred() magicPositions[idx] = (magicPred, pred) inArcs = [(N, x) for ( N, x) in IncomingSIPArcs(rule.sip, getOccurrenceId(pred)) if not set(x).difference(GetArgs(pred))] if len(inArcs) > 1: # If there are several arcs entering qi, we define the # magic rule defining magic_qi in two steps. First, # for each arc Nj --> qi with label cj , we define a # rule with head label_qi_j(cj ). The body of the rule # is the same as the body of the magic rule in the # case where there is a single arc entering qi # (described above). Then the magic rule is defined as # follows. The head is magic_q(0). The body contains # label_qi_j(cj) for all j (that is, for all arcs # entering qi ). # # We combine all incoming arcs into a single list of # (body) conditions for the magic set PrettyPrintRule(rule) SIPRepresentation(rule.sip) print(pred, magicPred) _body = [] additionalRules = [] for idxSip, (N, x) in enumerate(inArcs): newPred = pred.clone() SetOp(newPred, URIRef('%s_label_%s' % (newPred.op, idxSip))) ruleBody = And( buildMagicBody(N, prevPreds, rule.formula.head, derivedPreds)) additionalRules.append( Rule(Clause(ruleBody, newPred))) _body.extend(newPred) # _body.extend(ruleBody) additionalRules.append( Rule(Clause(And(_body), magicPred))) newRules.extend(additionalRules) for i in additionalRules: print(i) raise NotImplementedError() else: for idxSip, (N, x) in enumerate(inArcs): ruleBody = And( buildMagicBody(N, prevPreds, rule.formula.head, derivedPreds, noMagic)) newRule = Rule(Clause(ruleBody, magicPred)) newRules.append(newRule) magicPredicates.add(magicPred) # Modify rules # we modify the original rule by inserting # occurrences of the magic predicates corresponding # to the derived predicates of the body and to the head # If there are no bound arguments in the head, we don't modify the rule idxIncrement = 0 newRule = copy.deepcopy(rule) for idx, (magicPred, origPred) in list(magicPositions.items()): newRule.formula.body.formulae.insert(idx + idxIncrement, magicPred) idxIncrement += 1 if 'b' in rule.formula.head.adornment and GetOp( rule.formula.head) not in noMagic: headMagicPred = rule.formula.head.makeMagicPred() if isinstance(newRule.formula.body, Uniterm): newRule.formula.body = And( [headMagicPred, newRule.formula.body]) else: newRule.formula.body.formulae.insert(0, headMagicPred) newRules.append(newRule) if not newRules: newRules.extend(AdditionalRules(factGraph)) for rule in newRules: if rule.formula.body: yield rule
def termHash(term): return GetOp(term), \ reduce(lambda x, y: x + y, term.adornment)
def AdornRule(derivedPreds, clause, newHead, ignoreUnboundDPreds=False, hybridPreds2Replace=None): """ Adorns a horn clause using the given new head and list of derived predicates """ assert len(list(iterCondition(clause.head))) == 1 hybridPreds2Replace = hybridPreds2Replace or [] adornedHead = AdornedUniTerm(clause.head, newHead.adornment) sip = BuildNaturalSIP(clause, derivedPreds, adornedHead, hybridPreds2Replace=hybridPreds2Replace, ignoreUnboundDPreds=ignoreUnboundDPreds) bodyPredReplace = {} def adornment(arg, headArc, x): if headArc: # Sip arc from head # don't mark bound if query has no bound/distinguished terms return (arg in x and arg in adornedHead.getDistinguishedVariables(True)) \ and 'b' or 'f' else: return arg in x and 'b' or 'f' for literal in iterCondition(sip.sipOrder): op = GetOp(literal) args = GetArgs(literal) if op in derivedPreds or (op in hybridPreds2Replace if hybridPreds2Replace else False): for N, x in IncomingSIPArcs(sip, getOccurrenceId(literal)): headArc = len(N) == 1 and N[0] == GetOp(newHead) if not set(x).difference(args): # A binding # for q is useful, however, only if it is a binding for an argument of q. bodyPredReplace[literal] = AdornedUniTerm( NormalizeUniterm(literal), [adornment(arg, headArc, x) for arg in args], literal.naf) # For a predicate occurrence with no incoming # arc, the adornment contains only f. For our purposes here, # we do not distinguish between a predicate with such an # adornment and an unadorned predicate (we do in order to support open queries) if literal not in bodyPredReplace and ignoreUnboundDPreds: bodyPredReplace[literal] = AdornedUniTerm( NormalizeUniterm(literal), ['f' for arg in GetArgs(literal)], literal.naf) if hybridPreds2Replace: atomPred = GetOp(adornedHead) if atomPred in hybridPreds2Replace: adornedHead.setOperator(URIRef(atomPred + u'_derived')) for bodAtom in [ bodyPredReplace.get(p, p) for p in iterCondition(sip.sipOrder) ]: bodyPred = GetOp(bodAtom) if bodyPred in hybridPreds2Replace: bodAtom.setOperator(URIRef(bodyPred + u'_derived')) rule = AdornedRule( Clause( And([ bodyPredReplace.get(p, p) for p in iterCondition(sip.sipOrder) ]), adornedHead)) rule.sip = sip return rule
def AdornProgram(factGraph, rs, goals, derivedPreds=None, ignoreUnboundDPreds=False, hybridPreds2Replace=None): """ The process starts from the given query. The query determines bindings for q, and we replace q by an adorned version, in which precisely the positions bound in the query are designated as bound, say q e . In general, we have a collection of adorned predicates, and as each one is processed, we will mark it, so that it will not be processed again. If p a is an unmarked adorned predicate, then for each rule that has p in its head, we generate an adorned version for the rule and add it to Pad; then p is marked as processed. The adorned version of a rule contains additional adorned predicates, and these are added to the collection, unless they already appear there. The process terminates when no unmarked adorned predicates are left. """ from FuXi.DLP import LloydToporTransformation from collections import deque goalDict = {} hybridPreds2Replace = hybridPreds2Replace or [] adornedPredicateCollection = set() for goal, nsBindings in NormalizeGoals(goals): adornedPredicateCollection.add(AdornLiteral(goal, nsBindings)) if not derivedPreds: derivedPreds = list(DerivedPredicateIterator(factGraph, rs)) def unprocessedPreds(aPredCol): rt = [] for p in aPredCol: if not p.marked: rt.append(p) if p not in goalDict: goalDict.setdefault(GetOp(p), set()).add(p) return rt toDo = deque(unprocessedPreds(adornedPredicateCollection)) adornedProgram = set() while len(toDo): term = toDo.popleft() #check if there is a rule with term as its head for rule in rs: for clause in LloydToporTransformation(rule.formula): head = isinstance( clause.head, Exists) and clause.head.formula or clause.head # headPredicate = GetOp(head) if compareAdornedPredToRuleHead(term, head, hybridPreds2Replace): #for each rule that has p in its head, we generate an adorned version for the rule adornedRule = AdornRule( derivedPreds, clause, term, ignoreUnboundDPreds=ignoreUnboundDPreds, hybridPreds2Replace=hybridPreds2Replace) adornedProgram.add(adornedRule) #The adorned version of a rule contains additional adorned #predicates, and these are added for pred in iterCondition(adornedRule.formula.body): if isinstance(pred, N3Builtin): aPred = pred else: aPred = not isinstance(pred, AdornedUniTerm) and \ AdornLiteral(pred.toRDFTuple(), nsBindings, pred.naf) or pred op = GetOp(pred) if (op in derivedPreds or (op in hybridPreds2Replace if hybridPreds2Replace else False) ) and aPred not in adornedPredicateCollection: adornedPredicateCollection.add(aPred) term.marked = True toDo.extendleft(unprocessedPreds(adornedPredicateCollection)) factGraph.queryAtoms = goalDict return adornedProgram
def IsHybridPredicate(pred, hybridPreds2Replace): op = GetOp(pred) return op[-7:] == 'derived' and op[:-8] in hybridPreds2Replace