Skip to content

OWL

The owl generator writes an OWL 2 ontology for a LinkML schema. The OWL importer does the reverse, and the two should preserve most axioms of an OWL ontology.

OWL can express things LinkML cannot (property chains, defined classes, individuals), and LinkML can express things OWL cannot (tree_root, inlined, identifiers). Those are lost on the way.

The generator aims for OWL 2 DL. SAREF, SOSA, gist, PROV-O and D3FEND, after a trip through LinkML, pass ROBOT’s DL profile check (robot validate-profile --profile DL, run by hand).

Terminal window
ohi get w3c/prov-o
ohi linkml to owl --only-root-schema \
--to prov-o.owl.ttl prov-o.linkml.yaml
Try it in the playground, with PROV-O
formatRdfFormatdefault ttl
CLI --format

The RDF serialization of the output.

  • ttl Turtle, with prefixes and pretty-printed.
  • nt N-Triples, one statement per line.
metadata_profileOwlMetadataProfiledefault rdfs
CLI --metadata-profileScala metadataProfile

The annotation property for descriptions.

  • rdfs rdfs:comment, shown by most OWL tools.
  • linkml skos:definition, as in the LinkML metamodel and gen-owl.
only_root_schemabooleandefault false
CLI --only-root-schemaScala onlyRootSchema

Whether to describe only the root schema, with an owl:imports for each of its imports. Without it, the output includes the imported schemas, like gen-owl --mergeimports.

permissible_valuesOwlPermissibleValueKinddefault individual
CLI --permissible-valuesScala permissibleValues

The kind of OWL entity for the permissible values of an enum.

  • individual Named individuals of the enum's class.
  • class Subclasses of the enum's class, as in gen-owl.
LinkML OWL
schema owl:Ontology with the schema id as its IRI
prefixes, default_prefix sh:declare on the ontology, and vann:preferredNamespacePrefix / vann:preferredNamespaceUri
class owl:Class
is_a, mixins rdfs:subClassOf the parents
a slot of the class (in slots, attributes, slot_usage) restrictions on the class: owl:allValuesFrom the range, cardinalities, owl:hasValue, owl:someValuesFrom
required, minimum_cardinality, maximum_cardinality owl:minCardinality, owl:maxCardinality, or owl:cardinality when the two are equal. On a transitive slot, its parents and inverses, which OWL 2 DL allows no counts on, required is owl:someValuesFrom owl:Thing and the counts are skipped
a single-valued slot owl:maxCardinality 1, and owl:FunctionalProperty when it is single-valued everywhere it is used. A langstring slot gets owl:maxQualifiedCardinality 1 on xsd:string instead, as a text in several languages is one literal per language
equals_string, equals_number owl:hasValue
has_member, and more has_member under all_of owl:someValuesFrom, one each
abstract with two or more is_a children rdfs:subClassOf the union of the children
disjoint_with, children_are_mutually_disjoint owl:disjointWith, owl:AllDisjointClasses
unique_keys owl:hasKey
exact_mappings to a class or slot of the same schema owl:equivalentClass, owl:equivalentProperty
slot owl:ObjectProperty, or owl:DatatypeProperty when every use of it has a type as its range
implements: [owl:DatatypeProperty] and friends the property kind asked for, as in gen-owl
range, any_of rdfs:range, a union for an any_of with two or more members
domain, domain_of rdfs:domain, a union for domain_of
is_a, mixins on a slot rdfs:subPropertyOf
inverse owl:inverseOf
transitive, symmetric and the other characteristics owl:TransitiveProperty and so on
enum owl:Class equivalent to owl:oneOf its permissible values
permissible value owl:NamedIndividual with the meaning as its IRI, and skos:notation for its text when that is not the IRI’s local name
type its uri; with constraints, a datatype restriction on it (xsd:integer[>= 0])
pattern, minimum_value, maximum_value, minimum_length, maximum_length xsd:pattern, xsd:minInclusive, xsd:maxInclusive, xsd:minLength, xsd:maxLength. An XSD pattern matches the whole value, so ^abc$ is written abc, and abc is written .*abc.*
title rdfs:label, tagged with in_language when the schema has one
description rdfs:comment (or skos:definition with --metadata-profile linkml)
other documentation (aliases, comments, see_also, mappings, …) the property the LinkML metamodel gives it, such as skos:altLabel, skos:note, rdfs:seeAlso
annotations annotations with the tag as the property. A value is an IRI if it is a full IRI, with :// or a scheme such as urn, mailto, did, info or tag, else text, even if it looks like a CURIE
deprecated owl:deprecated true

The LinkML-to-OWL generator declares every annotation property that is used, which OWL 2 DL requires. The same goes for every datatype that is not in OWL 2’s datatype map, such as xsd:date. RDF’s, RDFS’s, OWL’s and XSD’s own terms are never declared.

These differences are in place mostly to make the OWL describe the same RDF data as the RDFS and SHACL generators, which we think is a more consistent approach.

  • IRIs are the ones the data uses: class_uri, slot_uri, enum_uri and the permissible value meaning, as in this project’s RDF, RDFS and SHACL. gen-owl uses the schema’s own namespace by default (--use-native-uris).
  • Identifier slots are not properties. In RDF, an identifier is the IRI of the node.
  • Slots with a uri or uriorcurie range, or with an implicit_prefix, are object properties without a range, since their values are IRIs in RDF, as in the SHACL generator.
  • Permissible values are individuals of a class defined by owl:oneOf. gen-owl makes them classes by default, which is still possible in ohi LinkML with the --permissible-values class option.
  • Descriptions are rdfs:comment by default, rather than the metamodel’s skos:definition. If you select the --metadata-profile linkml option, descriptions will be written as skos:definition, as gen-owl does by default.
  • owl:allValuesFrom restrictions that repeat a property’s range are kept for the slots a class lists itself. They record which classes use which slots, which the importer needs. A class that only refines an inherited slot in slot_usage gets one only when it narrows the range.
  • type_designator rules, rules and classification_rules are not written.

Improvements over Python gen-owl:

  • pattern is translated to XSD’s form, which matches the whole value: gen-owl writes it as it is, so ^…$ patterns match nothing.
  • An abstract class with only one subclass gets no covering axiom. Consider class B, which is a subclass of an abstract class A. gen-owl in this case writes “every A is a B”, and since every B is already an A, a reasoner treats the two classes as the same.
  • A single-valued slot is also marked as an owl:FunctionalProperty when all classes that use this property treat it as single-valued.
  • An attribute’s property gets an rdfs:range when every use of it has the same range.
  • The announced new defaults are used: no owl:minCardinality 0, which says nothing, and one owl:cardinality where the lower and upper bounds are equal.
  • A license that is a link is written as an IRI.