A la fin de ce notebook, vous saurez : 1. Comprendre OWL 2 et ce qu’il apporte au-dela de RDFS 2. Utiliser OntologyGraph de dotNetRDF pour manipuler des ontologies 3. Définir des classes OWL avec equivalence, disjonction et restrictions 4. Définir des proprietes OWL : ObjectProperty, DatatypeProperty, FunctionalProperty, inverseOf 5. Distinguer les profils OWL 2 (EL, QL, RL) et leurs cas d’usage 6. Charger et interroger une ontologie existante (university.owl)
Concepts cles
Concept
Description
OWL 2
Langage d’ontologie du W3C, extension de RDFS avec logique descriptive
Ontologie
Modèle formel d’un domaine : classes, proprietes, contraintes, individus
OntologyGraph
Classe dotNetRDF specialisee pour manipuler les ontologies
Logique descriptive
Formalisme logique sous-jacent a OWL (decidable)
Prerequis
.NET SDK 9.0+ et .NET Interactive
Notebooks SW-1 a SW-6 (bases RDF, SPARQL, RDFS)
Duree estimee : 50 minutes
Installation et imports
Chargeons dotNetRDF avec les espaces de noms necessaires pour travailler avec OWL.
dotNetRDF charge avec succes.
Espace de noms OWL disponible : VDS.RDF.Ontology
Interpretation
L’espace de noms VDS.RDF.Ontology est la cle pour travailler avec OWL dans dotNetRDF. Il fournit :
Classe
Rôle
OntologyGraph
Graphe specialise avec acces aux classes et proprietes OWL
OntologyClass
Represente une classe OWL (avec super/sous-classes, instances)
OntologyProperty
Represente une propriete OWL (avec domaine, portee)
OntologyResource
Classe de base pour les ressources ontologiques
1. Introduction a OWL 2 : au-dela de RDFS
Dans le notebook précédent (SW-6), nous avons vu que RDFS permet de définir des classes, des hiérarchies et des proprietes avec inference. Cependant, RDFS a des limites importantes.
OWL 2 (Web Ontology Language) est base sur la logique descriptive (Description Logic, DL), un fragment decidable de la logique du premier ordre.
OWL 2 (Web Ontology Language) est le standard W3C de modelisation d’ontologies (Motik, Patel-Schneider & Cuenca Grau eds., W3C Recommendation 2009/2012). Il repose formellement sur la logique descriptive (Description Logic, DL) – un fragment decidable de la logique du premier ordre dont la théorie est synthetisee dans Baader, Calvanese, McGuinness, Nardi & Patel-Schneider, The Description Logic Handbook (Cambridge University Press, 2e ed. 2007). Le vocabulaire DL (Concept / Rôle / Individu / TBox / ABox) introduit ci-dessous est celui de cette tradition.
2. OntologyGraph : la classe specialisee de dotNetRDF
OntologyGraph etend Graph avec des méthodes spécifiques pour naviguer dans les ontologies OWL et RDFS. Creons une première ontologie simple en code.
// Creer une ontologie simple avec OntologyGraphOntologyGraph onto =newOntologyGraph();onto.NamespaceMap.AddNamespace("uni",newUri("http://example.org/university#"));onto.NamespaceMap.AddNamespace("owl",newUri("http://www.w3.org/2002/07/owl#"));onto.NamespaceMap.AddNamespace("rdf",newUri("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));onto.NamespaceMap.AddNamespace("rdfs",newUri("http://www.w3.org/2000/01/rdf-schema#"));onto.NamespaceMap.AddNamespace("xsd",newUri("http://www.w3.org/2001/XMLSchema#"));// Noeuds predicatsIUriNode rdfType = onto.CreateUriNode("rdf:type");IUriNode owlClass = onto.CreateUriNode("owl:Class");IUriNode subClassOf = onto.CreateUriNode("rdfs:subClassOf");IUriNode rdfsLabel = onto.CreateUriNode("rdfs:label");// Definir les classes OWLIUriNode person = onto.CreateUriNode("uni:Person");IUriNode student = onto.CreateUriNode("uni:Student");IUriNode professor = onto.CreateUriNode("uni:Professor");IUriNode course = onto.CreateUriNode("uni:Course");IUriNode department = onto.CreateUriNode("uni:Department");// Declarer comme owl:Classforeach(var cls innew[]{ person, student, professor, course, department }) onto.Assert(newTriple(cls, rdfType, owlClass));// Hierarchieonto.Assert(newTriple(student, subClassOf, person));onto.Assert(newTriple(professor, subClassOf, person));// Etiquettesonto.Assert(newTriple(person, rdfsLabel, onto.CreateLiteralNode("Personne","fr")));onto.Assert(newTriple(student, rdfsLabel, onto.CreateLiteralNode("Etudiant","fr")));onto.Assert(newTriple(professor, rdfsLabel, onto.CreateLiteralNode("Professeur","fr")));onto.Assert(newTriple(course, rdfsLabel, onto.CreateLiteralNode("Cours","fr")));onto.Assert(newTriple(department, rdfsLabel, onto.CreateLiteralNode("Departement","fr")));Console.WriteLine($"Ontologie creee : {onto.Triples.Count} triplets");// Utiliser l'API OntologyGraph pour explorerConsole.WriteLine("\nClasses OWL trouvees :");foreach(OntologyClass cls in onto.OwlClasses){string uri = cls.Resource.ToString();if(uri.Contains("university#")){string localName = uri.Split('#').Last(); Console.WriteLine($" - {localName}");}}Console.WriteLine();// Naviguer dans la hierarchie avec OntologyClassConsole.WriteLine("Hierarchie via OntologyClass :");foreach(OntologyClass cls in onto.OwlClasses){string uri = cls.Resource.ToString();if(!uri.Contains("university#"))continue;string name = uri.Split('#').Last();var superClasses = cls.SuperClasses.Where(sc => sc.Resource.ToString().Contains("university#")).Select(sc => sc.Resource.ToString().Split('#').Last());if(superClasses.Any()) Console.WriteLine($" {name} rdfs:subClassOf {string.Join(",", superClasses)}");var subClasses = cls.SubClasses.Where(sc => sc.Resource.ToString().Contains("university#")).Select(sc => sc.Resource.ToString().Split('#').Last());if(subClasses.Any()) Console.WriteLine($" {name} a pour sous-classes : {string.Join(",", subClasses)}");}
Ontologie creee : 12 triplets
Classes OWL trouvees :
- Person
- Student
- Professor
- Course
- Department
Hierarchie via OntologyClass :
Person a pour sous-classes : Student, Professor
Student rdfs:subClassOf Person
Professor rdfs:subClassOf Person
Interpretation : OntologyGraph vs Graph
Aspect
Graph
OntologyGraph
Triplets
Oui
Oui (herite de Graph)
OwlClasses
Non
Oui – enumere les classes owl:Class
RdfClasses
Non
Oui – enumere les classes rdfs:Class
AllClasses
Non
Oui – union des deux
OwlProperties
Non
Oui – proprietes OWL
Navigation sémantique
Non
SuperClasses, SubClasses, Instances
Points cles : 1. onto.OwlClasses retourne uniquement les ressources typees owl:Class 2. OntologyClass.SuperClasses et SubClasses retournent les relations directes seulement 3. Pour obtenir la fermeture transitive, il faut iterer ou utiliser un raisonneur
OWL 2 offre des constructeurs de classes bien plus riches que RDFS :
Constructeur
Syntaxe OWL
Description
Classe nommee
owl:Class
Classe définie par un URI
Equivalence
owl:equivalentClass
Deux classes ont exactement les mêmes instances
Disjonction
owl:disjointWith
Aucune instance commune
Complement
owl:complementOf
Tout ce qui n’est PAS dans la classe
Union
owl:unionOf
Instances de C1 OU C2
Intersection
owl:intersectionOf
Instances de C1 ET C2
Restriction
owl:Restriction
Classe définie par une contrainte sur une propriete
Demontrons owl:equivalentClass et owl:disjointWith en code.
// Demonstration de owl:equivalentClass et owl:disjointWithOntologyGraph classDemo =newOntologyGraph();classDemo.NamespaceMap.AddNamespace("ex",newUri("http://example.org/transport#"));classDemo.NamespaceMap.AddNamespace("owl",newUri("http://www.w3.org/2002/07/owl#"));classDemo.NamespaceMap.AddNamespace("rdf",newUri("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));classDemo.NamespaceMap.AddNamespace("rdfs",newUri("http://www.w3.org/2000/01/rdf-schema#"));IUriNode rdfTypeC = classDemo.CreateUriNode("rdf:type");IUriNode owlClassC = classDemo.CreateUriNode("owl:Class");IUriNode equivalentClass = classDemo.CreateUriNode("owl:equivalentClass");IUriNode disjointWith = classDemo.CreateUriNode("owl:disjointWith");IUriNode subClassOfC = classDemo.CreateUriNode("rdfs:subClassOf");// ClassesIUriNode vehicule = classDemo.CreateUriNode("ex:Vehicule");IUriNode voiture = classDemo.CreateUriNode("ex:Voiture");IUriNode automobile = classDemo.CreateUriNode("ex:Automobile");IUriNode moto = classDemo.CreateUriNode("ex:Moto");IUriNode velo = classDemo.CreateUriNode("ex:Velo");// Declarer les classesforeach(var cls innew[]{ vehicule, voiture, automobile, moto, velo }) classDemo.Assert(newTriple(cls, rdfTypeC, owlClassC));// HierarchieclassDemo.Assert(newTriple(voiture, subClassOfC, vehicule));classDemo.Assert(newTriple(moto, subClassOfC, vehicule));classDemo.Assert(newTriple(velo, subClassOfC, vehicule));// owl:equivalentClass : Voiture = Automobile (ce sont des synonymes)classDemo.Assert(newTriple(voiture, equivalentClass, automobile));// owl:disjointWith : une Voiture ne peut pas etre une Moto, etc.classDemo.Assert(newTriple(voiture, disjointWith, moto));classDemo.Assert(newTriple(voiture, disjointWith, velo));classDemo.Assert(newTriple(moto, disjointWith, velo));Console.WriteLine($"Ontologie vehicules : {classDemo.Triples.Count} triplets");Console.WriteLine();// Afficher les equivalencesConsole.WriteLine("Equivalences (owl:equivalentClass) :");foreach(Triple t in classDemo.GetTriplesWithPredicate(equivalentClass)){string s =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#');string o =((IUriNode)t.Object).Uri.Fragment.TrimStart('#'); Console.WriteLine($" {s} = {o}");}Console.WriteLine();// Afficher les disjonctionsConsole.WriteLine("Disjonctions (owl:disjointWith) :");foreach(Triple t in classDemo.GetTriplesWithPredicate(disjointWith)){string s =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#');string o =((IUriNode)t.Object).Uri.Fragment.TrimStart('#'); Console.WriteLine($" {s} disjoint de {o}");}
Ontologie vehicules : 12 triplets
Equivalences (owl:equivalentClass) :
Voiture = Automobile
Disjonctions (owl:disjointWith) :
Voiture disjoint de Moto
Voiture disjoint de Velo
Moto disjoint de Velo
Interpretation : Equivalence et disjonction
Relation
Signification
Consequence logique
Voiture owl:equivalentClass Automobile
Synonymes parfaits
Toute Voiture est une Automobile et vice-versa
Voiture owl:disjointWith Moto
Ensembles disjoints
Aucun individu ne peut etre a la fois Voiture et Moto
Moto owl:disjointWith Velo
Ensembles disjoints
Aucun individu ne peut etre a la fois Moto et Velo
Différence entre equivalentClass et subClassOf : - A rdfs:subClassOf B : toute instance de A est instance de B (inclusion, \(A \subseteq B\)) - A owl:equivalentClass B : A et B ont exactement les mêmes instances (\(A = B\))
Cas d’usage : owl:equivalentClass est souvent utilise pour relier des termes de vocabulaires différents (ex: schema:Person owl:equivalentClass foaf:Person).
4. Proprietes OWL
OWL distingue deux types fondamentaux de proprietes et offre des caractéristiques supplementaires :
Types de proprietes
Type
Declaration
Description
Object Property
owl:ObjectProperty
Relie deux individus (sujet et objet sont des ressources)
Datatype Property
owl:DatatypeProperty
Relie un individu a une valeur litterale
Caractéristiques des proprietes
Caractéristique
Syntaxe
Signification
Fonctionnelle
owl:FunctionalProperty
Au plus une valeur par sujet
Inverse-fonctionnelle
owl:InverseFunctionalProperty
Au plus un sujet par valeur
Transitive
owl:TransitiveProperty
Si a->b et b->c alors a->c
Symetrique
owl:SymmetricProperty
Si a->b alors b->a
Inverse
owl:inverseOf
P1 est l’inverse de P2
Construisons un exemple complet avec les proprietes d’une ontologie universitaire.
// Proprietes OWL : types et caracteristiquesOntologyGraph propDemo =newOntologyGraph();propDemo.NamespaceMap.AddNamespace("uni",newUri("http://example.org/university#"));propDemo.NamespaceMap.AddNamespace("owl",newUri("http://www.w3.org/2002/07/owl#"));propDemo.NamespaceMap.AddNamespace("rdf",newUri("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));propDemo.NamespaceMap.AddNamespace("rdfs",newUri("http://www.w3.org/2000/01/rdf-schema#"));propDemo.NamespaceMap.AddNamespace("xsd",newUri("http://www.w3.org/2001/XMLSchema#"));IUriNode rdfTypeP = propDemo.CreateUriNode("rdf:type");IUriNode owlObjProp = propDemo.CreateUriNode("owl:ObjectProperty");IUriNode owlDatatypeProp = propDemo.CreateUriNode("owl:DatatypeProperty");IUriNode owlFunctional = propDemo.CreateUriNode("owl:FunctionalProperty");IUriNode owlInverseOf = propDemo.CreateUriNode("owl:inverseOf");IUriNode rdfsDomainP = propDemo.CreateUriNode("rdfs:domain");IUriNode rdfsRangeP = propDemo.CreateUriNode("rdfs:range");IUriNode rdfsLabelP = propDemo.CreateUriNode("rdfs:label");IUriNode owlClassP = propDemo.CreateUriNode("owl:Class");// Classes (necessaires pour domaine/portee)IUriNode personP = propDemo.CreateUriNode("uni:Person");IUriNode studentP = propDemo.CreateUriNode("uni:Student");IUriNode professorP = propDemo.CreateUriNode("uni:Professor");IUriNode courseP = propDemo.CreateUriNode("uni:Course");IUriNode deptP = propDemo.CreateUriNode("uni:Department");foreach(var c innew[]{ personP, studentP, professorP, courseP, deptP }) propDemo.Assert(newTriple(c, rdfTypeP, owlClassP));// --- Object Properties ---// uni:teaches (Professor -> Course)IUriNode teaches = propDemo.CreateUriNode("uni:teaches");propDemo.Assert(newTriple(teaches, rdfTypeP, owlObjProp));propDemo.Assert(newTriple(teaches, rdfsDomainP, professorP));propDemo.Assert(newTriple(teaches, rdfsRangeP, courseP));propDemo.Assert(newTriple(teaches, rdfsLabelP, propDemo.CreateLiteralNode("enseigne","fr")));// uni:taughtBy (Course -> Professor) - INVERSE de teachesIUriNode taughtBy = propDemo.CreateUriNode("uni:taughtBy");propDemo.Assert(newTriple(taughtBy, rdfTypeP, owlObjProp));propDemo.Assert(newTriple(taughtBy, rdfsDomainP, courseP));propDemo.Assert(newTriple(taughtBy, rdfsRangeP, professorP));propDemo.Assert(newTriple(taughtBy, owlInverseOf, teaches));// uni:enrolledIn (Student -> Course)IUriNode enrolledIn = propDemo.CreateUriNode("uni:enrolledIn");propDemo.Assert(newTriple(enrolledIn, rdfTypeP, owlObjProp));propDemo.Assert(newTriple(enrolledIn, rdfsDomainP, studentP));propDemo.Assert(newTriple(enrolledIn, rdfsRangeP, courseP));// --- Datatype Properties ---// uni:name (Person -> xsd:string) - FONCTIONNELLE (au plus 1 nom)IUriNode nameProp = propDemo.CreateUriNode("uni:name");propDemo.Assert(newTriple(nameProp, rdfTypeP, owlDatatypeProp));propDemo.Assert(newTriple(nameProp, rdfTypeP, owlFunctional));// Au plus 1 valeurpropDemo.Assert(newTriple(nameProp, rdfsDomainP, personP));propDemo.Assert(newTriple(nameProp, rdfsRangeP, propDemo.CreateUriNode("xsd:string")));// uni:credits (Course -> xsd:integer)IUriNode credits = propDemo.CreateUriNode("uni:credits");propDemo.Assert(newTriple(credits, rdfTypeP, owlDatatypeProp));propDemo.Assert(newTriple(credits, rdfsDomainP, courseP));propDemo.Assert(newTriple(credits, rdfsRangeP, propDemo.CreateUriNode("xsd:integer")));Console.WriteLine($"Ontologie avec proprietes : {propDemo.Triples.Count} triplets");Console.WriteLine();// Afficher les Object PropertiesConsole.WriteLine("Object Properties :");foreach(Triple t in propDemo.GetTriplesWithPredicateObject(rdfTypeP, owlObjProp)){string name =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#');var dom = propDemo.GetTriplesWithSubjectPredicate(t.Subject, rdfsDomainP);var rng = propDemo.GetTriplesWithSubjectPredicate(t.Subject, rdfsRangeP);string d = dom.Any()?((IUriNode)dom.First().Object).Uri.Fragment.TrimStart('#'):"?";string r = rng.Any()?((IUriNode)rng.First().Object).Uri.Fragment.TrimStart('#'):"?"; Console.WriteLine($" {name} : {d} -> {r}");}Console.WriteLine();// Afficher les Datatype PropertiesConsole.WriteLine("Datatype Properties :");foreach(Triple t in propDemo.GetTriplesWithPredicateObject(rdfTypeP, owlDatatypeProp)){string name =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#');var dom = propDemo.GetTriplesWithSubjectPredicate(t.Subject, rdfsDomainP);var rng = propDemo.GetTriplesWithSubjectPredicate(t.Subject, rdfsRangeP);string d = dom.Any()?((IUriNode)dom.First().Object).Uri.Fragment.TrimStart('#'):"?";string r = rng.Any()?((IUriNode)rng.First().Object).Uri.Fragment.TrimStart('#'):"?";bool isFunctional = propDemo.GetTriplesWithSubjectPredicate(t.Subject, rdfTypeP).Any(tr => tr.Object.ToString().Contains("FunctionalProperty"));string funcTag = isFunctional ?" [fonctionnelle]":""; Console.WriteLine($" {name} : {d} -> {r}{funcTag}");}Console.WriteLine();// Afficher les proprietes inversesConsole.WriteLine("Proprietes inverses (owl:inverseOf) :");foreach(Triple t in propDemo.GetTriplesWithPredicate(owlInverseOf)){string s =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#');string o =((IUriNode)t.Object).Uri.Fragment.TrimStart('#'); Console.WriteLine($" {s} est l'inverse de {o}");}
Ontologie avec proprietes : 23 triplets
Object Properties :
teaches : Professor -> Course
taughtBy : Course -> Professor
enrolledIn : Student -> Course
Datatype Properties :
name : Person -> string [fonctionnelle]
credits : Course -> integer
Proprietes inverses (owl:inverseOf) :
taughtBy est l'inverse de teaches
Interpretation : Proprietes OWL
Propriete
Type
Domaine
Portee
Particularite
teaches
ObjectProperty
Professor
Course
–
taughtBy
ObjectProperty
Course
Professor
Inverse de teaches
enrolledIn
ObjectProperty
Student
Course
–
name
DatatypeProperty
Person
xsd:string
Fonctionnelle (au plus 1 valeur)
credits
DatatypeProperty
Course
xsd:integer
–
Points cles : 1. ObjectProperty relie deux individus : prof_dupont teaches course_ia 2. DatatypeProperty relie un individu a une valeur : prof_dupont name "Marie Dupont" 3. FunctionalProperty garantit l’unicite : une personne a au plus un nom 4. owl:inverseOf permet la navigation bidirectionnelle : si “Dupont enseigne IA”, alors “IA est enseigne par Dupont”
En RDFS, rdf:Property ne distingue pas ObjectProperty et DatatypeProperty. OWL rend cette distinction explicite, ce qui ameliore la validation et le raisonnement.
5. Profils OWL 2 : EL, QL, RL
OWL 2 définit trois profils (sous-ensembles) qui offrent des compromis entre expressivite et performances de raisonnement :
Profil
Logique
Expressivite
Performance
Cas d’usage
OWL 2 EL
\(\mathcal{EL}^{++}\)
Limitee
Temps polynomial
Grandes ontologies biomedicales (SNOMED CT, Gene Ontology)
OWL 2 QL
DL-Lite
Limitee
Reductible a SQL
Acces par requêtes SPARQL/SQL, OBDA
OWL 2 RL
Règles
Moderee
Implementable par règles
Systèmes a base de règles, validation
OWL 2 DL
\(\mathcal{SROIQ}(D)\)
Maximale
Decidable mais couteux
Ontologies complexes, raisonnement complet
Les logiques sous-jacentes aux profils sont des fragments DL nommes et etudies : \(\mathcal{EL}^{++}\) pour OWL 2 EL (Baader, Brandt & Lutz, “Pushing the EL Envelope”, IJCAI 2005), DL-Lite pour OWL 2 QL (Calvanese, De Giacomo, Lembo, Lenzerini & Rosati, 2007) et \(\mathcal{SROIQ}(D)\) pour OWL 2 DL (Horrocks, Kutz & Sattler, 2006). Les profils eux-mêmes sont specifies par le W3C (Motik et al., OWL 2 Profiles, 2009).
Comparaison detaillee
Critere
EL
QL
RL
Conjonction de classes
Oui
Non
Oui
Quantification existentielle
Oui
Limitee
Non
Negation
Non
Oui (limitee)
Oui (limitee)
Cardinalite
Non
Non
Oui (limitee)
Nombre de classes
> 100 000
> 10 000
> 10 000
Implementation typique
Classificateur
Rewriting SPARQL
Règles de production
Comment choisir ?
Votre ontologie est-elle très grande (>100K classes) ?
--> OUI : OWL 2 EL
--> NON :
Acces par requêtes SQL/SPARQL principalement ?
--> OUI : OWL 2 QL
--> NON :
Implementation par règles ?
--> OUI : OWL 2 RL
--> NON : OWL 2 DL (expressivite maximale)
Exemples d’ontologies reelles
Ontologie
Profil
Domaine
Taille
SNOMED CT
EL
Terminologie medicale
~350 000 concepts
Gene Ontology
EL
Bioinformatique
~45 000 termes
Schema.org
RL
SEO, données structurees
~800 types
DBpedia Ontology
RL
Connaissances générales
~760 classes
CIDOC-CRM
DL
Patrimoine culturel
~90 classes
6. Exemple guide avec data/university.owl
Le fichier data/university.owl contient une petite ontologie OWL 2 au format RDF/XML. Chargeons-la avec OntologyGraph et explorons sa structure.
Ontologie chargee : 64 triplets
Namespaces : rdf, rdfs, xsd, owl, xml, uni
=== Classes OWL ===
Person (Personne)
Student (Etudiant)
rdfs:subClassOf Person
Professor (Professeur)
rdfs:subClassOf Person
Course (Cours)
Department (Departement)
GraduateStudent (Etudiant en master/doctorat)
rdfs:subClassOf Student, Person
Interpretation : Structure de university.owl
L’ontologie contient :
Élément
Exemples
Classes
Person, Student, Professor, GraduateStudent, Course, Department
Hiérarchie
Student < Person, Professor < Person, GraduateStudent < Student
Explorons les proprietes de l’ontologie et les relations entre individus.
// Explorer les proprietes et individus de university.owlIUriNode rdfTypeU = uniOnto.CreateUriNode(newUri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"));IUriNode owlObjPropU = uniOnto.CreateUriNode(newUri("http://www.w3.org/2002/07/owl#ObjectProperty"));IUriNode owlDtPropU = uniOnto.CreateUriNode(newUri("http://www.w3.org/2002/07/owl#DatatypeProperty"));IUriNode rdfsDomainU = uniOnto.CreateUriNode(newUri("http://www.w3.org/2000/01/rdf-schema#domain"));IUriNode rdfsRangeU = uniOnto.CreateUriNode(newUri("http://www.w3.org/2000/01/rdf-schema#range"));IUriNode rdfsLabelU = uniOnto.CreateUriNode(newUri("http://www.w3.org/2000/01/rdf-schema#label"));Console.WriteLine("=== Object Properties ===");foreach(Triple t in uniOnto.GetTriplesWithPredicateObject(rdfTypeU, owlObjPropU)){string name =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#');var dom = uniOnto.GetTriplesWithSubjectPredicate(t.Subject, rdfsDomainU);var rng = uniOnto.GetTriplesWithSubjectPredicate(t.Subject, rdfsRangeU);var lbl = uniOnto.GetTriplesWithSubjectPredicate(t.Subject, rdfsLabelU);string d = dom.Any()?((IUriNode)dom.First().Object).Uri.Fragment.TrimStart('#'):"?";string r = rng.Any()?((IUriNode)rng.First().Object).Uri.Fragment.TrimStart('#'):"?";string l = lbl.Any()?((ILiteralNode)lbl.First().Object).Value:""; Console.WriteLine($" {name} ({l}) : {d} -> {r}");}Console.WriteLine();Console.WriteLine("=== Datatype Properties ===");foreach(Triple t in uniOnto.GetTriplesWithPredicateObject(rdfTypeU, owlDtPropU)){string name =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#');var dom = uniOnto.GetTriplesWithSubjectPredicate(t.Subject, rdfsDomainU);var rng = uniOnto.GetTriplesWithSubjectPredicate(t.Subject, rdfsRangeU);string d = dom.Any()?((IUriNode)dom.First().Object).Uri.Fragment.TrimStart('#'):"?";string r = rng.Any()?((IUriNode)rng.First().Object).Uri.Fragment.TrimStart('#'):"?"; Console.WriteLine($" {name} : {d} -> {r}");}
=== Object Properties ===
enrolledIn (inscrit a) : Student -> Course
teaches (enseigne) : Professor -> Course
memberOf (membre de) : Person -> Department
advisedBy (dirige par) : GraduateStudent -> Professor
=== Datatype Properties ===
name : Person -> string
credits : Course -> integer
Interpretation : Proprietes de university.owl
Propriete
Type
Domaine
Portee
Description
enrolledIn
ObjectProperty
Student
Course
Inscription a un cours
teaches
ObjectProperty
Professor
Course
Enseignement
memberOf
ObjectProperty
Person
Department
Appartenance
advisedBy
ObjectProperty
GraduateStudent
Professor
Direction de these
name
DatatypeProperty
Person
xsd:string
Nom de la personne
credits
DatatypeProperty
Course
xsd:integer
Nombre de credits ECTS
Requêtes SPARQL sur l’ontologie
Utilisons SPARQL pour interroger les individus et croiser les informations.
// Requetes SPARQL sur university.owlvar uniDataset =newInMemoryDataset(uniOnto);var uniProcessor =newLeviathanQueryProcessor(uniDataset);var sparqlParser =newSparqlQueryParser();// Requete 1 : Lister toutes les personnes avec leur typestring q1 = @"PREFIX uni:<http://example.org/university#>PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#>PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#>SELECT ?person ?name ?typeWHERE {?person rdf:type ?type .?type rdfs:subClassOf* uni:Person .?person uni:name ?name .}ORDER BY ?name";SparqlResultSet r1 =(SparqlResultSet)uniProcessor.ProcessQuery(sparqlParser.ParseFromString(q1));Console.WriteLine("Personnes dans l'ontologie :");Console.WriteLine("---------------------------");foreach(SparqlResult r in r1){string name =((ILiteralNode)r["name"]).Value;string type =((IUriNode)r["type"]).Uri.Fragment.TrimStart('#'); Console.WriteLine($" {name} (type: {type})");}Console.WriteLine();// Requete 2 : Qui enseigne quoi ?string q2 = @"PREFIX uni:<http://example.org/university#>PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#>SELECT ?profName ?courseName ?creditsWHERE {?prof uni:teaches ?course .?prof uni:name ?profName .?course rdfs:label ?courseName . OPTIONAL {?course uni:credits ?credits .}}ORDER BY ?profName ?courseName";SparqlResultSet r2 =(SparqlResultSet)uniProcessor.ProcessQuery(sparqlParser.ParseFromString(q2));Console.WriteLine("Enseignements :");Console.WriteLine("---------------");foreach(SparqlResult r in r2){string prof =((ILiteralNode)r["profName"]).Value;string course =((ILiteralNode)r["courseName"]).Value;string cred = r.HasBoundValue("credits")?((ILiteralNode)r["credits"]).Value:"?"; Console.WriteLine($" {prof} enseigne '{course}' ({cred} credits)");}
Personnes dans l'ontologie :
---------------------------
Marie Dupont (type: Professor)
Pierre Martin (type: GraduateStudent)
Sophie Bernard (type: Student)
Enseignements :
---------------
Marie Dupont enseigne 'Intelligence Artificielle' (6 credits)
Marie Dupont enseigne 'Web Semantique' (4 credits)
Interpretation : Requêtes sur l’ontologie
Personne
Type
Cours
Rôle
Marie Dupont
Professor
IA, Web Sémantique
Enseigne
Pierre Martin
GraduateStudent
IA, Web Sémantique
Inscrit, dirige par Dupont
Sophie Bernard
Student
Web Sémantique
Inscrite
Points cles : 1. La requête avec rdfs:subClassOf* (chemin de propriete) remonte la hiérarchie de classes 2. OPTIONAL evite de perdre les résultats sans credits 3. L’ontologie permet de croiser les informations de différentes proprietes
Verifier les contraintes OWL
L’ontologie declare Professor owl:disjointWith Student. Verifions cette contrainte et observons ce qui se passe en cas de violation.
// Verifier la disjonction Professor / StudentIUriNode owlDisjointU = uniOnto.CreateUriNode(newUri("http://www.w3.org/2002/07/owl#disjointWith"));Console.WriteLine("Contraintes de disjonction :");foreach(Triple t in uniOnto.GetTriplesWithPredicate(owlDisjointU)){string s =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#');string o =((IUriNode)t.Object).Uri.Fragment.TrimStart('#'); Console.WriteLine($" {s} owl:disjointWith {o}");}Console.WriteLine();// Requete SPARQL pour detecter les violationsstring qViolation = @"PREFIX uni:<http://example.org/university#>PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#>PREFIX owl:<http://www.w3.org/2002/07/owl#>SELECT ?class1 ?class2 ?individualWHERE {?class1 owl:disjointWith ?class2 .?individual rdf:type ?class1 .?individual rdf:type ?class2 .}";SparqlResultSet violations =(SparqlResultSet)uniProcessor.ProcessQuery( sparqlParser.ParseFromString(qViolation));if(violations.Count==0){ Console.WriteLine("Aucune violation detectee. L'ontologie est coherente.");}else{ Console.WriteLine($"ATTENTION : {violations.Count} violation(s) !");}Console.WriteLine();// Demonstration : ajouter une violation intentionnelleConsole.WriteLine("--- Ajout intentionnel d'une violation ---");IUriNode profDupont = uniOnto.CreateUriNode(newUri("http://example.org/university#prof_dupont"));IUriNode studentType = uniOnto.CreateUriNode(newUri("http://example.org/university#Student"));IUriNode rdfTypeV = uniOnto.CreateUriNode(newUri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"));Triple violationTriple =newTriple(profDupont, rdfTypeV, studentType);uniOnto.Assert(violationTriple);SparqlResultSet violations2 =(SparqlResultSet)uniProcessor.ProcessQuery( sparqlParser.ParseFromString(qViolation));Console.WriteLine($"Violations detectees : {violations2.Count}");foreach(SparqlResult r in violations2){string ind =((IUriNode)r["individual"]).Uri.Fragment.TrimStart('#');string c1 =((IUriNode)r["class1"]).Uri.Fragment.TrimStart('#');string c2 =((IUriNode)r["class2"]).Uri.Fragment.TrimStart('#'); Console.WriteLine($" {ind} est a la fois {c1} et {c2}");}// Retirer la violationuniOnto.Retract(violationTriple);Console.WriteLine("\nViolation retiree. Ontologie restauree.")
Contraintes de disjonction :
Professor owl:disjointWith Student
Aucune violation detectee. L'ontologie est coherente.
--- Ajout intentionnel d'une violation ---
Violations detectees : 1
prof_dupont est a la fois Professor et Student
Violation retiree. Ontologie restauree.
Interpretation : Verification des contraintes
Étape
Résultat
Explication
Avant violation
0 violation
L’ontologie est coherente
Après prof_dupont rdf:type Student
1 violation
prof_dupont est Professor ET Student
Après retrait
0 violation
Coherence restauree
Points cles : 1. OWL définit des contraintes sémantiques verifiables par SPARQL 2. dotNetRDF ne bloque pas l’ajout de triplets violant une contrainte (hypothese du monde ouvert) 3. C’est au raisonneur ou a la requête de detecter les incoherences 4. Un raisonneur OWL complet (HermiT, Pellet) deduirait que l’ontologie est inconsistante
Hypothese du monde ouvert : en OWL, l’absence d’information ne signifie pas la negation. Le raisonneur ne rejette pas les triplets, il detecte les contradictions.
Appliquer l’inference RDFS a l’ontologie OWL
Bien que dotNetRDF ne fournisse pas de raisonneur OWL DL complet, le StaticRdfsReasoner permet de propager les types dans la hiérarchie.
// Recharger l'ontologie propre pour la demonstration d'inferenceGraph uniGraph =newGraph();RdfXmlParser rdfXmlParser2 =newRdfXmlParser();rdfXmlParser2.Load(uniGraph,"data/university.owl");int before = uniGraph.Triples.Count;// Types de etud_martin avant inferenceIUriNode martin = uniGraph.CreateUriNode(newUri("http://example.org/university#etud_martin"));IUriNode rdfTypeN = uniGraph.CreateUriNode(newUri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"));Console.WriteLine("Types de etud_martin AVANT inference :");foreach(Triple t in uniGraph.GetTriplesWithSubjectPredicate(martin, rdfTypeN)){if(t.Objectis IUriNode typeUri) Console.WriteLine($" - {typeUri.Uri.Fragment.TrimStart('#')}");}// Appliquer le raisonneur RDFSStaticRdfsReasoner rdfsReasoner =newStaticRdfsReasoner();rdfsReasoner.Initialise(uniGraph);rdfsReasoner.Apply(uniGraph);int after = uniGraph.Triples.Count;Console.WriteLine($"\nInference : {before} -> {after} triplets (+{after - before} inferes)");Console.WriteLine("\nTypes de etud_martin APRES inference :");foreach(Triple t in uniGraph.GetTriplesWithSubjectPredicate(martin, rdfTypeN)){if(t.Objectis IUriNode typeUri) Console.WriteLine($" - {typeUri.Uri.Fragment.TrimStart('#')}");}Console.WriteLine();IUriNode dupont = uniGraph.CreateUriNode(newUri("http://example.org/university#prof_dupont"));Console.WriteLine("Types de prof_dupont APRES inference :");foreach(Triple t in uniGraph.GetTriplesWithSubjectPredicate(dupont, rdfTypeN)){if(t.Objectis IUriNode typeUri) Console.WriteLine($" - {typeUri.Uri.Fragment.TrimStart('#')}");}
Types de etud_martin AVANT inference :
- GraduateStudent
Inference : 64 -> 71 triplets (+7 inferes)
Types de etud_martin APRES inference :
- GraduateStudent
- Student
- Person
Types de prof_dupont APRES inference :
- Professor
- Person
Interpretation : Inference sur l’ontologie OWL
Individu
Types explicites
Types inferes par RDFS
etud_martin
GraduateStudent
Student, Person
prof_dupont
Professor
Person
etud_bernard
Student
Person
Chaîne d’inference pour etud_martin :
etud_martin rdf:type GraduateStudent
GraduateStudent rdfs:subClassOf Student --> etud_martin rdf:type Student
Student rdfs:subClassOf Person --> etud_martin rdf:type Person
Limites du raisonneur dotNetRDF : - StaticRdfsReasoner applique uniquement les règles RDFS, pas les règles OWL - Il ne verifie pas owl:disjointWith, owl:FunctionalProperty, etc. - Pour un raisonnement OWL complet, il faut un raisonneur externe (HermiT, Pellet, FaCT++)
Exercices
Exercice 1 : Completer l’ontologie universitaire
Etendez l’ontologie university.owl en ajoutant : - Une classe ResearchProject (owl:Class) - Une propriete worksOn (owl:ObjectProperty, domaine: Professor, portee: ResearchProject) - Une propriete participatesIn (owl:ObjectProperty, domaine: GraduateStudent, portee: ResearchProject) - Deux instances de ResearchProject avec des labels - Rattachez prof_dupont et etud_martin a un projet - Ecrivez une requête SPARQL pour lister tous les participants a chaque projet
Exercice : Explorer les equivalences de classes OWL
La section sur owl:equivalentClass et owl:disjointWith a montre comment modeliser des relations entre classes. Verifiez quelles classes sont equivalentes dans university.owl.
Indices :
SPARQL : ?cls1 owl:equivalentClass ?cls2 pour trouver les equivalences
g.CreateUriNode("owl:equivalentClass") si necessaire
// Exercice : Explorer les equivalences de classes dans university.owl// TODO etudiant : chargez university.owl et trouvez toutes les classes equivalentes//// Etape 1 : charger le fichier university.owl dans un graphe// Etape 2 : ecrire une requete SPARQL pour trouver les paires owl:equivalentClass// Etape 3 : verifier aussi les owl:disjointWith// Etape 4 : afficher les resultats et interpreter les relations trouveesConsole.WriteLine("Exercice a completer");
Exercice a completer
// Exercice 1 : Completer l'ontologie universitaire// Recharger l'ontologie propreOntologyGraph exOnto =newOntologyGraph();RdfXmlParser exParser =newRdfXmlParser();exParser.Load(exOnto,"data/university.owl");// TODO: Ajouter la classe ResearchProject (owl:Class)// TODO: Ajouter les proprietes worksOn et participatesIn (owl:ObjectProperty)// TODO: Creer 2 instances de ResearchProject avec rdfs:label// TODO: Relier prof_dupont et etud_martin aux projets// TODO: Requete SPARQL pour lister les participants par projetConsole.WriteLine("Exercice 1 : a completer");
Exercice 1 : a completer
Exercice 2 : Detecter les incoherences
Ecrivez un programme qui : 1. Charge university.owl 2. Ajoute volontairement des triplets problematiques : - etud_bernard rdf:type Professor (violation de disjonction) - Un nouveau cours sans credits 3. Ecrivez des requêtes SPARQL pour detecter : - Les violations de disjonction (individu avec deux types disjoints) - Les cours sans credits (propriete manquante) - Les etudiants sans inscription (pas de enrolledIn) 4. Affichez un rapport de coherence
// Exercice 2 : Detecter les incoherencesOntologyGraph checkOnto =newOntologyGraph();RdfXmlParser checkParser =newRdfXmlParser();checkParser.Load(checkOnto,"data/university.owl");// TODO: Ajouter etud_bernard rdf:type Professor (violation)// TODO: Ajouter un cours sans credits// TODO: Requete pour violations de disjonction// TODO: Requete pour cours sans credits// TODO: Requete pour etudiants sans inscription// TODO: Afficher un rapport de coherenceConsole.WriteLine("Exercice 2 : a completer");
Exercice 2 : a completer
Resume
OWL 2 : vocabulaire essentiel
Terme OWL
Utilisation
Exemple
owl:Class
Declarer une classe
uni:Person rdf:type owl:Class
owl:ObjectProperty
Propriete entre individus
uni:teaches rdf:type owl:ObjectProperty
owl:DatatypeProperty
Propriete individu-valeur
uni:name rdf:type owl:DatatypeProperty
owl:equivalentClass
Synonymie de classes
Voiture owl:equivalentClass Automobile
owl:disjointWith
Classes exclusives
Professor owl:disjointWith Student
owl:FunctionalProperty
Au plus une valeur
uni:name (un seul nom par personne)
owl:inverseOf
Propriete miroir
taughtBy owl:inverseOf teaches
Ce que nous avons appris
OWL 2 depasse RDFS avec des classes disjointes, des proprietes typees et des restrictions
OntologyGraph de dotNetRDF facilite la navigation dans les ontologies
owl:equivalentClass declare la synonymie entre classes de vocabulaires différents
ObjectProperty vs DatatypeProperty distingue les relations entre individus et vers les valeurs
owl:FunctionalProperty et owl:inverseOf ajoutent des contraintes sur les proprietes
Les profils OWL 2 (EL, QL, RL) offrent des compromis entre expressivite et performance
SPARQL peut interroger et verifier les contraintes d’une ontologie OWL
RDFS vs OWL : recapitulatif comparatif
Capacite
RDFS (SW-6)
OWL 2 (SW-7)
Classes et hiérarchies
Oui
Oui + restrictions
Disjonction
Non
owl:disjointWith
Equivalence
Non
owl:equivalentClass
Proprietes inverses
Non
owl:inverseOf
Proprietes fonctionnelles
Non
owl:FunctionalProperty
Cardinalite
Non
owl:minCardinality, owl:maxCardinality
Logique decidable
Limitee
Garantie (selon profil)
Limites de OWL dans dotNetRDF
Pas de raisonneur OWL DL complet integre
StaticRdfsReasoner applique uniquement les règles RDFS
Pour un raisonnement OWL complet : utiliser HermiT (Glimm, Horrocks et al., HermiT: An OWL 2 Reasoner, J. Automated Reasoning 2014), Pellet (Sirin et al. 2007) ou FaCT++ (Tsarkov & Horrocks 2006) via un triple store
Prochaine étape
Dans le notebook suivant (SW-8-PythonRDF), nous decouvrirons : - La bibliotheque Python rdflib pour manipuler RDF - Comparaison de l’approche Python vs .NET - Interoperabilite des formats de serialisation
References
OWL 2 Web Ontology Language – Motik, Patel-Schneider & Cuenca Grau (eds.), W3C Recommendation (2009/2012). w3.org/TR/owl2-overview
The Description Logic Handbook – Baader, Calvanese, McGuinness, Nardi & Patel-Schneider (eds.), Cambridge University Press, 2e ed. (2007). Synthese de reference des logiques descriptives.
Pushing the EL Envelope (\(\mathcal{EL}^{++}\)) – Baader, Brandt & Lutz, IJCAI (2005). Logique du profil OWL 2 EL.
DL-Lite – Calvanese, De Giacomo, Lembo, Lenzerini & Rosati (2007). Logique du profil OWL 2 QL.
Comment choisir ?