People & Organizations -- Kind, SubKind, Phase, Role, Category, RoleMixin, PhaseMixin
This example is about usage scenarios 3 and 4 from the gUFO usage guide: instantiating and specializing gUFO's taxonomy of types. You will see how to say, of an OWL class you define, what kind of type it is -- whether it is rigid or anti-rigid, whether it supplies identity to its instances (a sortal) or merely groups several sortals together (a non-sortal) -- and why that distinction matters enough that gUFO ships a whole taxonomy, and gufoshapes ships rules to check it.
The ontology models people and organizations at a small scale: :Person and
:Organization, a few of the ways an instance of either can be temporarily or contingently
classified (:Child/:Adult, :Student, :Employee), and one
specialization that is permanent rather than contingent (:University, a kind of organization).
It also has two classes that intentionally do not specialize just :Person or just
:Organization, because the distinctions they capture -- being a customer, being currently
active -- cut across both.
Declaring :Person rdfs:subClassOf gufo:FunctionalComplex tells OWL that persons are
functional complexes, but it says nothing about what kind of class :Person itself is.
Two classes can look identical in OWL -- both are just owl:Class with some
rdfs:subClassOf axioms -- while meaning very different things conceptually. Compare:
:Person rdfs:subClassOf gufo:FunctionalComplex . # every person is necessarily a person
:Student rdfs:subClassOf :Person . # a person may stop being a student
Nothing in that OWL fragment distinguishes a class whose instances hold it necessarily from one whose instances hold it contingently. gUFO's taxonomy of types exists to make that distinction explicit, by having the modeler additionally state, using OWL 2 punning, that the class is itself an instance of one of gUFO's type metaclasses:
:Person rdf:type owl:Class ;
rdfs:subClassOf gufo:FunctionalComplex ;
rdf:type gufo:Kind .
:Student rdf:type owl:Class ;
rdfs:subClassOf :Person ;
rdf:type gufo:Role .
Punning is what makes :Person both a class (of persons) and an individual (an instance of
gufo:Kind) at once. This is legal OWL 2 DL not because of anything specific to gUFO, but because
OWL 2's semantics always interpret a name's "class facet" and its "individual facet" separately, so the two
uses never contradict each other -- any OWL 2 DL ontology gets that for free. What gUFO contributes is not
this legality but the vocabulary: a whole second taxonomy of metaclasses (gufo:Kind,
gufo:Role, gufo:Phase, ...) for the individual facet of a punned class like
:Person to instantiate meaningfully.
Every gUFO endurant type sits somewhere on two independent axes:
| Sortal (supplies identity) | Non-sortal (does not) | |
|---|---|---|
| Rigid (necessary for all instances) | gufo:Kind, gufo:SubKind | gufo:Category |
| Anti-rigid (contingent for all instances) | gufo:Phase, gufo:Role | gufo:PhaseMixin, gufo:RoleMixin |
| Semi-rigid (necessary for some, contingent for others) | (gUFO defines no such sortal) | gufo:Mixin |
Sortal vs. non-sortal is about identity: a sortal type (like :Person)
provides the principle by which we count and re-identify its instances over time -- "this is the same person
as before". A non-sortal (like :Agent below) groups instances that already get their
identity from some sortal further down the hierarchy; :Agent itself answers no "is this the same
agent?" question that isn't really "is this the same person/organization?".
Rigid, anti-rigid and semi-rigid are about modality: a rigid type applies to its
instances in every possible world in which they exist at all (a person cannot stop being a person without ceasing to
exist). An anti-rigid type applies contingently (a person can stop being a student, or a child,
while remaining the very same person). A semi-rigid type is neither uniformly: it applies necessarily
to some of its instances and contingently to others. gUFO's own example is "FemaleAnimal"
(gufo:Mixin): necessary for a lion or a shark, but contingent for a clownfish or a mushroom
coral, which can change sex. Note that gufo:Mixin is non-sortal only, not
anti-rigid -- gUFO defines no semi-rigid sortal, since a sortal's identity principle is expected to apply
uniformly to its instances. This example has no natural semi-rigid grouping in its domain, so
gufo:Mixin is not instantiated below; see the table above for where it would fit if it were.
gufo:Category:Agent captures what persons and organizations have in common, without providing identity for
either. It is rigid (nothing stops being an agent while it exists) but non-sortal, so it is declared a
gufo:Category:
:Agent a owl:Class , gufo:Category ;
rdfs:subClassOf gufo:FunctionalComplex .
gufo:Kind:Person and :Organization each supply their own principle of identity, and each
applies necessarily to its instances, so both are kinds. In UFO, an individual can only ever instantiate one
kind -- a kind is precisely what supplies its unique principle of identity -- so any two distinct kinds are,
in principle, always disjoint. OWL has no way of enforcing this by itself, though: nothing about being
declared a gufo:Kind makes two classes disjoint automatically. Declaring
owl:disjointWith explicitly is what actually makes that hold in this ontology, and it is good
practice to do so for any two kinds, regardless of what supertype (if any) they share:
:Person a owl:Class , gufo:Kind ;
rdfs:subClassOf :Agent .
:Organization a owl:Class , gufo:Kind ;
rdfs:subClassOf :Agent ;
owl:disjointWith :Person .
gufo:SubKind:University is still rigid -- an organization does not stop being a university while it
exists -- but it does not introduce a new principle of identity: a university is individuated exactly
as any organization is. That combination (rigid, sortal, but not identity-providing) is
gufo:SubKind:
:University a owl:Class , gufo:SubKind ;
rdfs:subClassOf :Organization .
gufo:Phase and gufo:Role:Child/:Adult and :Student/:Employee are all anti-rigid
specializations of :Person: a given person moves in and out of them without changing who they
are. gUFO further distinguishes why the type is contingent:
gufo:Phase applies in virtue of an intrinsic condition of the instance
itself (age, for :Child/:Adult). This example declares :Child and
:Adult disjoint, but that is only sound because this ontology represents a single snapshot of
the domain, where each person is recorded with just their currently-applicable phase. rdf:type
is not temporalized -- a plain :Bob a :Child triple carries no date -- so if the knowledge graph
instead needed to accumulate history (Bob was a child, and is now an adult) without retracting old triples,
the two plain rdf:type facts would sit in the graph together and the disjointness would make
them inconsistent. gUFO's mechanism for that case is not phase disjointness but
gufo:TemporaryInstantiationSituation (see example 6),
which attaches a begin and end point to each instantiation of an anti-rigid type instead of relying on
rdf:type alone.gufo:Role applies in virtue of a relational condition -- something the
instance participates in (enrollment, for :Student; employment, for :Employee, see
example 4). Roles are not generally disjoint: the same person can be
both a student and an employee.:Child a owl:Class , gufo:Phase ;
rdfs:subClassOf :Person .
:Adult a owl:Class , gufo:Phase ;
rdfs:subClassOf :Person ;
owl:disjointWith :Child .
:Student a owl:Class , gufo:Role ;
rdfs:subClassOf :Person .
:Employee a owl:Class , gufo:Role ;
rdfs:subClassOf :Person .
gufo:RoleMixin and gufo:PhaseMixin"Being a customer" and "being currently active" are contingent, just like a role or a phase -- but they
are not specific to persons or to organizations: both can be customers, and both can be
active or inactive. A class like this must specialize the non-sortal :Agent, not either kind,
and its metatype records that it is anti-rigid and relationally- or intrinsically-grounded, just like Role
and Phase, but without supplying identity:
:Customer a owl:Class , gufo:RoleMixin ;
rdfs:subClassOf :Agent .
:ActiveAgent a owl:Class , gufo:PhaseMixin ;
rdfs:subClassOf :Agent .
:InactiveAgent a owl:Class , gufo:PhaseMixin ;
rdfs:subClassOf :Agent ;
owl:disjointWith :ActiveAgent .
This is exactly the situation gUFO's non-sortals are for: had :Customer been forced to
specialize :Person alone, :GlobexCorp (an organization) could never be modeled as a
customer without either duplicating the role under :Organization or, worse, misclassifying
:GlobexCorp as a person.
With the type-level declarations in place, ordinary individuals just instantiate these classes as usual (usage scenario 1 from the guide):
:Alice a :Person , :Student , :Adult , :Customer , :ActiveAgent .
:GlobexCorp a :Organization , :Customer , :ActiveAgent .
:OldTownPrinters a :Organization , :InactiveAgent .
Notice :GlobexCorp and :Alice share the :Customer and
:ActiveAgent classifications despite being different kinds of thing -- precisely what the
non-sortals were introduced to allow.
See ontology.ttl for the complete file, including all
individuals (:Bob, :Carol, :AcmeUniversity).
Suppose a modeler wants a class for managers and, reaching for the class that "sounds most important", declares:
:Manager a owl:Class , gufo:Kind ;
rdfs:subClassOf :Employee . # :Employee is a gufo:Role
This is syntactically fine OWL, and -- because gufo:Kind and gufo:Role are just
punned individuals, not classes that OWL's open-world semantics can reason about as mutually exclusive in
this configuration -- a DL reasoner raises no error. But it is nonsense in UFO: a
gufo:Kind must be rigid and must supply identity on its own, yet here it specializes
:Employee, which is both anti-rigid and already sortal. Managers would, absurdly, never stop
being managers even after leaving the company, while simultaneously getting their identity from a role that
is itself contingent.
What was probably meant is :Manager a gufo:Role (a kind of employee, contingent on managing
something) or gufo:SubKind (if "manager" is meant to be permanent once attained). The full
mistake is kept in invalid-example.ttl, and the gUFO SHACL
shapes reject it on two counts at once:
gufo:Kind cannot specialize another sortal.examples/tests/test_examples.py runs two checks against ontology.ttl, and two
against invalid-example.ttl:
gufo.ttl +
ontology.ttl is consistent and every class is satisfiable. Run alone against
invalid-example.ttl, this check passes, which is the point of the pitfall above: OWL
punning hides the mistake from the reasoner.examples/common/gufoshapes.ttl) -- ontology.ttl conforms;
invalid-example.ttl does not, with the two violations quoted above.From examples/tests/, after installing requirements.txt into a virtualenv:
.venv/bin/pytest -v -k endurant-types