def transform(self, graph): """ Transforms a 'pure' nominal range into a disjunction of value restrictions """ Individual.factoryGraph = graph for restriction, intermediateCl, nominal, prop in graph.query( self.NOMINAL_QUERY, initNs={'owl': OWL_NS}): nominalCollection = Collection(graph, nominal) # purge restriction restr = Class(restriction) parentSets = [i for i in restr.subClassOf] restr.clearOutDegree() newConjunct = BooleanClass(restriction, OWL_NS.unionOf, [Property(prop) | value | val for val in nominalCollection], graph) newConjunct.subClassOf = parentSets # purge nominalization placeholder iClass = BooleanClass(intermediateCl) iClass.clear() iClass.delete()
def test_ampersand_logical_operator(self): # The '&' operator can be used to construct class intersection: woman = Class(exNs.Female, graph=self.graph) & Class(exNs.Human, graph=self.graph) woman.identifier = exNs.Woman assert pformat(woman) == '( ex:Female AND ex:Human )', pformat(woman)
def test_owlpyclasses(self): # Now we have an empty Graph, we can construct OWL classes in it using the # Python classes defined in this module a = Class(exNs.Opera, graph=self.graph) # Now we can assert rdfs:subClassOf and owl:equivalentClass relationships # (in the underlying graph) with other classes using the subClassOf and # equivalentClass descriptors which can be set to a list of objects for # the corresponding predicates. a.subClassOf = [exNs.MusicalWork] # We can then access the rdfs:subClassOf relationships assert pformat(list( a.subClassOf)) == '[Class: ex:MusicalWork ]', pformat( list(a.subClassOf)) # This can also be used against already populated graphs: owlGraph = Graph().parse(str(OWL_NS)) nsm = self.graph.namespace_manager nsm.bind('owl', OWL_NS, override=False) owlGraph.namespace_manager = nsm assert pformat( list(Class(OWL_NS.Class, graph=owlGraph).subClassOf)) == \ '[Class: rdfs:Class ]'
def test_subclass(self): # Operators are also available. For instance we can add ex:Opera to the # extension of the ex:CreativeWork class via the '+=' operator a = Class(exNs.Opera, graph=self.graph) a.subClassOf = [exNs.MusicalWork] # assert str(a) == 'Class: ex:Opera SubClassOf: ex:MusicalWork' assert str(a) == "Class: ex:Opera SubClassOf: b'ex:MusicalWork'", str( a) b = Class(exNs.CreativeWork, graph=self.graph) b += a assert pformat(list(a.subClassOf)) == \ '[Class: ex:CreativeWork , Class: ex:MusicalWork ]' or \ '[Class: ex:MusicalWork , Class: ex:CreativeWork ]' # And we can then remove it from the extension as well b -= a a # assert pformat(a) == 'Class: ex:Opera SubClassOf: ex:MusicalWork' assert pformat( a) == "Class: ex:Opera SubClassOf: b'ex:MusicalWork'", pformat(a)
def test_owlpyclasses(self): # Now we have an empty Graph, we can construct OWL classes in it using the # Python classes defined in this module a = Class(exNs.Opera, graph=self.graph) # Now we can assert rdfs:subClassOf and owl:equivalentClass relationships # (in the underlying graph) with other classes using the subClassOf and # equivalentClass descriptors which can be set to a list of objects for # the corresponding predicates. a.subClassOf = [exNs.MusicalWork] # We can then access the rdfs:subClassOf relationships assert pformat(list(a.subClassOf)) == '[Class: ex:MusicalWork ]', pformat(list(a.subClassOf)) # This can also be used against already populated graphs: owlGraph = Graph().parse(str(OWL_NS)) nsm = self.graph.namespace_manager nsm.bind('owl', OWL_NS, override=False) owlGraph.namespace_manager = nsm assert pformat( list(Class(OWL_NS.Class, graph=owlGraph).subClassOf)) == \ '[Class: rdfs:Class ]'
def test_owl_enumerated_classes(self): # Enumerated classes can also be manipulated contList = [ Class(exNs.Africa, graph=self.graph), Class(exNs.NorthAmerica, graph=self.graph) ] # assert pformat(EnumeratedClass(members=contList, graph=self.graph)) == \ # '{ ex:Africa ex:NorthAmerica }' assert pformat(EnumeratedClass(members=contList, graph=self.graph)) == \ "{ b'ex:Africa' b'ex:NorthAmerica' }", pformat(EnumeratedClass(members=contList, graph=self.graph))
def test_manchester_owl_restrictions(self): # Restrictions can also be created using Manchester OWL syntax in # 'colloquial' Python. A Python infix operator recipe was used for # this purpose. See below assert pformat( exNs.hasParent | some | Class(exNs.Physician, graph=self.graph)) == \ '( ex:hasParent SOME ex:Physician )', pformat(exNs.hasParent | some | Class(exNs.Physician, graph=self.graph)) assert pformat( Property(exNs.hasParent, graph=self.graph) | max | Literal(1)) == \ '( ex:hasParent MAX 1 )', pformat(Property(exNs.hasParent, graph=self.graph) | max | Literal(1))
def transform(self, graph): """ Transforms a universal restriction to a negated existential restriction """ Individual.factoryGraph = graph for restr, p, o in graph.triples((None, OWL_NS.allValuesFrom, None)): graph.remove((restr, p, o)) innerCompl = Class(complementOf=o) graph.add((restr, OWL_NS.someValuesFrom, innerCompl.identifier)) outerCompl = Class() for _s, _p, _o in graph.triples((None, None, restr)): graph.add((_s, _p, outerCompl.identifier)) graph.remove((_s, _p, _o)) outerCompl.complementOf = restr
def setUp(self): self.tBoxGraph = Graph() self.tBoxGraph.namespace_manager.bind('ex', EX) self.tBoxGraph.namespace_manager.bind('owl', OWL_NS) Individual.factoryGraph = self.tBoxGraph self.classB = Class(EX.b) self.classE = Class(EX.e) self.classF = Class(EX.f) self.classA = BooleanClass(EX.a, operator=OWL_NS.unionOf, members=[self.classE, self.classF]) self.classC = BooleanClass(EX.c, operator=OWL_NS.unionOf, members=[self.classA, self.classB]) self.ruleStore, self.ruleGraph = SetupRuleStore()
def testGeneralConceptInclusion(self): # Some Class # ## Primitive Type ## # SubClassOf: Class: ex:NoExclusion . # DisjointWith ( ex:contains some ex:IsolatedCABGConcomitantExclusion ) contains = Property(EX_NS.contains) testClass = ~(contains | some | EX.Exclusion) testClass2 = EX.NoExclusion testClass2 += testClass NormalFormReduction(self.ontGraph) individual1 = BNode() individual2 = BNode() contains.extent = [(individual1, individual2)] ruleStore, ruleGraph, network = SetupRuleStore(makeNetwork=True) posRules, negRules = CalculateStratifiedModel(network, self.ontGraph, [EX_NS.NoExclusion]) self.failUnless(not posRules, "There should be no rules in the 0 strata.") self.assertEqual(len(negRules), 2, "There should be 2 'negative' rules") Individual.factoryGraph = network.inferredFacts targetClass = Class(EX_NS.NoExclusion, skipOWLClassMembership=False) self.failUnless( individual1 in targetClass.extent, "There is a BNode that bears the contains relation with another individual that is not a member of Exclusion." ) self.assertEquals(len(list(targetClass.extent)), 1, "There should only be one member in NoExclusion")
def testOtherForm2(self): hasCoronaryBypassConduit = Property(EX_NS.hasCoronaryBypassConduit) ITALeft = EX.ITALeft ITALeft += (hasCoronaryBypassConduit | some | EnumeratedClass(members=[ EX_NS.CoronaryBypassConduit_internal_thoracic_artery_left_insitu, EX_NS.CoronaryBypassConduit_internal_thoracic_artery_left_free ])) from FuXi.DLP.DLNormalization import NormalFormReduction self.assertEquals(repr(Class(first(ITALeft.subSumpteeIds()))), "Some Class SubClassOf: Class: ex:ITALeft ") NormalFormReduction(self.ontGraph) self.assertEquals( repr(Class(first(ITALeft.subSumpteeIds()))), 'Some Class SubClassOf: Class: ex:ITALeft . EquivalentTo: ( ( ex:hasCoronaryBypassConduit VALUE <http://example.com/CoronaryBypassConduit_internal_thoracic_artery_left_insitu> ) OR ( ex:hasCoronaryBypassConduit VALUE <http://example.com/CoronaryBypassConduit_internal_thoracic_artery_left_free> ) )' )
def transform(self, graph): """ Transforms a universal restriction on a 'pure' nominal range into a conjunction of value restriction (using set theory and demorgan's laws) """ Individual.factoryGraph = graph for restriction, intermediateCl, nominal, prop, partition in graph.query( self.NOMINAL_QUERY, initNs={ u'owl': OWL_NS, u'rdfs': str(RDFS) }): exceptions = EnumeratedClass() partition = Collection(graph, partition) nominalCollection = Collection(graph, nominal) for i in partition: if i not in nominalCollection: exceptions._rdfList.append(i) #exceptions+=i exists = Class(complementOf=(Property(prop) | some | exceptions)) for s, p, o in graph.triples((None, None, restriction)): graph.add((s, p, exists.identifier)) Individual(restriction).delete() #purge nominalization placeholder iClass = BooleanClass(intermediateCl) iClass.clear() iClass.delete()
def testExistentialInRightOfGCI(self): someProp = Property(EX_NS.someProp) existential = someProp | some | EX.Omega existential += EX.Foo self.assertEqual( repr(Class(EX_NS.Foo)), "Class: ex:Foo SubClassOf: ( ex:someProp SOME ex:Omega )") ruleStore, ruleGraph, network = SetupRuleStore(makeNetwork=True)
def test_booleans(self): # Boolean class constructions can also be created with Python operators # For example, The | operator can be used to construct a class consisting # of a owl:unionOf the operands: a = Class(exNs.Opera, graph=self.graph) a.subClassOf = [exNs.MusicalWork] b = Class(exNs.CreativeWork, graph=self.graph) c = a | b | Class(exNs.Work, graph=self.graph) # assert(pformat(c)) == '( ex:Opera OR ex:CreativeWork OR ex:Work )' assert(pformat(c)) == "( b'ex:Opera' OR b'ex:CreativeWork' OR b'ex:Work' )", pformat(c) # Boolean class expressions can also be operated as lists (natively in python) del c[c.index(Class(exNs.Work, graph=self.graph))] # assert pformat(c) == '( ex:Opera OR ex:CreativeWork )' assert pformat(c) == "( b'ex:Opera' OR b'ex:CreativeWork' )", pformat(c)
def SubSumptionExpansion(owlClass): owlClass = CastClass(owlClass) if isinstance(owlClass, BooleanClass) and owlClass._operator == OWL_NS.unionOf: for member in owlClass: expanded = False for innerMember in SubSumptionExpansion(Class(member)): expanded = True yield innerMember if not expanded: yield member else: for member in owlClass.subSumpteeIds(): expanded = False for innerMember in SubSumptionExpansion(Class(member)): expanded = True yield innerMember if not expanded: yield member
def testBasePredicateEquivalence(self): (EX.Foo).equivalentClass = [EX.Bar] self.assertEqual(repr(Class(EX_NS.Foo)), "Class: ex:Foo EquivalentTo: ex:Bar") ruleStore, ruleGraph, network = SetupRuleStore(makeNetwork=True) rules = network.setupDescriptionLogicProgramming( self.ontGraph, addPDSemantics=False, constructNetwork=False) self.assertEqual( repr(rules), 'set([Forall ?X ( ex:Bar(?X) :- ex:Foo(?X) ), Forall ?X ( ex:Foo(?X) :- ex:Bar(?X) )])' ) self.assertEqual(len(rules), 2, "There should be 2 rules")
def transform(self, graph): """ Transforms a 'pure' nominal range into a disjunction of value restrictions """ Individual.factoryGraph = graph for restriction, intermediateCl, nominal, prop in graph.query( self.NOMINAL_QUERY, initNs={u'owl': OWL_NS}): nominalCollection = Collection(graph, nominal) #purge restriction restr = Class(restriction) parentSets = [i for i in restr.subClassOf] restr.clearOutDegree() newConjunct = BooleanClass( restriction, OWL_NS.unionOf, [Property(prop) | value | val for val in nominalCollection], graph) newConjunct.subClassOf = parentSets #purge nominalization placeholder iClass = BooleanClass(intermediateCl) iClass.clear() iClass.delete()
def test_subclass(self): # Operators are also available. For instance we can add ex:Opera to the # extension of the ex:CreativeWork class via the '+=' operator a = Class(exNs.Opera, graph=self.graph) a.subClassOf = [exNs.MusicalWork] # assert str(a) == 'Class: ex:Opera SubClassOf: ex:MusicalWork' assert str(a) == "Class: ex:Opera SubClassOf: b'ex:MusicalWork'", str(a) b = Class(exNs.CreativeWork, graph=self.graph) b += a assert pformat(list(a.subClassOf)) == \ '[Class: ex:CreativeWork , Class: ex:MusicalWork ]' or \ '[Class: ex:MusicalWork , Class: ex:CreativeWork ]' # And we can then remove it from the extension as well b -= a a # assert pformat(a) == 'Class: ex:Opera SubClassOf: ex:MusicalWork' assert pformat(a) == "Class: ex:Opera SubClassOf: b'ex:MusicalWork'", pformat(a)
def transform(self, graph): """ Uses demorgan's laws to reduce negated disjunctions to a conjunction of negated formulae """ Individual.factoryGraph = graph for disjunctId in graph.subjects(predicate=OWL_NS.unionOf): if (None, OWL_NS.complementOf, disjunctId) in graph and \ isinstance(disjunctId, BNode): #not ( A1 or A2 or .. or An ) # = # ( not A1 and not A2 and .. and not An ) disjunct = BooleanClass(disjunctId, operator=OWL_NS.unionOf) items = list(disjunct) newConjunct = BooleanClass( members=[~Class(item) for item in items]) for negation in graph.subjects(predicate=OWL_NS.complementOf, object=disjunctId): Class(negation).replace(newConjunct) if not isinstance(negation, BNode): newConjunct.identifier = negation disjunct.clear() disjunct.delete() elif ((disjunctId, OWL_NS.unionOf, None) in graph) and not \ [item for item in BooleanClass(disjunctId, operator=OWL_NS.unionOf) if not Class(item).complementOf]: #( not A1 or not A2 or .. or not An ) # = #not ( A1 and A2 and .. and An ) disjunct = BooleanClass(disjunctId, operator=OWL_NS.unionOf) items = [Class(item).complementOf for item in disjunct] for negation in disjunct: Class(negation).delete() negatedConjunct = ~BooleanClass(members=items) disjunct.clear() disjunct.replace(negatedConjunct)
def ComplementExpand(tBoxGraph, complementAnnotation): complementExpanded = [] for negativeClass in tBoxGraph.subjects(predicate=OWL_NS.complementOf): containingList = first(tBoxGraph.subjects(RDF.first, negativeClass)) prevLink = None while containingList: prevLink = containingList containingList = first(tBoxGraph.subjects(RDF.rest, containingList)) if prevLink: for s, p, o in tBoxGraph.triples_choices( (None, [OWL_NS.intersectionOf, OWL_NS.unionOf], prevLink)): if (s, complementAnnotation, None) in tBoxGraph: continue _class = Class(s) complementExpanded.append(s) print("Added %s to complement expansion" % _class) ComplementExpansion(_class)
def ComplementExpansion(owlClass, debug=False): """ For binary conjunctions of a positive conjunction concept and a negative atomic concept """ owlClass = CastClass(owlClass.identifier, owlClass.graph) if isinstance(owlClass, BooleanClass) and \ len(owlClass) == 2 and owlClass._operator == OWL_NS.intersectionOf: oldRepr = owlClass.__repr__() # A boolean-constructed class negativeClasses = set() otherClasses = set() for member in owlClass: member = Class(member) if member.complementOf: # A negative class, expand it and add to bucket of classes to # 'remove' for expandedClass in SubSumptionExpansion(member.complementOf): negativeClasses.add(expandedClass) else: # A positive class, expand it and add to bucket of base classes expanded = False for expandedClass in SubSumptionExpansion(member): expanded = True otherClasses.add(expandedClass) if not expanded: otherClasses.add(member.identifier) if negativeClasses: # Delete the old list of operands for the boolean class oldList = owlClass._rdfList oldList.clear() # Recreate the list of operands, exluding the expanded negative # classes for allowedClasses in otherClasses.difference(negativeClasses): oldList.append(classOrIdentifier(allowedClasses)) owlClass.changeOperator(OWL_NS.unionOf) if debug: print("Incoming boolean class: ", oldRepr) print("Expanded boolean class: ", owlClass.__repr__()) return owlClass else: if debug: print("There were no negative classes.")
def test_booleans(self): # Boolean class constructions can also be created with Python operators # For example, The | operator can be used to construct a class consisting # of a owl:unionOf the operands: a = Class(exNs.Opera, graph=self.graph) a.subClassOf = [exNs.MusicalWork] b = Class(exNs.CreativeWork, graph=self.graph) c = a | b | Class(exNs.Work, graph=self.graph) # assert(pformat(c)) == '( ex:Opera OR ex:CreativeWork OR ex:Work )' assert ( pformat(c) ) == "( b'ex:Opera' OR b'ex:CreativeWork' OR b'ex:Work' )", pformat(c) # Boolean class expressions can also be operated as lists (natively in python) del c[c.index(Class(exNs.Work, graph=self.graph))] # assert pformat(c) == '( ex:Opera OR ex:CreativeWork )' assert pformat(c) == "( b'ex:Opera' OR b'ex:CreativeWork' )", pformat( c)
def ProcessConcept(klass, owlGraph, FreshConcept, newOwlGraph): """ This method implements the pre-processing portion of the completion-based procedure and recursively transforms the input ontology one concept at a time """ iD = klass.identifier # maps the identifier to skolem:bnodeLabel if # the identifier is a BNode or to skolem:newBNodeLabel # if its a URI FreshConcept[iD] = SkolemizeExistentialClasses( BNode() if isinstance(iD, URIRef) else iD ) # A fresh atomic concept (A_c) newCls = Class(FreshConcept[iD], graph=newOwlGraph) cls = CastClass(klass, owlGraph) # determine if the concept is the left, right (or both) # operand of a subsumption axiom in the ontology location = WhichSubsumptionOperand(iD, owlGraph) # log.debug(repr(cls)) if isinstance(iD, URIRef): # An atomic concept? if location in [LEFT_SUBSUMPTION_OPERAND, BOTH_SUBSUMPTION_OPERAND]: log.debug( "Original (atomic) concept appears in the left HS of a subsumption axiom") # If class is left operand of subsumption operator, # assert (in new OWL graph) that A_c subsumes the concept _cls = Class(cls.identifier, graph=newOwlGraph) newCls += _cls log.debug("%s subsumes %s" % (newCls, _cls)) if location in [RIGHT_SUBSUMPTION_OPERAND, BOTH_SUBSUMPTION_OPERAND]: log.debug( "Original (atomic) concept appears in the right HS of a subsumption axiom") # If class is right operand of subsumption operator, # assert that it subsumes A_c _cls = Class(cls.identifier, graph=newOwlGraph) _cls += newCls log.debug("%s subsumes %s" % (_cls, newCls)) elif isinstance(cls, Restriction): if location != NEITHER_SUBSUMPTION_OPERAND: # appears in at least one subsumption operator # An existential role restriction log.debug( "Original (role restriction) appears in a subsumption axiom") role = Property(cls.onProperty, graph=newOwlGraph) fillerCls = ProcessConcept( Class(cls.restrictionRange), owlGraph, FreshConcept, newOwlGraph) # leftCls is (role SOME fillerCls) leftCls = role | some | fillerCls log.debug("let leftCls be %s" % leftCls) if location in [LEFT_SUBSUMPTION_OPERAND, BOTH_SUBSUMPTION_OPERAND]: # if appears as the left operand, we say A_c subsumes # leftCls newCls += leftCls log.debug("%s subsumes leftCls" % newCls) if location in [RIGHT_SUBSUMPTION_OPERAND, BOTH_SUBSUMPTION_OPERAND]: # if appears as right operand, we say left Cls subsumes A_c leftCls += newCls log.debug("leftCls subsumes %s" % newCls) else: assert isinstance(cls, BooleanClass), "Not ELH ontology: %r" % cls assert cls._operator == OWL_NS.intersectionOf, "Not ELH ontology" log.debug( "Original conjunction (or boolean operator wlog ) appears in a subsumption axiom") # A boolean conjunction if location != NEITHER_SUBSUMPTION_OPERAND: members = [ProcessConcept(Class(c), owlGraph, FreshConcept, newOwlGraph) for c in cls] newBoolean = BooleanClass( BNode(), members=members, graph=newOwlGraph) # create a boolean conjunction of the fresh concepts corresponding # to processing each member of the existing conjunction if location in [LEFT_SUBSUMPTION_OPERAND, BOTH_SUBSUMPTION_OPERAND]: # if appears as the left operand, we say the new conjunction # is subsumed by A_c newCls += newBoolean log.debug("%s subsumes %s" % (newCls, newBoolean)) if location in [RIGHT_SUBSUMPTION_OPERAND, BOTH_SUBSUMPTION_OPERAND]: # if appears as the right operand, we say A_c is subsumed by # the new conjunction newBoolean += newCls log.debug("%s subsumes %s" % (newBoolean, newCls)) return newCls
(options, args) = parser.parse_args() owlGraph = Graph() for input in args[0:]: if options.verbose: print("Parsing ", input, " as ", options.format) owlGraph.parse(input, format=options.format) Individual.factoryGraph = owlGraph def topList(node, g): for s in g.subjects(RDF.rest, node): yield s for negativeClass in owlGraph.subjects(predicate=OWL_NS.complementOf): containingList = first(owlGraph.subjects(RDF.first, negativeClass)) prevLink = None while containingList: prevLink = containingList containingList = first(owlGraph.subjects(RDF.rest, containingList)) for s, p, o in owlGraph.triples_choices( (None, [OWL_NS.intersectionOf, OWL_NS.unionOf], prevLink)): _class = Class(s) # print(_class.__repr__(True,True)) ComplementExpansion(_class, debug=options.verbose) # from FuXi.Horn import ComplementExpansion # from FuXi.Horn import SubSumptionExpansion # from FuXi.Horn import ComplementExpansionTestSuite
from rdflib import Graph, RDF, URIRef from FuXi.Syntax.InfixOWL import OWL_NS, Class # local source: # galenGraph = Graph().parse( # os.path.join(os.path.dirname(__file__), 'GALEN-CABG-Segment.owl')) # remote source: galenGraph = Graph().parse(location="./GALEN-CABG-Segment.owl", format="xml") graph = galenGraph with open('GALEN-CABG-Segment.asc', 'w') as fp: for c in graph.subjects(predicate=RDF.type, object=OWL_NS.Class): if isinstance(c, URIRef): fp.write(Class(c, graph=graph).__repr__(True) + "\n\n") print("Done")
def testExpand(self): EX = Namespace("http://example.com/") namespace_manager = NamespaceManager(Graph()) namespace_manager.bind('ex', EX, override=False) self.testGraph.namespace_manager = namespace_manager man = Class(EX.Man) boy = Class(EX.Boy) woman = Class(EX.Woman) girl = Class(EX.Girl) male = Class(EX.Male) female = Class(EX.Female) human = Class(EX.Human) animal = Class(EX.Animal) cat = Class(EX.Cat) dog = Class(EX.Dog) animal = Class(EX.Animal) animal = cat | dog | human human += man human += boy human += woman human += girl male += man male += boy female += woman female += girl testClass = human & ~female self.assertEquals(repr(testClass), 'ex:Human THAT ( NOT ex:Female )') newtestClass = ComplementExpansion(testClass, debug=True) self.assertTrue( repr(newtestClass) in ['( ex:Boy or ex:Man )', '( ex:Man or ex:Boy )'], repr(newtestClass)) testClass2 = animal & ~(male | female) self.assertEquals( repr(testClass2), '( ( ex:Cat or ex:Dog or ex:Human ) and ( not ( ex:Male or ex:Female ) ) )' ) newtestClass2 = ComplementExpansion(testClass2, debug=True) testClass2Repr = repr(newtestClass2) self.assertTrue( testClass2Repr in ['( ex:Cat or ex:Dog )', '( ex:Dog or ex:Cat )'], testClass2Repr)
Namespace, NamespaceManager, ) from pprint import pformat exNs = Namespace('http://example.com/') namespace_manager = NamespaceManager(Graph()) namespace_manager.bind('ex', exNs, override=False) namespace_manager.bind('owl', OWL_NS, override=False) g = Graph() g.namespace_manager = namespace_manager # Now we have an empty Graph, we can construct OWL classes in it using the # Python classes defined in this module a = Class(exNs.Opera, graph=g) # Now we can assert rdfs:subClassOf and owl:equivalentClass relationships # (in the underlying graph) with other classes using the subClassOf and # equivalentClass descriptors which can be set to a list of objects for # the corresponding predicates. a.subClassOf = [exNs.MusicalWork] # We can then access the rdfs:subClassOf relationships assert pformat(list(a.subClassOf)) == '[Class: ex:MusicalWork ]' # This can also be used against already populated graphs: owlGraph = Graph().parse(OWL_NS)
def main(): from optparse import OptionParser op = OptionParser( 'usage: %prog [options] factFile1 factFile2 ... factFileN') op.add_option( '--why', default=None, help='Specifies the goals to solve for using the non-naive methods' + 'see --method') op.add_option( '--closure', action='store_true', default=False, help='Whether or not to serialize the inferred triples' + ' along with the original triples. Otherwise ' + '(the default behavior), serialize only the inferred triples') op.add_option( '--imports', action='store_true', default=False, help='Whether or not to follow owl:imports in the fact graph') op.add_option( '--output', default='n3', metavar='RDF_FORMAT', choices=[ 'xml', 'TriX', 'n3', 'pml', 'proof-graph', 'nt', 'rif', 'rif-xml', 'conflict', 'man-owl' ], help= "Serialize the inferred triples and/or original RDF triples to STDOUT " + "using the specified RDF syntax ('xml', 'pretty-xml', 'nt', 'turtle', " + "or 'n3') or to print a summary of the conflict set (from the RETE " + "network) if the value of this option is 'conflict'. If the the " + " value is 'rif' or 'rif-xml', Then the rules used for inference " + "will be serialized as RIF. If the value is 'pml' and --why is used, " + " then the PML RDF statements are serialized. If output is " + "'proof-graph then a graphviz .dot file of the proof graph is printed. " + "Finally if the value is 'man-owl', then the RDF facts are assumed " + "to be OWL/RDF and serialized via Manchester OWL syntax. The default is %default" ) op.add_option( '--class', dest='classes', action='append', default=[], metavar='QNAME', help='Used with --output=man-owl to determine which ' + 'classes within the entire OWL/RDF are targetted for serialization' + '. Can be used more than once') op.add_option( '--hybrid', action='store_true', default=False, help='Used with with --method=bfp to determine whether or not to ' + 'peek into the fact graph to identify predicates that are both ' + 'derived and base. This is expensive for large fact graphs' + 'and is explicitely not used against SPARQL endpoints') op.add_option( '--property', action='append', dest='properties', default=[], metavar='QNAME', help='Used with --output=man-owl or --extract to determine which ' + 'properties are serialized / extracted. Can be used more than once') op.add_option( '--normalize', action='store_true', default=False, help= "Used with --output=man-owl to attempt to determine if the ontology is 'normalized' [Rector, A. 2003]" + "The default is %default") op.add_option( '--ddlGraph', default=False, help= "The location of a N3 Data Description document describing the IDB predicates" ) op.add_option( '--input-format', default='xml', dest='inputFormat', metavar='RDF_FORMAT', choices=['xml', 'trix', 'n3', 'nt', 'rdfa'], help= "The format of the RDF document(s) which serve as the initial facts " + " for the RETE network. One of 'xml', 'n3', 'trix', 'nt', " + "or 'rdfa'. The default is %default") op.add_option( '--safety', default='none', metavar='RULE_SAFETY', choices=['loose', 'strict', 'none'], help="Determines how to handle RIF Core safety. A value of 'loose' " + " means that unsafe rules will be ignored. A value of 'strict' " + " will cause a syntax exception upon any unsafe rule. A value of " + "'none' (the default) does nothing") op.add_option( '--pDSemantics', action='store_true', default=False, help= 'Used with --dlp to add pD semantics ruleset for semantics not covered ' + 'by DLP but can be expressed in definite Datalog Logic Programming' + ' The default is %default') op.add_option( '--stdin', action='store_true', default=False, help= 'Parse STDIN as an RDF graph to contribute to the initial facts. The default is %default ' ) op.add_option( '--ns', action='append', default=[], metavar="PREFIX=URI", help='Register a namespace binding (QName prefix to a base URI). This ' + 'can be used more than once') op.add_option( '--rules', default=[], action='append', metavar='PATH_OR_URI', help='The Notation 3 documents to use as rulesets for the RETE network' + '. Can be specified more than once') op.add_option('-d', '--debug', action='store_true', default=True, help='Include debugging output') op.add_option( '--strictness', default='defaultBase', metavar='DDL_STRICTNESS', choices=['loose', 'defaultBase', 'defaultDerived', 'harsh'], help= 'Used with --why to specify whether to: *not* check if predicates are ' + ' both derived and base (loose), if they are, mark as derived (defaultDerived) ' + 'or as base (defaultBase) predicates, else raise an exception (harsh)') op.add_option( '--method', default='naive', metavar='reasoning algorithm', choices=['gms', 'bfp', 'naive'], help='Used with --why to specify how to evaluate answers for query. ' + 'One of: gms, sld, bfp, naive') op.add_option( '--firstAnswer', default=False, action='store_true', help= 'Used with --why to determine whether to fetch all answers or just ' + 'the first') op.add_option( '--edb', default=[], action='append', metavar='EXTENSIONAL_DB_PREDICATE_QNAME', help= 'Used with --why/--strictness=defaultDerived to specify which clashing ' + 'predicate will be designated as a base predicate') op.add_option( '--idb', default=[], action='append', metavar='INTENSIONAL_DB_PREDICATE_QNAME', help= 'Used with --why/--strictness=defaultBase to specify which clashing ' + 'predicate will be designated as a derived predicate') op.add_option( '--hybridPredicate', default=[], action='append', metavar='PREDICATE_QNAME', help= 'Used with --why to explicitely specify a hybrid predicate (in both ' + ' IDB and EDB) ') op.add_option( '--noMagic', default=[], action='append', metavar='DB_PREDICATE_QNAME', help='Used with --why to specify that the predicate shouldnt have its ' + 'magic sets calculated') op.add_option( '--filter', action='append', default=[], metavar='PATH_OR_URI', help= 'The Notation 3 documents to use as a filter (entailments do not particpate in network)' ) op.add_option( '--ruleFacts', action='store_true', default=False, help="Determines whether or not to attempt to parse initial facts from " + "the rule graph. The default is %default") op.add_option( '--builtins', default=False, metavar='PATH_TO_PYTHON_MODULE', help="The path to a python module with function definitions (and a " + "dicitonary called ADDITIONAL_FILTERS) to use for builtins implementations" ) op.add_option( '--dlp', action='store_true', default=False, help= 'Use Description Logic Programming (DLP) to extract rules from OWL/RDF. The default is %default' ) op.add_option( '--sparqlEndpoint', action='store_true', default=False, help= 'Indicates that the sole argument is the URI of a SPARQL endpoint to query' ) op.add_option( '--ontology', action='append', default=[], metavar='PATH_OR_URI', help= 'The path to an OWL RDF/XML graph to use DLP to extract rules from ' + '(other wise, fact graph(s) are used) ') op.add_option( '--ontologyFormat', default='xml', dest='ontologyFormat', metavar='RDF_FORMAT', choices=['xml', 'trix', 'n3', 'nt', 'rdfa'], help= "The format of the OWL RDF/XML graph specified via --ontology. The default is %default" ) op.add_option( '--builtinTemplates', default=None, metavar='N3_DOC_PATH_OR_URI', help= 'The path to an N3 document associating SPARQL FILTER templates to ' + 'rule builtins') op.add_option('--negation', action='store_true', default=False, help='Extract negative rules?') op.add_option( '--normalForm', action='store_true', default=False, help='Whether or not to reduce DL axioms & LP rules to a normal form') (options, facts) = op.parse_args() nsBinds = {'iw': 'http://inferenceweb.stanford.edu/2004/07/iw.owl#'} for nsBind in options.ns: pref, nsUri = nsBind.split('=') nsBinds[pref] = nsUri namespace_manager = NamespaceManager(Graph()) if options.sparqlEndpoint: factGraph = Graph(plugin.get('SPARQLStore', Store)(facts[0])) options.hybrid = False else: factGraph = Graph() ruleSet = Ruleset() for fileN in options.rules: if options.ruleFacts and not options.sparqlEndpoint: factGraph.parse(fileN, format='n3') print("Parsing RDF facts from ", fileN) if options.builtins: import imp userFuncs = imp.load_source('builtins', options.builtins) rs = HornFromN3(fileN, additionalBuiltins=userFuncs.ADDITIONAL_FILTERS) else: rs = HornFromN3(fileN) nsBinds.update(rs.nsMapping) ruleSet.formulae.extend(rs) #ruleGraph.parse(fileN, format='n3') ruleSet.nsMapping = nsBinds for prefix, uri in list(nsBinds.items()): namespace_manager.bind(prefix, uri, override=False) closureDeltaGraph = Graph() closureDeltaGraph.namespace_manager = namespace_manager factGraph.namespace_manager = namespace_manager if not options.sparqlEndpoint: for fileN in facts: factGraph.parse(fileN, format=options.inputFormat) if options.imports: for owlImport in factGraph.objects(predicate=OWL_NS.imports): factGraph.parse(owlImport) print("Parsed Semantic Web Graph.. ", owlImport) if not options.sparqlEndpoint and facts: for pref, uri in factGraph.namespaces(): nsBinds[pref] = uri if options.stdin: assert not options.sparqlEndpoint, "Cannot use --stdin with --sparqlEndpoint" factGraph.parse(sys.stdin, format=options.inputFormat) #Normalize namespace mappings #prune redundant, rdflib-allocated namespace prefix mappings newNsMgr = NamespaceManager(factGraph) from FuXi.Rete.Util import CollapseDictionary for k, v in list( CollapseDictionary( dict([(k, v) for k, v in factGraph.namespaces()])).items()): newNsMgr.bind(k, v) factGraph.namespace_manager = newNsMgr if options.normalForm: NormalFormReduction(factGraph) if not options.sparqlEndpoint: workingMemory = generateTokenSet(factGraph) if options.builtins: import imp userFuncs = imp.load_source('builtins', options.builtins) rule_store, rule_graph, network = SetupRuleStore( makeNetwork=True, additionalBuiltins=userFuncs.ADDITIONAL_FILTERS) else: rule_store, rule_graph, network = SetupRuleStore(makeNetwork=True) network.inferredFacts = closureDeltaGraph network.nsMap = nsBinds if options.dlp: from FuXi.DLP.DLNormalization import NormalFormReduction if options.ontology: ontGraph = Graph() for fileN in options.ontology: ontGraph.parse(fileN, format=options.ontologyFormat) for prefix, uri in ontGraph.namespaces(): nsBinds[prefix] = uri namespace_manager.bind(prefix, uri, override=False) if options.sparqlEndpoint: factGraph.store.bind(prefix, uri) else: ontGraph = factGraph NormalFormReduction(ontGraph) dlp = network.setupDescriptionLogicProgramming( ontGraph, addPDSemantics=options.pDSemantics, constructNetwork=False, ignoreNegativeStratus=options.negation, safety=safetyNameMap[options.safety]) ruleSet.formulae.extend(dlp) if options.output == 'rif' and not options.why: for rule in ruleSet: print(rule) if options.negation: for nRule in network.negRules: print(nRule) elif options.output == 'man-owl': cGraph = network.closureGraph(factGraph, readOnly=False) cGraph.namespace_manager = namespace_manager Individual.factoryGraph = cGraph if options.classes: mapping = dict(namespace_manager.namespaces()) for c in options.classes: pref, uri = c.split(':') print(Class(URIRef(mapping[pref] + uri)).__repr__(True)) elif options.properties: mapping = dict(namespace_manager.namespaces()) for p in options.properties: pref, uri = p.split(':') print(Property(URIRef(mapping[pref] + uri))) else: for p in AllProperties(cGraph): print(p.identifier, first(p.label)) print(repr(p)) for c in AllClasses(cGraph): if options.normalize: if c.isPrimitive(): primAnc = [ sc for sc in c.subClassOf if sc.isPrimitive() ] if len(primAnc) > 1: warnings.warn( "Branches of primitive skeleton taxonomy" + " should form trees: %s has %s primitive parents: %s" % (c.qname, len(primAnc), primAnc), UserWarning, 1) children = [desc for desc in c.subSumpteeIds()] for child in children: for otherChild in [ o for o in children if o is not child ]: if not otherChild in [ c.identifier for c in Class(child).disjointWith ]: # and \ warnings.warn( "Primitive children (of %s) " % (c.qname) + \ "must be mutually disjoint: %s and %s" % ( Class(child).qname, Class(otherChild).qname), UserWarning, 1) # if not isinstance(c.identifier, BNode): print(c.__repr__(True)) if not options.why: # Naive construction of graph for rule in ruleSet: network.buildNetworkFromClause(rule) magicSeeds = [] if options.why: builtinTemplateGraph = Graph() if options.builtinTemplates: builtinTemplateGraph = Graph().parse(options.builtinTemplates, format='n3') factGraph.templateMap = \ dict([(pred, template) for pred, _ignore, template in builtinTemplateGraph.triples( (None, TEMPLATES.filterTemplate, None))]) goals = [] query = ParseSPARQL(options.why) network.nsMap['pml'] = PML network.nsMap['gmp'] = GMP_NS network.nsMap['owl'] = OWL_NS nsBinds.update(network.nsMap) network.nsMap = nsBinds if not query.prologue: query.prologue = Prologue(None, []) query.prologue.prefixBindings.update(nsBinds) else: for prefix, nsInst in list(nsBinds.items()): if prefix not in query.prologue.prefixBindings: query.prologue.prefixBindings[prefix] = nsInst print("query.prologue", query.prologue) print("query.query", query.query) print("query.query.whereClause", query.query.whereClause) print("query.query.whereClause.parsedGraphPattern", query.query.whereClause.parsedGraphPattern) goals.extend([(s, p, o) for s, p, o, c in ReduceGraphPattern( query.query.whereClause.parsedGraphPattern, query.prologue).patterns]) # dPreds=[]# p for s, p, o in goals ] # print("goals", goals) magicRuleNo = 0 bottomUpDerivedPreds = [] # topDownDerivedPreds = [] defaultBasePreds = [] defaultDerivedPreds = set() hybridPredicates = [] mapping = dict(newNsMgr.namespaces()) for edb in options.edb: pref, uri = edb.split(':') defaultBasePreds.append(URIRef(mapping[pref] + uri)) noMagic = [] for pred in options.noMagic: pref, uri = pred.split(':') noMagic.append(URIRef(mapping[pref] + uri)) if options.ddlGraph: ddlGraph = Graph().parse(options.ddlGraph, format='n3') # @TODO: should also get hybrid predicates from DDL graph defaultDerivedPreds = IdentifyDerivedPredicates( ddlGraph, Graph(), ruleSet) else: for idb in options.idb: pref, uri = idb.split(':') defaultDerivedPreds.add(URIRef(mapping[pref] + uri)) defaultDerivedPreds.update( set([p == RDF.type and o or p for s, p, o in goals])) for hybrid in options.hybridPredicate: pref, uri = hybrid.split(':') hybridPredicates.append(URIRef(mapping[pref] + uri)) if options.method == 'gms': for goal in goals: goalSeed = AdornLiteral(goal).makeMagicPred() print("Magic seed fact (used in bottom-up evaluation)", goalSeed) magicSeeds.append(goalSeed.toRDFTuple()) if noMagic: print("Predicates whose magic sets will not be calculated") for p in noMagic: print("\t", factGraph.qname(p)) for rule in MagicSetTransformation( factGraph, ruleSet, goals, derivedPreds=bottomUpDerivedPreds, strictCheck=nameMap[options.strictness], defaultPredicates=(defaultBasePreds, defaultDerivedPreds), noMagic=noMagic): magicRuleNo += 1 network.buildNetworkFromClause(rule) if len(list(ruleSet)): print("reduction in size of program: %s (%s -> %s clauses)" % (100 - (float(magicRuleNo) / float(len(list(ruleSet)))) * 100, len(list(ruleSet)), magicRuleNo)) start = time.time() network.feedFactsToAdd(generateTokenSet(magicSeeds)) if not [ rule for rule in factGraph.adornedProgram if len(rule.sip) ]: warnings.warn( "Using GMS sideways information strategy with no " + "information to pass from query. Falling back to " + "naive method over given facts and rules") network.feedFactsToAdd(workingMemory) sTime = time.time() - start if sTime > 1: sTimeStr = "%s seconds" % sTime else: sTime = sTime * 1000 sTimeStr = "%s milli seconds" % sTime print("Time to calculate closure on working memory: ", sTimeStr) if options.output == 'rif': print("Rules used for bottom-up evaluation") if network.rules: for clause in network.rules: print(clause) else: for clause in factGraph.adornedProgram: print(clause) if options.output == 'conflict': network.reportConflictSet() elif options.method == 'bfp': topDownDPreds = defaultDerivedPreds if options.builtinTemplates: builtinTemplateGraph = Graph().parse(options.builtinTemplates, format='n3') builtinDict = dict([ (pred, template) for pred, _ignore, template in builtinTemplateGraph.triples((None, TEMPLATES.filterTemplate, None)) ]) else: builtinDict = None topDownStore = TopDownSPARQLEntailingStore( factGraph.store, factGraph, idb=ruleSet, DEBUG=options.debug, derivedPredicates=topDownDPreds, templateMap=builtinDict, nsBindings=network.nsMap, identifyHybridPredicates=options.hybrid if options.method == 'bfp' else False, hybridPredicates=hybridPredicates) targetGraph = Graph(topDownStore) for pref, nsUri in list(network.nsMap.items()): targetGraph.bind(pref, nsUri) start = time.time() # queryLiteral = EDBQuery([BuildUnitermFromTuple(goal) for goal in goals], # targetGraph) # query = queryLiteral.asSPARQL() # print("Goal to solve ", query) sTime = time.time() - start result = targetGraph.query(options.why, initNs=network.nsMap) if result.askAnswer: sTime = time.time() - start if sTime > 1: sTimeStr = "%s seconds" % sTime else: sTime = sTime * 1000 sTimeStr = "%s milli seconds" % sTime print("Time to reach answer ground goal answer of %s: %s" % (result.askAnswer[0], sTimeStr)) else: for rt in result: sTime = time.time() - start if sTime > 1: sTimeStr = "%s seconds" % sTime else: sTime = sTime * 1000 sTimeStr = "%s milli seconds" % sTime if options.firstAnswer: break print( "Time to reach answer %s via top-down SPARQL sip strategy: %s" % (rt, sTimeStr)) if options.output == 'conflict' and options.method == 'bfp': for _network, _goal in topDownStore.queryNetworks: print(network, _goal) _network.reportConflictSet(options.debug) for query in topDownStore.edbQueries: print(query.asSPARQL()) elif options.method == 'naive': start = time.time() network.feedFactsToAdd(workingMemory) sTime = time.time() - start if sTime > 1: sTimeStr = "%s seconds" % sTime else: sTime = sTime * 1000 sTimeStr = "%s milli seconds" % sTime print("Time to calculate closure on working memory: ", sTimeStr) print(network) if options.output == 'conflict': network.reportConflictSet() for fileN in options.filter: for rule in HornFromN3(fileN): network.buildFilterNetworkFromClause(rule) if options.negation and network.negRules and options.method in [ 'both', 'bottomUp' ]: now = time.time() rt = network.calculateStratifiedModel(factGraph) print( "Time to calculate stratified, stable model (inferred %s facts): %s" % (rt, time.time() - now)) if options.filter: print("Applying filter to entailed facts") network.inferredFacts = network.filteredFacts if options.closure and options.output in RDF_SERIALIZATION_FORMATS: cGraph = network.closureGraph(factGraph) cGraph.namespace_manager = namespace_manager print( cGraph.serialize(destination=None, format=options.output, base=None)) elif options.output and options.output in RDF_SERIALIZATION_FORMATS: print( network.inferredFacts.serialize(destination=None, format=options.output, base=None))