Skip to content

feat(owlgen): give annotation properties their OWL role and no logical axioms - #59

Open
jdsika wants to merge 3 commits into
feat/declared-annotation-termsfrom
feat/owl-annotation-property-roles
Open

jdsika wants to merge 3 commits into
feat/declared-annotation-termsfrom
feat/owl-annotation-property-roles

Conversation

@jdsika

@jdsika jdsika commented Oct 21, 2026 •

Copy link
Copy Markdown

Mirror of linkml#4158, same head. Review there; this PR tracks the fork stack.

Proposed upstream description (2026-10-30), not yet applied to linkml#4158; it is copied there after review.

Merge order of this series: linkml#3716 → linkml#4168 → linkml#3993 → linkml#4144 → linkml#4171 → linkml#3989 → linkml#3990 → linkml#4156 → linkml#4157 → linkml#4158 → linkml#4028 → linkml#4169 → linkml#4170 → linkml#3446. A stack is rebased when its base lands.

Stacked on linkml#4157, which GitHub shows too; this PR's own diff is #59.

Read first: owlgen.py (170 lines of source); the snapshots only gain owl:AnnotationProperty declarations; the rest is tests, snapshots and docs.

gen-owl now gives annotation properties their OWL role and nothing else: a slot that implements: [owl:AnnotationProperty], or whose property IRI is one of the nine built-in annotation properties, is declared as an annotation property and produces no class restriction. Its domain, range and super-properties become annotation axioms; logical uses are errors.

Example

slots:
  note:
    slot_uri: dcterms:description
    implements: [owl:AnnotationProperty]
classes:
  Thing:
    slots: [note]
    attributes:
      name: {slot_uri: rdfs:label}
gen-owl --no-use-native-uris main this PR
dcterms:description a owl:AnnotationProperty and owl:allValuesFrom, owl:maxCardinality restrictions on Thing a owl:AnnotationProperty ; rdfs:range xsd:string, no restriction
rdfs:label a owl:DatatypeProperty and three restrictions nothing: a built-in annotation property

On main the declaration from linkml#2095 is emitted, but the restrictions make the ontology leave OWL 2 DL, and a slot mapped to rdfs:label redeclares a reserved IRI as a data property (linkml#3249).

Every OWL snapshot changes the same way, e.g. issue_163c.owl:

+skos:definition a owl:AnnotationProperty .
+skos:inScheme a owl:AnnotationProperty .
+skos:prefLabel a owl:AnnotationProperty .

The rule

  • A slot is an annotation property when it implements owl:AnnotationProperty or when its property IRI is rdfs:label, rdfs:comment, rdfs:seeAlso, rdfs:isDefinedBy, owl:deprecated, owl:versionInfo, owl:priorVersion, owl:backwardCompatibleWith or owl:incompatibleWith (OWL 2 §5.5, Table 5, the complete list).
  • No class restriction and no cardinality axiom on it (§10.1: annotations have no logical meaning; §8 and §9 admit only object and data property expressions in restrictions).
  • Domain, a datatype or class range, and super-properties as annotation axioms (§10.2); a node-type range such as nodeidentifier gives no range axiom. Built-in properties get no declaration and no axioms, since OWL 2 reserves them.
  • A slot that implements two roles is an error (§5.8.1), as is an inverse or a property characteristic, a unique key, a type designator, or a rule or class-expression condition on an annotation property.
  • A super-property (is_a or a mixin) must have the same role: a sub-property axiom relates two annotation properties (§10.2.1) or two logical properties (§9.2.1, §9.3.1).
  • Every other predicate the generator writes outside the reserved vocabulary, such as skos:definition and skos:inScheme for element metadata, is declared as an annotation property, since OWL 2 DL requires a declaration for each property (§5.8.1). A predicate a restriction uses keeps its logical role.
  • Other generators: gen-shacl and gen-json-schema keep constraining the slot's values in instance data. The role concerns OWL's logical meaning, not data validation.
  • The predicate of a declared annotation (feat(rdf): give declared annotation values and native metadata the term of their range linkml/linkml#4157) whose metaclass slot has the role is declared too, so dcterms:creator in the documented example is an annotation property with native URIs as well.
  • Slots without the role keep their object or data property projection, also when a metaclass declares them: LinkML puns the annotated element as an individual, as it does for all metadata (cmungall in Allow customization of mapping to property types when converting schemas to OWL linkml/linkml#2091).

Behaviour changes

  • Slots with implements: [owl:AnnotationProperty] lose their restrictions and gain annotation axioms. Slots emitted with a built-in annotation property IRI lose their owl:DatatypeProperty declaration and restrictions.
  • New errors for contradictory roles and logical uses of annotation properties.
  • OwlSchemaGenerator.is_annotation_property(slot) is public. No option; the role is already expressed by implements.
  • Every predicate gen-owl writes outside the reserved vocabulary and without a role is declared as an annotation property, so OWL snapshots gain those declarations (and nothing else).

Not in this PR

  • Validation of annotation values against the metaclass (the annotations guide: "not yet supported in the current LinkML validator").
  • An OWLAPI consumer check. The fork can run one as a separate job; upstream CI has no Java.
  • Role collisions between different slots that map to the same IRI, which keep their current output.

Testing

@jdsika
jdsika force-pushed the feat/declared-annotation-terms branch from 9020d54 to cb6a6f1 Compare October 21, 2026 20:54
@jdsika
jdsika force-pushed the feat/owl-annotation-property-roles branch from 29ce5c7 to 26630f9 Compare October 21, 2026 20:55
@jdsika
jdsika force-pushed the feat/declared-annotation-terms branch from cb6a6f1 to 6fe02bc Compare October 23, 2026 08:47
@jdsika
jdsika force-pushed the feat/owl-annotation-property-roles branch from 26630f9 to 4b55b0c Compare October 23, 2026 08:47
@jdsika
jdsika force-pushed the feat/declared-annotation-terms branch from 6fe02bc to 0fa8f65 Compare October 23, 2026 11:55
@jdsika
jdsika force-pushed the feat/owl-annotation-property-roles branch from 4b55b0c to 596bb1d Compare October 23, 2026 11:57
@jdsika
jdsika force-pushed the feat/owl-annotation-property-roles branch from 596bb1d to 63c711c Compare October 23, 2026 14:25
@jdsika
jdsika force-pushed the feat/declared-annotation-terms branch from 0fa8f65 to e56ed2d Compare October 30, 2026 07:27
@jdsika
jdsika force-pushed the feat/owl-annotation-property-roles branch from 63c711c to bcf311b Compare October 30, 2026 07:42
@jdsika
jdsika force-pushed the feat/declared-annotation-terms branch from e56ed2d to 763f4fd Compare November 4, 2026 08:56
…l axioms

A slot that implements owl:AnnotationProperty, or whose property IRI is one of
the built-in annotation properties of OWL 2 (Structural Specification §5.5), is
an annotation property: it is declared as such, produces no class restriction
or cardinality axiom (annotations have no logical meaning, §10.1), and its
domain, datatype or class range and super-properties are emitted as annotation
axioms (§10.2). Built-in properties get neither a declaration nor axioms. The
predicate of a declared annotation whose metaclass slot has the role is
declared too.

A slot that implements two roles is an error (§5.8.1), as is an inverse or a
property characteristic, a unique key, a type designator, or a rule or
class-expression condition on an annotation property.

Every other predicate gen-owl writes outside the reserved vocabulary,
such as skos:definition and skos:inScheme for element metadata, is
declared as an annotation property: OWL 2 DL requires a declaration for
each property (section 5.8.1), and without one OWLAPI found every
generated ontology outside the profile. A predicate a restriction uses
keeps its logical role. OWL snapshots gain only these declarations.

A super-property must have the role of its sub-property: a sub-property
axiom relates two annotation properties (§10.2.1) or two logical
properties (§9.2.1, §9.3.1), so an annotation property under a data or
object property, or the reverse, is an error.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
@jdsika
jdsika force-pushed the feat/owl-annotation-property-roles branch from bcf311b to 7fc0443 Compare November 4, 2026 12:02
…terms

feat(rdf): give declared annotation values and native metadata the term of their range
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants