--- jupytext: formats: ipynb,md:myst text_representation: extension: .md format_name: myst format_version: 0.13 jupytext_version: 1.14.1 kernelspec: display_name: Python 3 (ipykernel) language: python name: python3 --- # Milch (DE) Die maßgebliche Motivation für GenDifS ist das Konzept der `skos:Collection`-Klasse, veranschaulicht in dem Milch-Beispiel aus dem SKOS-Primer [4 Advanced SKOS: When KOSs are not Simple Anymore > 4.1 Collections of Concepts](https://www.w3.org/TR/skos-primer/#seccollections): milk cow milk goat milk buffalo milk In GenDifS wird diese Differenzierung wir folgt modelliert: ```{code-cell} ipython3 :tags: [hide-input] from gd05 import GenDifS_Map mm = "Milch_DE" o = GenDifS_Map(f"../mm/{mm}.mm") ``` ```{code-cell} ipython3 o.rdflib.serialize(destination=f"../ttl/{mm}.ttl") o.parser_markdown() ``` Anschauliche Erklärung: Offensichtlich sind in diesem Beispiel zwei parallele Teilmengen-Beziehungen enthalten. Die erste, in **Fettdruck** wiedergegebene Beziehung besteht zwischen den Hauptklassen Milch und Kuhmilch und spannt insgesamt den für Mindmaps typischen Primärbaum auf: ``` :Kuhmilch rdfs:subClassOf :Milch ``` Mit diesem Baum ist aber auch eine zweite, in der Abbildung in unterstrichenem *Kursivdruck* wiedergegebene Beziehung zwischen charakterisierenden Sekundärbaum eng verflochten: ``` :Kuh rdfs:subClassOf :Tier ``` In GenDifS werden zwei wie hier zusammengehörenden Bäume ineinander verflochten modelliert. Dieses Syntax-Detail von GenDifS entspringt der Beobachtung, dass wir oft einen Sekundärbaum verwenden, um einen Primärbaum zu modellieren. In Protegé sieht man, dass hier (mindestens) die folgenden zwei Begriffsbäume enthalten sind: ```{figure} ../images/Milch_DE_Protege.png --- name: fig-milch-beispiel alt: Milch > BY Fleisch_hat_Herkunft SOME Tier >> Kuhmilch >>> SOME Kuh --- Beispiel Milch in Protegé ``` ## T-Box: OWL-Klassen und -Inferencing Wir betrachten den Satz: Kuhmilch ist eine Milch, die von Rindern kommt. In diesem Satz sind diese Informationen enthalten: * `Kuhmilch`: Darum geht es im folgenden; Subjekt * `ist`: die Beziehung zum Oberbegriff ist (hier) eine Teilmengenbeziehung * `eine Milch ...`: erste Chrakterisierung von Kuhmilch, hier durch Kennzeichnung als Teilmenge des Oberbegriffs `Milch` * `eine Milch, die ...`: Charakterisierung einer speziellen Teilklasse von `Milch` durch eine zusätzliche chrakterisierende Eigenschaft * `von ... kommt.`: chrakteristisches Attribut ist die *Herkunft* (und nicht z.B. der Fettgehalt) * `... Rindern ...`: der Wert des chrakteristischen Attributs Herkunft sind Rinder. An der Formalisierung beteiligte Mengen: **Menge 1: alle Dinge, die eine Milch sind** :Milch a owl:Class . **Menge 2: alle Dinge, die als Herkunft "Kuh" haben** :hat_Herkunft_SOME_:Kuh a owl:Class ; owl:equivalentClass [ a owl:Restriction ; owl:onProperty :hat_Herkunft ; owl:someValuesFrom :Kuh ] . **Menge 3: alle Dinge, die eine Kuhmilch sind** :Kuhmilch a owl:Class . Die Mengen 1-3 stehen bis jetzt völlig nebeneinander. Um Inferencing zu ermöglichen, beschreiben wir ihren Zusammenhang mit Axiomen. ### Aufwärts-Inferencing Um ein relativ triviales Inferencing zu ermöglichen, fügen wir ein Subklassen-Axiom hinzu: **Menge 3 ist eine Teilmenge von Menge 1** :Kuhmilch rdfs:subClassOf :Milch . Aufgrund dieser Aussage wird ein Reasoner dann, wenn ein Ding X Element der Menge 3: Kuhmilch ist, dieses Ding auch als Element der Menge 1: Milch klassifizieren. Weil hier von einer Teilmenge auf die übergeordnete Menge geschlossen wird, wollen wir dieses Inferencing "aufwärts" nennen. ### Abwärts-Inferencing Um ein Ding anhand seiner Eigenschaften genauer klassifizieren zu können, benötigen wir erstens eine weitere Menge: **Menge 4 = Schnittmenge von Menge 1 und Menge 2:** Alle Dinge, die eine Milch sind, UND die als Herkunft Kuh haben. :Milch_AND_:hat_Herkunft_SOME_:Kuh a owl:Class ; owl:equivalentClass [ a owl:Class ; owl:intersectionOf ( :Milch :hat_Herkunft_SOME_:Kuh ) ] . Und um das Inferencing letztlich möglich zu machen, fügen wir zweitens noch eine weiteres Axiom hinzu, mit dem Menge 4 als Teilmenge von Menge 3 definiert wird: **Alle Dinge, die aus Menge 4 sind, sind automatisch auch enthalten in Menge 3** :Milch_AND_:hat_Herkunft_SOME_:Kuh rdfs:subClassOf :Kuhmilch . Indem wir die Schnittmenge von Menge 1 und 2 als Teilmenge von Menge 3 definieren, wird jetzt das gesuchte Inferencing unterstützt, das uns die Klassifiation erlaubt: Gegeben sei ein Ding X, von dem wir wissen, * dass es eine Milch ist: X ist Element von Menge 1; * dass es von einer Kuh stammt: X ist gleichzeitig auch Element von Menge 2. Aufgrund dieser Angaben wird der Reasoner X auch als Element der Menge 4 klassifizieren. Und da Menge 4 als eine Teilmenge von Menge 3 definiert wurde, wird der Reasoner dann X ebenfalls als Element der Menge 3 klassifizieren: Damit wäre der Klassifikationsschritt Milch + Herkunft Kuh = Kuhmilch durchgeführt! Formalisierung des Abwärts-Inferencings in Mengenschreibweise: Kuhmilch ⊇ {x | Milch(x) ∧ ∃y[hat_Herkunft(x,y) ∧ Kuh(y)] } Formalisierung in First Order Logic (FOL): ∀x[Kuhmilch(x) ← Milch(x) ∧ ∃y[hat_Herkunft(x,y) ∧ Kuh(y)] Formalisierung in Description Logic (DL): Kuhmilch ⊇ X X == Milch ∧ ∃ hat_Herkunft.Kuh (Wir haben hier `X` als Abkürzung für `:Milch_AND_:hat_Herkunft_SOME_:Kuh` verwendet.) ## A-Box In GenDifS wird eine Ontologie abstrakt in einer Mindmap-basierten Sprache dargestellt. Deshalb ist es nötig, die Mindmap in mindestens eine, und möglich, die Mindmap in mehr als eine Ontologiesprache zu übesetzen. Tatsächlich übersetzen wir einen einzigen Knoten in 3 verschiedene Objekte, die wir per Namespace unterscheiden: ``` @prefix ex: . @prefix x: . @prefix : . ``` Für `Kuhmilch` als OWL-Klasse verwenden wir den Namespace `:`. In ttl wird das Objekt `:Kuhmilch` also expandiert zu `http://mm2ttl.net/namespace/default#Kuhmilch`. ``` # BY/child.1, owl :Kuhmilch # Abkürzung für http://mm2ttl.net/namespace/default#Kuhmilch a owl:Class ; rdfs:subClassOf :milch . ``` Für jede OWL-Klasse legen wir eine Test-Instanz an, im Beispiel `:Kuhmilch` das Onjekt `ex::Kuhmilch`. Da das Präfix `ex:` zu `` expandiert wird, lautet die URI der Test-Instanz also `http://mm2ttl.net/namespace/ex#:Kuhmilch`. (Der verbleibende Doppelpunkt sieht vielleicht etwas komisch aus, ist aber in einer IRI syntaktisch erlaubt). ``` # BY/child.4, owl-test ex::Kuhmilch a :Kuhmilch . ``` ACHTUNG: Protege untedrückt in der Anzeige das Präfix. In der Folge werden `http://mm2ttl.net/namespace/default#Kuhmilch` und `http://mm2ttl.net/namespace/ex#:Kuhmilch` optisch gleich dargestellt; erst ein hover zeigt die unterschiedlichen Präfixe. ## A-Box Inferencing-Test Um zu prüfen, ob die Übersetzung von GenDifS nach OWL korrekt erfolgt und in Kombination mit dem vorgesehenen Reasoner - hier dem OWL-RL-Reasoner [owlrl](https://pypi.org/project/owlrl/) - funktioniert, fügen wir den automatisch angelegten Beispiel-Instanzen zusätzliche Features hinzu. Beispiel: ``` a :Milch ; gendifs:classifyLike ; :hat_Herkunft . ``` Nach dem Inferencing durch `owlrl` sind für die Beispiel-Instanz `` u.a. die folgenden Tripel in der ausmaterialisierten ttl-Datei enthalten: ``` a :Kuhmilch, :Milch, , , owl:Thing ; gendifs:classifyLike ; :hat_Herkunft . ``` ## SKOS Außerdem interpretieren wir die Mindmap als SKOS-Baum. Zum Knoten `Kuhmilch` erzeugen wir ein Objekt `x::Kuhmilch`, das zu `http://mm2ttl.net/namespace/x#:Kuhmilch` expandiert wird. ``` # BY/child.2, skos x::Kuhmilch rdf:type skos:Concept ; rdfs:broaderTransitive x::Milch . ``` Alle drei Objekte habe also völlig unterschiedliche IRIS, bezeichnen völlig unterschiedliche Dinge, und können in der selben ttl-Datei (und damit auch im selben named graph) nebeneinaner auftreten und in Beziehung treten, ohne dass man mit Punning etc. arbeiten muss.