Grenze / Edge (milk1)#

Hintergrund#

Wozu eine Taxonomie? Erstellen einer Klassifikation, um Objekte zu klassieren. Denn Dinge zu unterscheiden ist eine der wichtigsten, grundlegenden Tätigkeiten in der Entwicklung des Lebens: Essbar oder giftig? Hase oder Löwe? Freund oder Feind? – Dinge zu unterscheiden ist insbesondere in der Wissenschaft relevant. Analytische Unterscheidungen generieren die begrifflichen Grundlagen jeder Theorie.

Mit GenDifS folgen wir einer grundlegenden philosophisch-ontologischen Weltsicht:

  • Es gibt Dinge und Eigenschaften;

  • Dinge unterscheiden sich durch ihre Eigenschaften;

  • Dinge zu unterscheiden heiß zur erkennen, welche Eigenschaften für diese Dinge einen relevanten Unterschied machen;

  • die Relevanz von Unterschieden ergibt sich nicht aus den Dingen an sich, sondern aus einer Aufgabe, einer Handlung, einem sozialen oder kommunikativen Zusammenhang;

  • erfolgreiches Handeln setzt voraus, die entscheidenden Unterschiede einer Situation zu erkennen

  • … und kommunizieren, einer Kritik zugänglich machen, und dazu aufschreiben zu können.

Es stellt sich die Frage, wie man eine Klassifikation aufschreibt. Die Mathematik hat dazu die formale Logik erfunden, die allerdings für die Praxis zu kompliziert ist. GenDifS ist eine Sprache, in der Taxonomien als Mindmap aufgeschrieben werden können. Das ist zunächst “nur” eine wesentliche Vereinfachung – aber wer je mit einer formalen Logik gearbeitet hat weiß, dass schon das alleine nicht wenig ist. Eine abstrakte high level Modellerierungssprache wie GenDifS bietet darüber hinaus aber auch neue Möglichkeiten der Modellierung.

Wie geht man vor, um eine Taxonomie zu entwickeln? Nicht immer hat man schon alles im Kopf, das man „nur noch aufschreiben“ muss; manchmal entsteht Klarheit erst durch ein Denken, das durch Kommunikation und insbesondere Schreiben unterstützt wird (Kleist 1806: Ueber die allmähliche Verfertigung der Gedanken beim Reden). Schreibendes Denken basiert im Kern auf einer für die Codierung und Thematik spezifischen Interaktion zwischen Mensch, Symbolsystem und Tool. Bei Musikern ist das ein Musikinstrument und eine Sequenzer- oder Notensatz-Software. Philosophen genügt Papier und Bleistift, Mathematikern Papier, Bleistift und ein Radiergummi. Informatiker benutzen z.T. komplexe Entwicklungsumgebungen für ihr Software-Engineering.

Mit GenDifS kann man eine Taxonomie konzise in einem (im Prinzip beliebigen) Mindmap-Tool aufschreiben (wir empfehlen https://freemind.sourceforge.io/wiki/index.php/Main_Page oder besser das modernere https://docs.freeplane.org/). Das Python-Skript gd.py wird eine durch freemind oder freeplane erzeugte XML-Datei der in verschiedene (!) formale Semantic Web Modelle übersetzen, und kann diese Modelle dann auch gleich auf einen Datensatz anwenden. Die Rückmeldung aus diesem Prozess kann dann genutzt werden, um das GenDifS Modell zu erweitern. Mit GenDifS kann eine Taxonomie also nicht nur aufgeschrieben, sondern auch ausprobiert und weiterentwicklet werden: wenn man eine GenDifS Taxonomie auf einen Datensatz anwendet, kann GenDifS Rückmeldung geben, welche Elemente nicht vollständig klassiert werden können. Diese Information kann der Mensch dann nutzen um zu beurteilen:

  • Sperrt sich das neue Element der Klassierung, weil seine Daten unvollständig sind? Dann kann man eine GenDifS-Taxonomie nutzen, um gezielt Eigenschaften zu erheben, die eine weitere Klassierung unterstützen;

  • Sperrt sich das neue Element der Klassierung, weil es Daten enthält, die in unserer Taxonomie unbekannt sind, also unsere Taxonomie unvollständig ist? Dann kann ein neues Element Anlass geben, unsere Taxonomie zu erweitern;

  • Sperrt sich das neue Element der Klassierung, weil seine Daten falsch erhoben wurde oder das Element selbst fehlerhaft ist? Dann können wir das fehlerhafte Element aussortieren und einer Ausnahmebehandlung zuführen.

Der kognitive Prozess, aus überraschenden Ursachen und Wirkungen auf eine Regel zu schließen, die zwischen Ursache und Wirkung einen regelhaften Zusammenhang herstellt, wird Abduktion genannt. Wenn GeDifS neue, bisher nicht vollständig berücksichtigte Eigenschaften entdeckt und der (hier: menschliche) Autor einer Taxonomie daraufhin die Taxonomie erweitert oder umbaut, findet beim Menschen abduktives Schließen statt. Wenn eine Taxonomie eine automatische logikgestützte Klassierung von Elementen erlaubt, kann der iterative Aufbau einer Taxonomie auf Basis eines exemplarischen Datensatzes als ein Prozess datengestützen iterativen abduktiven Schließens verstanden werden.

Werkstatt#

Diese Datei:

  • vor allem technisch explorieren, wie man die Grenze (EN: the edge) zwischen den Wassern eines Data Lakes und des trockenen Landes bestimmen und in geeigneten Datenstrukturen an einen user zurückmelden kann.

# Bibliothek laden
import gd07 as gd

Wir wollen die “Grenze” für jeden einzelnen Knoten bestimmen, und bei Bedarf daraus dann auch ein Gesamtmodell bauen.

(Ein bekanntes Problem von Ontologien und anderen Logisystemen besteht darin, dass sich eine beliebig kleine Detail-Änderung so auf das Gesamtsystem auswirken kann, dass alleine die Information “unerfüllbar” oder bei Queries eine Leere Menge zurückgegeben wird. Das wollen wir nicht. Eine GenDifS-Mindmap hat eine klare Struktur, die wir idealerweise maximal ausnutzen wollen.)

Wir wollen und können Edge, Integrität etc. auf Knotenebene bestimmen. Der einzig eindeutige Zugriff auf einzelne Knoten geht über die Knoten-ID. Diese wird von freemind automatisch vergeben, wir haben keinen Einfluss darauf – außer wir konstruieren die entsprechende XML-Datei manuell. Dazu kann man der Klasse GenDifS außer einer freemind-Mindmap (eine XML-Datei) auch eine Textdatei mit Einrückungen übergeben, in der man dann auch IDs manuell angeben kann. Uns interessiert der Mindmap-Knoten mit der ID focus_id, hier mit dem Wert 999.

focus_id = "999"

milk1 = f"""
TAXONOMY milk1
  milk
    ISA DISJOINT ID {focus_id}
      goat milk
      cow milk
"""

Diese Mindmap einlesen:

m = gd.GenDifS(from_string=milk1, verb=2)

So sieht unser XML aus:

from lxml import etree
print(etree.tostring(m.mindmap_xml, pretty_print=True, encoding="unicode"))
<map>
  <node TEXT="TAXONOMY milk1" ID="1">
    <node TEXT="milk" ID="2">
      <node TEXT="ISA DISJOINT ID 999" ID="999">
        <node TEXT="goat milk" ID="4"/>
        <node TEXT="cow milk" ID="5"/>
      </node>
    </node>
  </node>
</map>

Zur Kontrolle XML ‘rausschreiben, in Freemind einlesen:

m.mindmap_xml.write("milk1.mm", pretty_print=True)

m.describe_mindmap()
self.mindmap_topnode.get('TEXT')='TAXONOMY milk1'
len(self.mindmap_xml.getroot().xpath('.//node'))=5 nodes

language_list_list: Eine Liste von Sprach-Kombinationen, die erzeugt werden. Wir benötigen hier nur die Sprachkombination "RDFS_OWL".

m.compile(language_list_list= [ ["RDFS", "OWL"] ])

Die Taxonomie milk1 enthält nach dem kompilieren einen Graphen mit dem Namen RDFS_OWL:

milk1 = m.taxonomies_by_name["milk1"]
type(milk1)
gd07.GenDifS_taxonomy
len( milk1.rdf_graphs["RDFS_OWL"] )
5

Technisch erzeugt gd07 für jede Taxonomie ein Dictionary von Instanzen der Klasse gdn(GenDifsNode)

for gdn in milk1.dict_of_all_gdn.values():
  print(gdn.entity_context)
{'id': '1', 'codeclass': 'TAXONOMY', 'taxonomy_name': Entity(@id='1', @text='milk1'), 'graph': Entity(@id='1', @text='graph_1'), 'species_list': [Entity(@id='2', @text='milk')]}
{'id': '2', 'codeclass': 'taxon', 'species': Entity(@id='2', @text='milk')}
{'id': '999', 'codeclass': 'ISA', 'genus': Entity(@id='2', @text='milk'), 'species_list': [Entity(@id='4', @text='goat_milk'), Entity(@id='5', @text='cow_milk')]}
{'id': '4', 'codeclass': 'taxon', 'species': Entity(@id='4', @text='goat_milk')}
{'id': '5', 'codeclass': 'taxon', 'species': Entity(@id='5', @text='cow_milk')}

Wir betrachten im folgenden insbesondere den Knoten mit der ID 999 (siehe oben focus_id). Der Knoten hat als Genus milk und die Species goat_milk und cow_milk.

milk1.dict_of_all_gdn[focus_id].entity_context
{'id': '999',
 'codeclass': 'ISA',
 'genus': Entity(@id='2', @text='milk'),
 'species_list': [Entity(@id='4', @text='goat_milk'),
  Entity(@id='5', @text='cow_milk')]}

Wir wollen zu dieser RDFS und OWL T-Box eine A-Box im Format ttl hinzufügen. Für ttl und später SPARQL benötigen wir die entsprechenden Namespaces technisch gesehen leider in 2 leicht unterschiedlichen Formaten. In gd07 sind diese derzeit noch im Code hart codiert und können natürlich abgefragt werden. (VORSICHT Falle: man beachte den Punkt . am Ende der Zeile.)

ns_ttl = milk1.ttl_namespaces
print(ns_ttl)
@prefix ex: <http://example.net/namespace/ex#> .
@prefix cpt: <http://example.net/namespace/cpt#> .
@prefix sheet: <http://example.net/namespace/sheet#> .
@prefix : <http://example.net/namespace/default#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xml: <http://www.w3.org/XML/1998/namespace> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix gendifs: <http://jbusse.de/gendifs#> .
ns_sparql = milk1.sparql_namespaces
print(ns_sparql)
PREFIX  ex: <http://example.net/namespace/ex#>  
PREFIX  cpt: <http://example.net/namespace/cpt#>  
PREFIX  sheet: <http://example.net/namespace/sheet#>  
PREFIX  : <http://example.net/namespace/default#>  
PREFIX  owl: <http://www.w3.org/2002/07/owl#>  
PREFIX  rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>  
PREFIX  xml: <http://www.w3.org/XML/1998/namespace>  
PREFIX  xsd: <http://www.w3.org/2001/XMLSchema#>  
PREFIX  rdfs: <http://www.w3.org/2000/01/rdf-schema#>  
PREFIX  skos: <http://www.w3.org/2004/02/skos/core#>  
PREFIX  gendifs: <http://jbusse.de/gendifs#>  

Wir legen unsere abox technisch als ein String am, der Code im Format ttl enthält.

abox =  ns_ttl + "\n"

ex:goat_milk_101#

ex:goat_milk_101: Eine Instanz der Klasse :goat_milk.

Hier gibt es nicht zu klassieren, eine langweilige Instanz.

abox += """
ex:goat_milk_101
    a :goat_milk .
"""
print(abox)
@prefix ex: <http://example.net/namespace/ex#> .
@prefix cpt: <http://example.net/namespace/cpt#> .
@prefix sheet: <http://example.net/namespace/sheet#> .
@prefix : <http://example.net/namespace/default#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xml: <http://www.w3.org/XML/1998/namespace> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix gendifs: <http://jbusse.de/gendifs#> .

ex:goat_milk_101
    a :goat_milk .

ex:cow_milk_102#

Die Entity heißt zwar ex:cow_milk_102, ist aber nur als Instanz der Klasse :milk deklariert.

Die Klasse :milk hat zwei Subklassen. Aber es fehlen uns Informationen, ex:cow_milk_102 genauer zu klassifizieren: ex:cow_milk_102 gehört daher zum Rand. Wir erwarten von GenDifS, dass es einen Menschen auf diesen Ort unvollständigen Wissens hinweist.

Der Mensch hat dann insbesonderen die Möglichkeit zur Verfeinerung der Taxonomie:

  • Ergänze ex:cow_milk_102 ein zusätzliches Attribut-Wert-Paar, z.B. ex:cow_milk_102 :has_source ex:cow_102 . ex:cow_102 a :cow .

  • und erweitere die Taxonomie so, dass diese Information zur Einordnung in die Subklasse :cow_milk führt.

(Wir führen an anderer Stelle XXXX genau diese Reperatur durch.)

abox += """
ex:cow_milk_102
    a :milk .
"""

Qualitätssicherung beim Ausdrucken der A-Box: Nicht den selbst konstruierten ttl-String der A-Box, sondern in rdflib einlesen und serialisieren lassen.

import rdflib
g_102 = rdflib.Graph()
g_102.parse(data = abox )
print(g_102.serialize())
@prefix : <http://example.net/namespace/default#> .
@prefix ex: <http://example.net/namespace/ex#> .

ex:cow_milk_102 a :milk .

ex:goat_milk_101 a :goat_milk .

ex:goat_cow_milk_103#

Die Entity ex:goat_cow_milk_103 wird als Instanz sowohl der Klasse :goat_milk und :cow_milk deklariert.

Allerdings gibt unser Focus-Knoten durch den Text ISA DISJOINT an, dass die Subklassen disjunkt sind. Wir haben hier also einen Fehler in der A-Box vorliegen.

# DISJOINT problem
abox += """
ex:goat_cow_milk_103
    a :goat_milk ;
    a :cow_milk .
"""
g_103_ttl = rdflib.Graph().parse(data=abox).serialize()
print(g_103_ttl)
@prefix : <http://example.net/namespace/default#> .
@prefix ex: <http://example.net/namespace/ex#> .

ex:cow_milk_102 a :milk .

ex:goat_cow_milk_103 a :cow_milk,
        :goat_milk .

ex:goat_milk_101 a :goat_milk .

ex:horse_milk_104#

Die Instanz ex:horse_milk_104 sperrt sich einer Klassierung, da die dafür eigentlich erforderliche Subclass :horse__milk nicht vorgesehen ist.

Reparatur in der T-Box:

  • Subklasse :horse_milk und ggf. entsprechend :horse anlegen;

  • oder das Exemplar ex:horse_milk_104 in der A-Box als unzulässig zurückweisen?

(Aus didaktischen Gründen geben wir die A-Box im früher ausschließlich verwendeten Format RDF/XML aus. Es wird offensichtlich klar, dass Turtle in Bezug auf Lesbarkeit ein Forschritt bedeutet.)

# NO SUBCLASS AVAILABLE problem
abox += """
ex:horse_milk_104
    a :milk .
"""
print(rdflib.Graph().parse(data=abox).serialize(format="xml"))
<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
>
  <rdf:Description rdf:about="http://example.net/namespace/ex#goat_cow_milk_103">
    <rdf:type rdf:resource="http://example.net/namespace/default#goat_milk"/>
    <rdf:type rdf:resource="http://example.net/namespace/default#cow_milk"/>
  </rdf:Description>
  <rdf:Description rdf:about="http://example.net/namespace/ex#goat_milk_101">
    <rdf:type rdf:resource="http://example.net/namespace/default#goat_milk"/>
  </rdf:Description>
  <rdf:Description rdf:about="http://example.net/namespace/ex#horse_milk_104">
    <rdf:type rdf:resource="http://example.net/namespace/default#milk"/>
  </rdf:Description>
  <rdf:Description rdf:about="http://example.net/namespace/ex#cow_milk_102">
    <rdf:type rdf:resource="http://example.net/namespace/default#milk"/>
  </rdf:Description>
</rdf:RDF>

Anreicherung / Augmentation#

Anreichern heißt hier: In einer geeigneten Wissensrepräsentation (welche ist noch unklar, sujet-typisch vermutlich wieder ein RDF-Graph) Informationen ableiten,

  • welche Entities in der A-Box fehlerhaft sind;

  • zu welchen Entities aus dem Rand (edge) der A-Box weitere Informationen hilfreich wären (welche?), um ihre Klassierung voranzubringen;

  • wo und wie ggf. die Taxonomie erweitert werden könnte.

Vorgehen im Prinzip:

  • GenDifS Mindmap laden, übersetzen: Es entsteht eine Taxonomie, hier milk1.

  • mit der Taxonomie milk1 die A-Box analysieren, “durchkämmen”

  • und die Ergebnisse (z.B. in einem neuen Graphen) zurückgeben.

“durchkämmen” passiert mit der Funktion milk1.augment(abox) und heißt hier:

  • abox und Taxonomie in Graph g1 einlesen; (Anzahl Tripel: len_before)

  • auf Basis von g1 mit owlrl alles ableiten, was abgeleitet werden kann: es entsteht der deutlich umfangreichere Grapg g2 (Anzahl Tripel: len_after)

  • g1 und g2 z.B. mit SPARQL auf Unterschiede und Widerprüche analysieren.

# Implementierung: Vorläufig geben wir die A-Box als einen String im Format `ttl` an, 
# aber das kann man auch anders machen.
milk1.augment(abox)
augment() 264: len(self.g_augment_before)=10, len(self.g_augment)=141
546: self.id='999', edge={'<http://example.net/namespace/ex#cow_milk_102>', '<http://example.net/namespace/ex#horse_milk_104>'})

Die Graphen g1 und g2 lassen sich auch abrufen. (Am Ende dieser Seite geben wir Sie vollständig wieder – es ist instruktiv, sich genau anzuschauen, was durch owlrl alles abgeleitet wurde.)

# len(g1), len(g2)
g1 = milk1.g_augment_before 
g2 = milk1.g_augment
len(g1), len(g2)
(10, 141)

2024-08-29: Zunächst erhalten wir für jede einzelne Node Informationen über den Rand, Integritäts-Verletzungen etc. Interaktion mit dem Nutzer etc. dann tatsächlich pro Node, oder besser zusammengefasst.

In unserem minimalen Beispiel interessiert uns genau die gdn- (GenDifS-) Node mit der focus_id, hier 999. Sie hat ein Dictionary augment_set, in dem für verschiedene Augment-Kategorien wie z.B. "edge", "non_disjoint" und andere (Liste kann erweitert werden) die Augmentation-Ergebnisse vorgehalten werden.

Instanzen aus dem Rand (edge) des Knotens ISA DISJOINT (also mit der ID focus_id=999):

milk1.dict_of_all_gdn[focus_id].augment_set["edge"]
{'<http://example.net/namespace/ex#cow_milk_102>',
 '<http://example.net/namespace/ex#horse_milk_104>'}

Instanzen, die fehlerhaft sind, weil sie eine Integritätsbedingung verletzen: Da ein ISA-Knoten viele Subclasses haben kann, wird die DISJOINT-Verletzung paarweise geprüft. Für jedes Subclass-Paar wird eine Menge der Instanzen angelegt, die verbotenerweise in beiden Klassen enthalten sind.

milk1.dict_of_all_gdn[focus_id].augment_set["non_disjoint"]
{(':goat_milk',
  ':cow_milk'): {'<http://example.net/namespace/ex#goat_cow_milk_103>'}}

Q.E.D.

g1#

print(g1.serialize())
@prefix : <http://example.net/namespace/default#> .
@prefix ex: <http://example.net/namespace/ex#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

ex:cow_milk_102 a :milk .

ex:goat_cow_milk_103 a :cow_milk,
        :goat_milk .

ex:goat_milk_101 a :goat_milk .

ex:horse_milk_104 a :milk .

:cow_milk a owl:Class ;
    rdfs:subClassOf :milk .

:goat_milk a owl:Class ;
    rdfs:subClassOf :milk .

:milk a owl:Class .

g2#

print(g2.serialize())
@prefix : <http://example.net/namespace/default#> .
@prefix ex: <http://example.net/namespace/ex#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ex:cow_milk_102 a :milk,
        owl:Thing ;
    owl:sameAs ex:cow_milk_102 .

ex:goat_cow_milk_103 a :cow_milk,
        :goat_milk,
        :milk,
        owl:Thing ;
    owl:sameAs ex:goat_cow_milk_103 .

ex:goat_milk_101 a :goat_milk,
        :milk,
        owl:Thing ;
    owl:sameAs ex:goat_milk_101 .

ex:horse_milk_104 a :milk,
        owl:Thing ;
    owl:sameAs ex:horse_milk_104 .

rdf:HTML a rdfs:Datatype ;
    owl:sameAs rdf:HTML .

rdf:PlainLiteral a rdfs:Datatype ;
    owl:sameAs rdf:PlainLiteral .

rdf:XMLLiteral a rdfs:Datatype ;
    owl:sameAs rdf:XMLLiteral .

rdf:langString a rdfs:Datatype ;
    owl:sameAs rdf:langString .

rdf:type owl:sameAs rdf:type .

rdfs:Literal a rdfs:Datatype ;
    owl:sameAs rdfs:Literal .

rdfs:comment a owl:AnnotationProperty ;
    owl:sameAs rdfs:comment .

rdfs:isDefinedBy a owl:AnnotationProperty ;
    owl:sameAs rdfs:isDefinedBy .

rdfs:label a owl:AnnotationProperty ;
    owl:sameAs rdfs:label .

rdfs:seeAlso a owl:AnnotationProperty ;
    owl:sameAs rdfs:seeAlso .

rdfs:subClassOf owl:sameAs rdfs:subClassOf .

xsd:NCName a rdfs:Datatype ;
    owl:sameAs xsd:NCName .

xsd:NMTOKEN a rdfs:Datatype ;
    owl:sameAs xsd:NMTOKEN .

xsd:Name a rdfs:Datatype ;
    owl:sameAs xsd:Name .

xsd:anyURI a rdfs:Datatype ;
    owl:sameAs xsd:anyURI .

xsd:base64Binary a rdfs:Datatype ;
    owl:sameAs xsd:base64Binary .

xsd:boolean a rdfs:Datatype ;
    owl:sameAs xsd:boolean .

xsd:byte a rdfs:Datatype ;
    owl:sameAs xsd:byte .

xsd:date a rdfs:Datatype ;
    owl:sameAs xsd:date .

xsd:dateTime a rdfs:Datatype ;
    owl:sameAs xsd:dateTime .

xsd:dateTimeStamp a rdfs:Datatype ;
    owl:sameAs xsd:dateTimeStamp .

xsd:decimal a rdfs:Datatype ;
    owl:sameAs xsd:decimal .

xsd:double a rdfs:Datatype ;
    owl:sameAs xsd:double .

xsd:float a rdfs:Datatype ;
    owl:sameAs xsd:float .

xsd:hexBinary a rdfs:Datatype ;
    owl:sameAs xsd:hexBinary .

xsd:int a rdfs:Datatype ;
    owl:sameAs xsd:int .

xsd:integer a rdfs:Datatype ;
    owl:sameAs xsd:integer .

xsd:language a rdfs:Datatype ;
    owl:sameAs xsd:language .

xsd:long a rdfs:Datatype ;
    owl:sameAs xsd:long .

xsd:negativeInteger a rdfs:Datatype ;
    owl:sameAs xsd:negativeInteger .

xsd:nonNegativeInteger a rdfs:Datatype ;
    owl:sameAs xsd:nonNegativeInteger .

xsd:nonPositiveInteger a rdfs:Datatype ;
    owl:sameAs xsd:nonPositiveInteger .

xsd:normalizedString a rdfs:Datatype ;
    owl:sameAs xsd:normalizedString .

xsd:positiveInteger a rdfs:Datatype ;
    owl:sameAs xsd:positiveInteger .

xsd:short a rdfs:Datatype ;
    owl:sameAs xsd:short .

xsd:string a rdfs:Datatype ;
    owl:sameAs xsd:string .

xsd:time a rdfs:Datatype ;
    owl:sameAs xsd:time .

xsd:token a rdfs:Datatype ;
    owl:sameAs xsd:token .

xsd:unsignedByte a rdfs:Datatype ;
    owl:sameAs xsd:unsignedByte .

xsd:unsignedInt a rdfs:Datatype ;
    owl:sameAs xsd:unsignedInt .

xsd:unsignedLong a rdfs:Datatype ;
    owl:sameAs xsd:unsignedLong .

xsd:unsignedShort a rdfs:Datatype ;
    owl:sameAs xsd:unsignedShort .

owl:backwardCompatibleWith a owl:AnnotationProperty ;
    owl:sameAs owl:backwardCompatibleWith .

owl:deprecated a owl:AnnotationProperty ;
    owl:sameAs owl:deprecated .

owl:equivalentClass owl:sameAs owl:equivalentClass .

owl:incompatibleWith a owl:AnnotationProperty ;
    owl:sameAs owl:incompatibleWith .

owl:priorVersion a owl:AnnotationProperty ;
    owl:sameAs owl:priorVersion .

owl:sameAs owl:sameAs owl:sameAs .

owl:versionInfo a owl:AnnotationProperty ;
    owl:sameAs owl:versionInfo .

owl:Nothing a owl:Class ;
    rdfs:subClassOf :cow_milk,
        :goat_milk,
        :milk,
        owl:Nothing,
        owl:Thing ;
    owl:equivalentClass owl:Nothing ;
    owl:sameAs owl:Nothing .

:cow_milk a owl:Class ;
    rdfs:subClassOf :cow_milk,
        :milk,
        owl:Thing ;
    owl:equivalentClass :cow_milk ;
    owl:sameAs :cow_milk .

:goat_milk a owl:Class ;
    rdfs:subClassOf :goat_milk,
        :milk,
        owl:Thing ;
    owl:equivalentClass :goat_milk ;
    owl:sameAs :goat_milk .

owl:Class owl:sameAs owl:Class .

:milk a owl:Class ;
    rdfs:subClassOf :milk,
        owl:Thing ;
    owl:equivalentClass :milk ;
    owl:sameAs :milk .

owl:AnnotationProperty owl:sameAs owl:AnnotationProperty .

owl:Thing a owl:Class ;
    rdfs:subClassOf owl:Thing ;
    owl:equivalentClass owl:Thing ;
    owl:sameAs owl:Thing .

rdfs:Datatype owl:sameAs rdfs:Datatype .