{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Terminological knowledge\n", "\n", "
\n", "\n", "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/simphony/docs/v4.0.0?filepath=docs%2Fusage%2Fterminological_knowledge.ipynb \"Click to run this tutorial yourself!\")\n", " \n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In an ontological framework, ontology entities are used as a knowledge representation form. Those can be further categorized in two groups: ontology individuals ([assertional knowledge](https://en.wikipedia.org/wiki/Abox)), and ontology classes, relationships, attributes and annotations ([terminological knowledge](https://en.wikipedia.org/wiki/Tbox)). This page **focuses on** how to access and navigate the **terminological knowledge** of an ontology using SimPhoNy." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Such functionality is presented in the form of a tutorial, in which both the `city` namespace from SimPhoNy's example [City ontology](ontologies/ontologies_included.md#the-city-ontology), and the `emmo` namespace from the [Elementary Multiperspective Material Ontology (EMMO)](ontologies/ontologies_included.md#elementary-multiperspective-material-ontology-emmo) are used as examples.\n", "\n", "If you want to follow the tutorial along, please [make sure that such ontologies are installed](ontologies/pico.md#pico-list). If you have not installed them yet, you can do so running the commands below." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "tags": [] }, "outputs": [], "source": [ "# Install the ontologies\n", "!pico install city emmo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "
Note
\n", " \n", "SimPhoNy does **not** feature the ability to edit the classes, relationships, attributes and\n", "annotations of ontologies, only to read them. This is the reason why ontologies need to be [installed using pico](ontologies/pico.md#installing-ontologies-pico).\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [Namespace objects](../api_reference.md#simphony_osp.ontology.OntologyNamespace): accessing entities\n", "\n", "To access ontology entities, it is first needed to know the aliases of the installed ontology namespaces. The [pico ontology management tool](ontologies/pico.md#installing-ontologies-pico) to can list the installed namespaces. Note that each namespace is provided by a specific [ontology package](ontologies/packages.md#ontology-packages).\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Packages:\n", "\t- simlammps\n", "\t- city\n", "\t- emmo\n", "Namespaces:\n", "\t- simphony\n", "\t- owl\n", "\t- rdfs\n", "\t- simlammps\n", "\t- city\n", "\t- emmo\n" ] } ], "source": [ "!pico list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once the names of the namespaces to be used are known, they can be imported in Python. In this tutorial, the namespaces `city` and `emmo` are imported. Through those imported namespace Python objects, the entities within the namespaces can be accessed:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "tags": [] }, "outputs": [], "source": [ "from simphony_osp.namespaces import city, emmo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The namespace objects are [Python iterables](https://docs.python.org/3/glossary.html). This implies that it is possible to get a list of all the entities available within a namespace running `list(namespace)`. \n", "\n", "To get the IRI of a namespace object, use the `iri` property. This will yield an [URIRef object](https://rdflib.readthedocs.io/en/stable/rdf_terms.html#uriref) from the [RDFLib](https://github.com/RDFLib/rdflib) library, that SimPhoNy makes use of." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "rdflib.term.URIRef('https://www.simphony-project.eu/city#')" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "city.iri" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are several ways to reference an ontology entity to be retrieved, which are summarized in the following list.\n", "\n", "- By **suffix**. For example, for the namespace `city`, whose [IRI](https://fusion.cs.uni-jena.de/fusion/blog/2016/11/18/iri-uri-url-urn-and-their-differences/) is `http://www.simphony-osp.eu/city#`, requesting the suffix `Citizen` would return the ontology entity with IRI `http://www.simphony-osp.eu/city#Citizen`.\n", "\n", "- By **label**. Retrieves the entity by the [_main label_](#Accessing-an-entity’s-label) that has been assigned to it in the ontology using either the `rdfs:label` or `skos:prefLabel` predicates.\n", "\n", "[//]: # \"TODO: It is possible to include more properties or change the order of the properties, link to the sessions page. Link also to give more information about the ordering of the properties and which labels are discarded.\"\n", "\n", "- By **IRI**. The [IRI](https://fusion.cs.uni-jena.de/fusion/blog/2016/11/18/iri-uri-url-urn-and-their-differences/) of an ontology entity is provided in order to retrieve it." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Although it is not the only possible approach, the **most convenient way** to access an ontology entity is to use the **dot notation** on the imported namespace objects (e.g. `city.Citizen`) to reference it either by suffix or label." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "
Tip
\n", " \n", "The dot notation supports IPython autocompletion. For example, when working on a Jupyter notebook, once the namespace has been imported, it is possible to get suggestions for the entity names by writing `namespace.` and pressing TAB.\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Accessing an ontology entity by suffix or label**\n", "\n", "Let's retrieve the _Living Being_ class from the `city` namespace, whose IRI is `https://www.simphony-project.eu/city#LivingBeing`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To retrieve the class using its suffix or its label, just use the dot notation" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "city.LivingBeing # by suffix" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, sometimes the suffix or label contains characters that Python does not accept as attribute names, such as dashes or spaces. In such cases, it is not possible to use the dot notation. An alternative way to retrieve entitites using the Python's index operator `[]` exists" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "city['Living Being'] # by label (contains a space)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "
Tip
\n", " \n", "The index notation also supports IPython autocompletion.\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that both operations are case-sensitive, and therefore the following would produce an error." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# city.Livingbeing # -> Fails." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition, the namespace object has some advanced methods, [from_suffix](../api_reference.md#simphony_osp.ontology.OntologyNamespace.from_suffix), [from_label](../api_reference.md#simphony_osp.ontology.OntologyNamespace.from_label) and [get](../api_reference.md#simphony_osp.ontology.OntologyNamespace.get) that can also be used to retrieve entities by suffix or label." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Accessing an ontology entity by IRI**\n", "\n", "Let's now retrieve the Integer class from the `emmo` namespace using its IRI `http://emmo.info/emmo#EMMO_f8bd64d5_5d3e_4ad4_a46e_c30714fecb7f`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To do so, use the method [from_iri](../api_reference.md#simphony_osp.ontology.OntologyNamespace.from_iri) of the corresponding namespace object" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emmo.from_iri('http://emmo.info/emmo#EMMO_f8bd64d5_5d3e_4ad4_a46e_c30714fecb7f')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [Ontology entity objects](../api_reference.md#simphony_osp.ontology.OntologyEntity)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Accessing an entity's label" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Labels are tipically assigned to ontology entities through the `rdfs:label` or `skos:prefLabel` predicates. In particular, it is possible for a single entity to have several labels.\n", "\n", "In SimPhoNy, all such labels can be seen, but one label is considered to be the _main_ label. When SimPhoNy has to select exclusively one label among all the available ones, it will first retrieve all the available labels for an entity an then sort them based on the following criteria:\n", "\n", "- The predicate used to assign the label. Both `rdfs:label` and `skos:prefLabel` are considered, but labels assigned using `rdfs:label` are preferred to labels assigned to `skos:prefLabel`.\n", "\n", "- The language of the label. English labels are preferred to labels with no language assigned to them, and labels with no language to labels in any other language.\n", "\n", "So for example, assume that the ontology class " ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "city.City" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "has as labels (which is actually not the case): _Municipality (language: \"en\", predicate: `skos:prefLabel`)_, _City (language: None, predicate: `rdfs:label`)_, _Città (language: 'it', predicate: `rdfs:label`)_. Then the second one, _City_, would be the main label." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The main label of an ontology entity can be accessed using the _label_ attribute." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'City'" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "city.City.label" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is also possible to access the language of the main label," ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'en'" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "city.City.label_lang" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and even the main label in the form of an [RDFLib Literal](https://rdflib.readthedocs.io/en/stable/rdf_terms.html#literals), which contains both the label itself and its language information." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "rdflib.term.Literal('City', lang='en')" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "city.City.label_literal" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To retrieve all labels as [RDFLib Literals](https://rdflib.readthedocs.io/en/stable/rdf_terms.html#literals), use the [iter_labels](../api_reference.md#simphony_osp.ontology.OntologyEntity.iter_labels) method." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[rdflib.term.Literal('City', lang='en')]" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(city.City.iter_labels())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, actually the City class has only one label!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Accessing an entity's identifier and namespace" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The identifier (IRI or blank node identifier) of an entity may be accessed using the [identifier](../api_reference.md#simphony_osp.ontology.OntologyEntity.identifier) property." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "rdflib.term.URIRef('http://emmo.info/emmo#EMMO_18d180e4_5e3e_42f7_820c_e08951223486')" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emmo.Real.identifier" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition, it is possible to get the namespace object to which the entity belongs using the [namespace](../api_reference.md#simphony_osp.ontology.OntologyEntity.namespace) property." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emmo.Equation.namespace" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Accessing super- and subclasses\n", "\n", "Using the properties [superclasses](../api_reference.md#simphony_osp.ontology.OntologyEntity.superclasses) and [subclasses](../api_reference.md#simphony_osp.ontology.OntologyEntity.subclasses) it is easy to navigate the ontology. [Direct superclasses](../api_reference.md#simphony_osp.ontology.OntologyEntity.direct_superclasses) and [subclasses](../api_reference.md#simphony_osp.ontology.OntologyEntity.direct_subclasses) can also be accessed:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Superclasses of \"Living Being\": frozenset({, })\n", "\n", "Subclasses of \"Living Being\": frozenset({, , })\n", "\n", "Direct superclasses of \"Living Being\": frozenset()\n", "\n", "Direct subclasses of \"Living Being\": frozenset({})\n", "\n", "Is \"Person\" a subclass of \"Living Being\"? True\n", "Is \"Living Being\" a superclass of \"Person\"? True\n" ] } ], "source": [ "from IPython.display import display\n", "\n", "print(f\"Superclasses of \\\"Living Being\\\": {city.LivingBeing.superclasses}\", end=\"\\n\"*2)\n", "print(f\"Subclasses of \\\"Living Being\\\": {city.LivingBeing.subclasses}\", end=\"\\n\"*2)\n", "\n", "print(f\"Direct superclasses of \\\"Living Being\\\": {city.LivingBeing.direct_superclasses}\", end=\"\\n\"*2)\n", "print(f\"Direct subclasses of \\\"Living Being\\\": {city.LivingBeing.direct_subclasses}\", end=\"\\n\"*2)\n", "\n", "print(\"Is \\\"Person\\\" a subclass of \\\"Living Being\\\"?\", city.Person.is_subclass_of(city.LivingBeing))\n", "print(\"Is \\\"Living Being\\\" a superclass of \\\"Person\\\"?\", city.LivingBeing.is_superclass_of(city.Person))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Checking the type of entity\n", "\n", "In the terminological knowledge side of the ontology, four types of entities can be defined: [classes](#Operations-specific-to-ontology-class-objects), [relationships](#Operations-specific-to-ontology-relationship-objects), [attributes](#Operations-specific-to-ontology-attribute-objects) and [annotations](#Operations-specific-to-ontology-annotation-objects). There are different Python objects for the different entity types. Both can be used to check to which type an entity belongs:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Is the entity is a class?\n", "True\n", "True\n", "True\n", "\n", "Is the entity is a relationship?\n", "True\n", "True\n", "\n", "Is the entity an attribute?\n", "True\n", "True\n", "\n", "Is the entity an annotation?\n", "True\n" ] } ], "source": [ "# These are the Python classes for the different types of ontology entities.\n", "# They all inherit from `simphony_osp.ontology.OntologyEntity`.\n", "from simphony_osp.namespaces import owl, rdfs\n", "from simphony_osp.ontology import (\n", " OntologyAnnotation,\n", " OntologyAttribute,\n", " OntologyClass,\n", " OntologyRelationship,\n", ")\n", "\n", "print(\"\\nIs the entity is a class?\")\n", "print(isinstance(city.LivingBeing, OntologyClass))\n", "print(city.LivingBeing.is_subclass_of(owl.Thing))\n", "print(not city.LivingBeing.is_subclass_of(owl.topObjectProperty)\n", " and not city.LivingBeing.is_subclass_of(owl.topDataProperty))\n", "\n", "print(\"\\nIs the entity is a relationship?\")\n", "print(isinstance(city.hasInhabitant, OntologyRelationship))\n", "print(city.hasInhabitant.is_subclass_of(owl.topObjectProperty))\n", "\n", "print(\"\\nIs the entity an attribute?\")\n", "print(isinstance(city['name'], OntologyAttribute))\n", "print(city['name'].is_subclass_of(owl.topDataProperty))\n", "\n", "print(\"\\nIs the entity an annotation?\")\n", "print(isinstance(rdfs.label, OntologyAnnotation))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All such objects **inherit from the ontology entity object**, and thus, share its common functionality, but each differs in the extra operations they offer." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Ontology class objects](../api_reference.md#simphony_osp.ontology.OntologyClass)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One of the extra functionalities offered by ontology class objects is the access to their attributes. The attributes of an ontology class are those that all instances of a class are expected to share. For example, in OWL ontologies they are defined using the `owl:hasValue`, `owl:someValuesFrom`, `owl:cardinality` or `owl:minCardinality` predicates." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "mappingproxy({: None,\n", " : None})" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "city.Citizen.attributes # returns a dictionary whose keys are attributes and whose values are their default values (if defined)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition, SimPhoNy has special support for the `owl:Restriction` and `owl:Composition` classes of the [Web Ontology Language (OWL)](https://en.wikipedia.org/wiki/Web_Ontology_Language) (check the [OWL ontology specification](https://www.w3.org/TR/owl2-syntax/) for more details). Such OWL classes are represented by the Python classes `Restriction` and `Composition`. See [operations specific to ontology axioms](#Operations-specific-to-ontology-axioms) for more information.\n", "\n", "For example, in the city ontology, the citizens have a restriction on the name and age attributes: a citizen must have exactly one name and one age. These axioms can be accessed using the `axioms` property, which returns both the restriction and compositions affecting the class." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('is child of QUANTIFIER.MAX 2',\n", " 'name QUANTIFIER.EXACTLY 1',\n", " 'works in QUANTIFIER.MAX 1',\n", " 'age QUANTIFIER.EXACTLY 1')" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tuple(str(x) for x in city.Citizen.axioms)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Ontology axioms" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For [restrictions](../api_reference.md#simphony_osp.ontology.Restriction), the quantifier, the target, the restriction type and the relationship/attribute (depending on whether it is a restriction of the relationship type or attribute type) may be accessed." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "is child of QUANTIFIER.MAX 2\n", "QUANTIFIER.MAX\n", "2\n", "RTYPE.RELATIONSHIP_RESTRICTION\n", "is child of\n" ] } ], "source": [ "from simphony_osp.ontology import RESTRICTION_QUANTIFIER, RESTRICTION_TYPE\n", "\n", "restriction = next(iter(city.Citizen.axioms))\n", "print(restriction)\n", "print(restriction.quantifier)\n", "print(restriction.target)\n", "print(restriction.rtype)\n", "print(\n", " restriction.attribute\n", " if restriction.rtype == RESTRICTION_TYPE.ATTRIBUTE_RESTRICTION else\n", " restriction.relationship\n", ") " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For [compositions](../api_reference.md#simphony_osp.ontology.Composition), both the operator and operands can be accesed." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(SymbolicConstruct OPERATOR.OR Symbol)\n", "OPERATOR.OR\n", "(, )\n" ] } ], "source": [ "from simphony_osp.ontology import COMPOSITION_OPERATOR, Composition\n", "\n", "composition = tuple(x for x in emmo.Integer.axioms if isinstance(x, Composition))[0]\n", "print(composition)\n", "print(composition.operator)\n", "print(composition.operands)" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "### [Ontology relationship objects](../api_reference.md#simphony_osp.ontology.OntologyRelationship)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can access the inverse of relationships." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "You can get the inverse of a relationship\n", "None\n" ] } ], "source": [ "print(\"\\nYou can get the inverse of a relationship\")\n", "print(city.hasInhabitant.inverse)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Ontology attribute objects](../api_reference.md#simphony_osp.ontology.OntologyAttribute)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The datatype of attributes can be accessed." ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "rdflib.term.URIRef('http://www.w3.org/2001/XMLSchema#integer')" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "city.age.datatype" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Ontology annotation objects](../api_reference.md#simphony_osp.ontology.OntologyAnnotation)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is **no specific extra functionality** offered by ontology annotation objects." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.12" } }, "nbformat": 4, "nbformat_minor": 4 }