def Th(owlGraph, _class, variable=Variable('X'), position=LHS): """ DLP head (antecedent) knowledge assertional forms (ABox assertions, conjunction of ABox assertions, and universal role restriction assertions) """ props = list(set(owlGraph.predicates(subject=_class))) if OWL_NS.allValuesFrom in props: #http://www.w3.org/TR/owl-semantics/#owl_allValuesFrom for s, p, o in owlGraph.triples((_class, OWL_NS.allValuesFrom, None)): prop = list(owlGraph.objects(subject=_class, predicate=OWL_NS.onProperty))[0] newVar = Variable(BNode()) body = Uniterm(prop, [variable, newVar], newNss=owlGraph.namespaces()) for head in Th(owlGraph, o, variable=newVar): yield Clause(body, head) elif OWL_NS.hasValue in props: prop = list(owlGraph.objects(subject=_class, predicate=OWL_NS.onProperty))[0] o = first(owlGraph.objects(subject=_class, predicate=OWL_NS.hasValue)) yield Uniterm(prop, [variable, o], newNss=owlGraph.namespaces()) elif OWL_NS.someValuesFrom in props: #http://www.w3.org/TR/owl-semantics/#someValuesFrom for s, p, o in owlGraph.triples((_class, OWL_NS.someValuesFrom, None)): prop = list(owlGraph.objects(subject=_class, predicate=OWL_NS.onProperty))[0] newVar = BNode() yield And([Uniterm(prop, [variable, newVar], newNss=owlGraph.namespaces()), generatorFlattener(Th(owlGraph, o, variable=newVar))]) elif OWL_NS.intersectionOf in props: from FuXi.Syntax.InfixOWL import BooleanClass yield And([first(Th(owlGraph, h, variable)) for h in BooleanClass(_class)]) else: #Simple class yield Uniterm(RDF.type, [variable, isinstance(_class, BNode) and SkolemizeExistentialClasses(_class) or _class], newNss=owlGraph.namespaces())
def extractImp(self, impl): body, bodyType, head, headType = self.implications[impl] head = first(self.extractPredication(head, headType)) if bodyType == RIF_NS.And: raise else: body = self.extractPredication(body, bodyType) body = And([first(body)]) if len(body) == 1 else And(body) return Rule(Clause(body, head), declare=[])
def Tb(owlGraph, _class, variable=Variable('X')): """ DLP body (consequent knowledge assertional forms (ABox assertions, conjunction / disjunction of ABox assertions, and exisential role restriction assertions) These are all common EL++ templates for KR """ props = list(set(owlGraph.predicates(subject=_class))) if OWL_NS.intersectionOf in props and not isinstance(_class, URIRef): for s, p, o in owlGraph.triples((_class, OWL_NS.intersectionOf, None)): conj = [] handleConjunct(conj, owlGraph, o, variable) return And(conj) elif OWL_NS.unionOf in props and not isinstance(_class, URIRef): # http://www.w3.org/TR/owl-semantics/#owl_unionOf for s, p, o in owlGraph.triples((_class, OWL_NS.unionOf, None)): return Or([ Tb(owlGraph, c, variable=variable) for c in Collection(owlGraph, o) ]) elif OWL_NS.someValuesFrom in props: # http://www.w3.org/TR/owl-semantics/#owl_someValuesFrom prop = list( owlGraph.objects(subject=_class, predicate=OWL_NS.onProperty))[0] o = list( owlGraph.objects(subject=_class, predicate=OWL_NS.someValuesFrom))[0] newVar = Variable(BNode()) # body = Uniterm(prop, [variable, newVar], newNss=owlGraph.namespaces()) # head = Th(owlGraph, o, variable=newVar) return And([ Uniterm(prop, [variable, newVar], newNss=owlGraph.namespaces()), Tb(owlGraph, o, variable=newVar) ]) elif OWL_NS.hasValue in props: # http://www.w3.org/TR/owl-semantics/#owl_hasValue # Domain-specific rules for hasValue # Can be achieved via pD semantics prop = list( owlGraph.objects(subject=_class, predicate=OWL_NS.onProperty))[0] o = first(owlGraph.objects(subject=_class, predicate=OWL_NS.hasValue)) return Uniterm(prop, [variable, o], newNss=owlGraph.namespaces()) elif OWL_NS.complementOf in props: return Tc(owlGraph, first(owlGraph.objects(_class, OWL_NS.complementOf))) else: # simple class # "Named" Uniterm _classTerm = SkolemizeExistentialClasses(_class) return Uniterm(RDF.type, [variable, _classTerm], newNss=owlGraph.namespaces())
def Tc(owlGraph, negatedFormula): """ Handles the conversion of negated DL concepts into a general logic programming condition for the body of a rule that fires when the body conjunct is in the minimal model """ if (negatedFormula, OWL_NS.hasValue, None) in owlGraph: #not ( R value i ) bodyUniTerm = Uniterm(RDF.type, [Variable("X"), NormalizeBooleanClassOperand(negatedFormula, owlGraph)], newNss=owlGraph.namespaces()) condition = NormalizeClause(Clause(Tb(owlGraph, negatedFormula), bodyUniTerm)).body assert isinstance(condition, Uniterm) condition.naf = True return condition elif (negatedFormula, OWL_NS.someValuesFrom, None) in owlGraph: #not ( R some C ) binaryRel, unaryRel = Tb(owlGraph, negatedFormula) negatedBinaryRel = copy.deepcopy(binaryRel) negatedBinaryRel.naf = True negatedUnaryRel = copy.deepcopy(unaryRel) negatedUnaryRel.naf = True return Or([negatedBinaryRel, And([binaryRel, negatedUnaryRel])]) elif isinstance(negatedFormula, URIRef): return Uniterm(RDF.type, [Variable("X"), NormalizeBooleanClassOperand(negatedFormula, owlGraph)], newNss=owlGraph.namespaces(), naf=True) else: raise UnsupportedNegation("Unsupported negated concept: %s" % negatedFormula)
def ApplyDemorgans(clause): """ >>> from FuXi.DLP import Clause >>> EX_NS = Namespace('http://example.com/') >>> ns = {'ex' : EX_NS} >>> pred1 = PredicateExtentFactory(EX_NS.somePredicate,newNss=ns) >>> pred2 = PredicateExtentFactory(EX_NS.somePredicate2,newNss=ns) >>> pred3 = PredicateExtentFactory(EX_NS.somePredicate3,binary=False,newNss=ns) >>> pred4 = PredicateExtentFactory(EX_NS.somePredicate4,binary=False,newNss=ns) >>> clause = Clause(And([pred1[(Variable('X'),Variable('Y'))], ... Or([pred2[(Variable('X'),EX_NS.individual1)], ... pred3[(Variable('Y'))]],naf=True)]), ... pred4[Variable('X')]) >>> clause ex:somePredicate4(?X) :- And( ex:somePredicate(?X ?Y) not Or( ex:somePredicate2(?X ex:individual1) ex:somePredicate3(?Y) ) ) >>> ApplyDemorgans(clause) >>> clause ex:somePredicate4(?X) :- And( ex:somePredicate(?X ?Y) And( not ex:somePredicate2(?X ex:individual1) not ex:somePredicate3(?Y) ) ) >>> FlattenConjunctions(clause.body) >>> clause ex:somePredicate4(?X) :- And( ex:somePredicate(?X ?Y) not ex:somePredicate2(?X ex:individual1) not ex:somePredicate3(?Y) ) """ from FuXi.DLP import breadth_first, breadth_first_replace replacementMap = {} for negDisj in [i for i in breadth_first(clause.body) if isinstance(i,Or) and i.naf]: replacementList = [] for innerTerm in negDisj: assert isinstance(i,Uniterm) innerTerm.naf = not innerTerm.naf replacementList.append(innerTerm) replacementMap[negDisj] = And(replacementList) for old,new in replacementMap.items(): list(breadth_first_replace(clause.body,candidate=old,replacement=new))
def extractRule(self, rule): vars, impl = self.rules[rule] body, bodyType, head, headType = self.implications[impl] allVars = map(self.extractTerm, Collection(self.graph, vars)) head = first(self.extractPredication(head, headType)) if bodyType == RIF_NS.And: body = [first(self.extractPredication( i, first(self.graph.objects(i, RDF.type))) ) for i in Collection(self.graph, first(self.graph.objects(body, RIF_NS.formulas)))] else: body = self.extractPredication(body, bodyType) body = And([first(body)]) if len(body) == 1 else And(body) return Rule( Clause(body, head), declare=allVars, nsMapping=dict(self.graph.namespaces()) )
def NormalizeBody(rule): from FuXi.Rete.RuleStore import N3Builtin # from itertools import groupby, chain newBody = [] builtIns = [] if isinstance(rule.formula.body, And): for lit in rule.formula.body: if isinstance(lit, N3Builtin): builtIns.append(lit) else: newBody.append(lit) newBody.extend(builtIns) rule.formula.body = And(newBody) return rule
def LloydToporTransformation(clause, fullReduction=True): """ Tautological, common horn logic forms (useful for normalizing conjunctive & disjunctive clauses) (H ^ H0) :- B -> { H :- B H0 :- B } (H :- H0) :- B -> H :- B ^ H0 H :- (B v B0) -> { H :- B H :- B0 } """ assert isinstance(clause, OriginalClause), repr(clause) assert isinstance(clause.body, Condition), repr(clause) if isinstance(clause.body, Or): for atom in clause.body.formulae: if hasattr(atom, 'next'): atom = first(atom) for clz in LloydToporTransformation( NormalizeClause( Clause(atom, clause.head) ), fullReduction=fullReduction): yield clz elif isinstance(clause.head, OriginalClause): yield NormalizeClause(Clause(And([clause.body, clause.head.body]), clause.head.head)) elif fullReduction and ( (isinstance(clause.head, Exists) and isinstance(clause.head.formula, And)) or isinstance(clause.head, And)): if isinstance(clause.head, Exists): head = clause.head.formula elif isinstance(clause.head, And): head = clause.head for i in head: for j in LloydToporTransformation(Clause(clause.body, i), fullReduction=fullReduction): if [i for i in breadth_first(j.head) if isinstance(i, And)]: #Ands in the head need to be further flattened yield PrepareHeadExistential(NormalizeClause(j)) else: yield PrepareHeadExistential(j) else: yield clause
def T(owlGraph, complementExpansions=[], derivedPreds=[]): """ #Subsumption (purely for TBOX classification) {?C rdfs:subClassOf ?SC. ?A rdfs:subClassOf ?C} => {?A rdfs:subClassOf ?SC}. {?C owl:equivalentClass ?A} => {?C rdfs:subClassOf ?A. ?A rdfs:subClassOf ?C}. {?C rdfs:subClassOf ?SC. ?SC rdfs:subClassOf ?C} => {?C owl:equivalentClass ?SC}. T(rdfs:subClassOf(C, D)) -> Th(D(y)) :- Tb(C(y)) T(owl:equivalentClass(C, D)) -> { T(rdfs:subClassOf(C, D) T(rdfs:subClassOf(D, C) } A generator over the Logic Programming rules which correspond to the DL ( unary predicate logic ) subsumption axiom described via rdfs:subClassOf """ for s, p, o in owlGraph.triples((None, OWL_NS.complementOf, None)): if isinstance(o, URIRef) and isinstance(s, URIRef): headLiteral = Uniterm( RDF.type, [Variable("X"), SkolemizeExistentialClasses(s)], newNss=owlGraph.namespaces()) yield NormalizeClause(Clause(Tc(owlGraph, o), headLiteral)) for c, p, d in owlGraph.triples((None, RDFS.subClassOf, None)): try: yield NormalizeClause(Clause(Tb(owlGraph, c), Th(owlGraph, d))) except UnsupportedNegation: warnings.warn( "Unable to handle negation in DL axiom (%s), skipping" % c, # e.msg, SyntaxWarning, 3) # assert isinstance(c, URIRef), "%s is a kind of %s"%(c, d) for c, p, d in owlGraph.triples((None, OWL_NS.equivalentClass, None)): if c not in derivedPreds: yield NormalizeClause(Clause(Tb(owlGraph, c), Th(owlGraph, d))) yield NormalizeClause(Clause(Tb(owlGraph, d), Th(owlGraph, c))) for s, p, o in owlGraph.triples((None, OWL_NS.intersectionOf, None)): try: if s not in complementExpansions: if s in derivedPreds: warnings.warn( "Derived predicate (%s) is defined via a conjunction (consider using a complex GCI) " % owlGraph.qname(s), SyntaxWarning, 3) elif isinstance( s, BNode): # and (None, None, s) not in owlGraph:# and \ # (s, RDFS.subClassOf, None) in owlGraph: # complex GCI, pass over (handled) by Tb continue conjunction = [] handleConjunct(conjunction, owlGraph, o) body = And(conjunction) head = Uniterm(RDF.type, [Variable("X"), SkolemizeExistentialClasses(s)], newNss=owlGraph.namespaces()) # O1 ^ O2 ^ ... ^ On => S(?X) yield Clause(body, head) if isinstance(s, URIRef): # S(?X) => O1 ^ O2 ^ ... ^ On # special case, owl:intersectionOf is a neccessary and sufficient # criteria and should thus work in *both* directions # This rule is not added for anonymous classes or derived # predicates if s not in derivedPreds: yield Clause(head, body) except UnsupportedNegation: warnings.warn( "Unable to handle negation in DL axiom (%s), skipping" % s, # e.msg, SyntaxWarning, 3) for s, p, o in owlGraph.triples((None, OWL_NS.unionOf, None)): if isinstance(s, URIRef): # special case, owl:unionOf is a neccessary and sufficient # criteria and should thus work in *both* directions body = Or([ Uniterm( RDF.type, [Variable("X"), NormalizeBooleanClassOperand(i, owlGraph)], newNss=owlGraph.namespaces()) for i in Collection(owlGraph, o) ]) head = Uniterm(RDF.type, [Variable("X"), s], newNss=owlGraph.namespaces()) yield Clause(body, head) for s, p, o in owlGraph.triples((None, OWL_NS.inverseOf, None)): # T(owl:inverseOf(P, Q)) -> { Q(x, y) :- P(y, x) # P(y, x) :- Q(x, y) } newVar = Variable(BNode()) s = SkolemizeExistentialClasses(s) if isinstance(s, BNode) else s o = SkolemizeExistentialClasses(o) if isinstance(o, BNode) else o body1 = Uniterm(s, [newVar, Variable("X")], newNss=owlGraph.namespaces()) head1 = Uniterm(o, [Variable("X"), newVar], newNss=owlGraph.namespaces()) yield Clause(body1, head1) newVar = Variable(BNode()) body2 = Uniterm(o, [Variable("X"), newVar], newNss=owlGraph.namespaces()) head2 = Uniterm(s, [newVar, Variable("X")], newNss=owlGraph.namespaces()) yield Clause(body2, head2) for s, p, o in owlGraph.triples( (None, RDF.type, OWL_NS.TransitiveProperty)): # T(owl:TransitiveProperty(P)) -> P(x, z) :- P(x, y) ^ P(y, z) y = Variable(BNode()) z = Variable(BNode()) x = Variable("X") s = SkolemizeExistentialClasses(s) if isinstance(s, BNode) else s body = And([ Uniterm(s, [x, y], newNss=owlGraph.namespaces()), Uniterm(s, [y, z], newNss=owlGraph.namespaces()) ]) head = Uniterm(s, [x, z], newNss=owlGraph.namespaces()) yield Clause(body, head) for s, p, o in owlGraph.triples((None, RDFS.subPropertyOf, None)): # T(rdfs:subPropertyOf(P, Q)) -> Q(x, y) :- P(x, y) x = Variable("X") y = Variable("Y") s = SkolemizeExistentialClasses(s) if isinstance(s, BNode) else s o = SkolemizeExistentialClasses(o) if isinstance(o, BNode) else o body = Uniterm(s, [x, y], newNss=owlGraph.namespaces()) head = Uniterm(o, [x, y], newNss=owlGraph.namespaces()) yield Clause(body, head) for s, p, o in owlGraph.triples((None, OWL_NS.equivalentProperty, None)): # T(owl:equivalentProperty(P, Q)) -> { Q(x, y) :- P(x, y) # P(x, y) :- Q(x, y) } x = Variable("X") y = Variable("Y") s = SkolemizeExistentialClasses(s) if isinstance(s, BNode) else s o = SkolemizeExistentialClasses(o) if isinstance(o, BNode) else o body = Uniterm(s, [x, y], newNss=owlGraph.namespaces()) head = Uniterm(o, [x, y], newNss=owlGraph.namespaces()) yield Clause(body, head) yield Clause(head, body) # Contribution (Symmetric DL roles) for s, p, o in owlGraph.triples( (None, RDF.type, OWL_NS.SymmetricProperty)): # T(owl:SymmetricProperty(P)) -> P(y, x) :- P(x, y) y = Variable("Y") x = Variable("X") s = SkolemizeExistentialClasses(s) if isinstance(s, BNode) else s body = Uniterm(s, [x, y], newNss=owlGraph.namespaces()) head = Uniterm(s, [y, x], newNss=owlGraph.namespaces()) yield Clause(body, head) for s, p, o in owlGraph.triples_choices((None, [RDFS.range, RDFS.domain], None)): s = SkolemizeExistentialClasses(s) if isinstance(s, BNode) else s if p == RDFS.range: # T(rdfs:range(P, D)) -> D(y) := P(x, y) x = Variable("X") y = Variable(BNode()) body = Uniterm(s, [x, y], newNss=owlGraph.namespaces()) head = Uniterm(RDF.type, [y, o], newNss=owlGraph.namespaces()) yield Clause(body, head) else: # T(rdfs:domain(P, D)) -> D(x) := P(x, y) x = Variable("X") y = Variable(BNode()) body = Uniterm(s, [x, y], newNss=owlGraph.namespaces()) head = Uniterm(RDF.type, [x, o], newNss=owlGraph.namespaces()) yield Clause(body, head)
def __init__(self, formulae=None, n3Rules=None, nsMapping=None): from FuXi.Rete.RuleStore import N3Builtin self.nsMapping = nsMapping and nsMapping or {} self.formulae = formulae and formulae or [] if n3Rules: from FuXi.DLP import breadth_first # Convert a N3 abstract model (parsed from N3) into a RIF BLD for lhs, rhs in n3Rules: allVars = set() for ruleCondition in [lhs, rhs]: for stmt in ruleCondition: if isinstance(stmt, N3Builtin): ExternalFunction(stmt, newNss=self.nsMapping) # print(stmt) # raise allVars.update([ term for term in stmt if isinstance(term, (BNode, Variable)) ]) body = [ isinstance(term, N3Builtin) and term or Uniterm(list(term)[1], [list(term)[0], list(term)[-1]], newNss=nsMapping) for term in lhs ] body = len(body) == 1 and body[0] or And(body) head = [ Uniterm(p, [s, o], newNss=nsMapping) for s, p, o in rhs ] head = len(head) == 1 and head[0] or And(head) # first we identify body variables bodyVars = set( reduce(lambda x, y: x + y, [ list(extractVariables(i, existential=False)) for i in iterCondition(body) ])) # then we identify head variables headVars = set( reduce(lambda x, y: x + y, [ list(extractVariables(i, existential=False)) for i in iterCondition(head) ])) # then we identify those variables that should (or should not) # be converted to skolem terms updateDict = dict([(var, BNode()) for var in headVars if var not in bodyVars]) for uniTerm in iterCondition(head): def updateUniterm(uterm): newArg = [updateDict.get(i, i) for i in uniTerm.arg] uniTerm.arg = newArg if isinstance(uniTerm, Uniterm): updateUniterm(uniTerm) else: for u in uniTerm: updateUniterm(u) exist = [ list(extractVariables(i)) for i in breadth_first(head) ] e = Exists(formula=head, declare=set(reduce(lambda x, y: x + y, exist, []))) if reduce(lambda x, y: x + y, exist): head = e assert e.declare, exist self.formulae.append(Rule(Clause(body, head), declare=allVars))
def BuildNaturalSIP(clause, derivedPreds, adornedHead, hybridPreds2Replace=None, ignoreUnboundDPreds=False): """ Natural SIP: Informally, for a rule of a program, a sip represents a decision about the order in which the predicates of the rule will be evaluated, and how values for variables are passed from predicates to other predicates during evaluation >>> from functools import reduce >>> from io import StringIO >>> from FuXi.Rete.RuleStore import SetupRuleStore >>> from FuXi.Rete import PROGRAM2 >>> ruleStore, ruleGraph = SetupRuleStore(StringIO(PROGRAM2)) >>> ruleStore._finalize() >>> fg = Graph().parse(data=PROGRAM2, format='n3') >>> from FuXi.Horn.HornRules import Ruleset >>> rs = Ruleset(n3Rules=ruleGraph.store.rules, nsMapping=ruleGraph.store.nsMgr) >>> for rule in rs: print(rule) Forall ?Y ?X ( ex:sg(?X ?Y) :- ex:flat(?X ?Y) ) Forall ?Y ?Z4 ?X ?Z1 ?Z2 ?Z3 ( ex:sg(?X ?Y) :- And( ex:up(?X ?Z1) ex:sg(?Z1 ?Z2) ex:flat(?Z2 ?Z3) ex:sg(?Z3 ?Z4) ex:down(?Z4 ?Y) ) ) >>> sip = BuildNaturalSIP(list(rs)[-1], [], None) #doctest: +SKIP >>> for N, x in IncomingSIPArcs(sip, MAGIC.sg): print(N.n3(), x.n3()) #doctest: +SKIP ( <http://doi.acm.org/10.1145/28659.28689#up> <http://doi.acm.org/10.1145/28659.28689#sg> <http://doi.acm.org/10.1145/28659.28689#flat> ) ( ?Z3 ) ( <http://doi.acm.org/10.1145/28659.28689#up> <http://doi.acm.org/10.1145/28659.28689#sg> ) ( ?Z1 ) >>> sip = BuildNaturalSIP(list(rs)[-1], [MAGIC.sg], None) #doctest: +SKIP >>> list(sip.query('SELECT ?q { ?prop a magic:SipArc . [] ?prop ?q . }', initNs={%(u)s'magic':MAGIC})) #doctest: +SKIP [rdflib.term.URIRef(%(u)s'http://doi.acm.org/10.1145/28659.28689#sg'), rdflib.term.URIRef(%(u)s'http://doi.acm.org/10.1145/28659.28689#sg')] """ from FuXi.Rete.Magic import AdornedUniTerm occurLookup = {} boundHead = isinstance(adornedHead, AdornedUniTerm) and 'b' in adornedHead.adornment phBoundVars = list(adornedHead.getDistinguishedVariables(varsOnly=True)) # assert isinstance(clause.head, Uniterm), "Only one literal in the head." def collectSip(left, right): if isinstance(left, list): vars = CollectSIPArcVars(left, right, phBoundVars) if not vars and ignoreUnboundDPreds: raise InvalidSIPException("No bound variables for %s" % right) leftList = Collection(sipGraph, None) left = list(set(left)) [leftList.append(i) for i in [GetOp(ii) for ii in left]] left.append(right) # arc = SIPGraphArc(leftList.uri, getOccurrenceId(right, occurLookup), vars, sipGraph) return left else: left.isHead = True vars = CollectSIPArcVars(left, right, phBoundVars) if not vars and ignoreUnboundDPreds: raise InvalidSIPException("No bound variables for %s" % right) ph = GetOp(left) # q = getOccurrenceId(right, occurLookup) if boundHead: # arc = SIPGraphArc(ph, q, vars, sipGraph, headPassing=boundHead) sipGraph.add((ph, RDF.type, MAGIC.BoundHeadPredicate)) rt = [left, right] else: rt = [right] return rt sipGraph = Graph() if isinstance(clause.body, And): if ignoreUnboundDPreds: foundSip = False sips = findFullSip(([clause.head], None), clause.body) while not foundSip: sip = next(sips) if py3compat.PY3 else sips.next() try: reduce(collectSip, iterCondition(And(sip))) foundSip = True bodyOrder = sip except InvalidSIPException: foundSip = False else: if first( filter(lambda i: isinstance(i, Uniterm) and i.naf or False, clause.body)): # There are negative literals in body, ensure # the given sip order puts negated literals at the end bodyOrder = first( filter(ProperSipOrderWithNegation, findFullSip(([clause.head], None), clause.body))) else: bodyOrder = first( findFullSip(([clause.head], None), clause.body)) assert bodyOrder, "Couldn't find a valid SIP for %s" % clause reduce(collectSip, iterCondition(And(bodyOrder))) sipGraph.sipOrder = And(bodyOrder[1:]) # assert validSip(sipGraph), sipGraph.serialize(format='n3') else: if boundHead: reduce( collectSip, itertools.chain(iterCondition(clause.head), iterCondition(clause.body))) sipGraph.sipOrder = clause.body if derivedPreds: # We therefore generalize our notation to allow # more succint representation of sips, in which only arcs entering # derived predicates are represented. arcsToRemove = [] collectionsToClear = [] for N, prop, q in sipGraph.query( 'SELECT ?N ?prop ?q { ?prop a magic:SipArc . ?N ?prop ?q . }', initNs={u'magic': MAGIC}): if occurLookup[q] not in derivedPreds and ( occurLookup[q] not in hybridPreds2Replace if hybridPreds2Replace else False): arcsToRemove.extend([(N, prop, q), (prop, None, None)]) collectionsToClear.append(Collection(sipGraph, N)) # clear bindings collection as well bindingsColBNode = first(sipGraph.objects( prop, MAGIC.bindings)) collectionsToClear.append( Collection(sipGraph, bindingsColBNode)) for removeSts in arcsToRemove: sipGraph.remove(removeSts) for col in collectionsToClear: col.clear() return sipGraph
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 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 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