ProtegeOWL API Advanced Class Definitions

From Protege Wiki
Jump to: navigation, search

Main article: ProtegeOWL_API_Programmers_Guide



Advanced Class Definitions (OWL DL)

Restrictions

The Protege-OWL API distinguishes between named classes (RDFSNamedClass and its subinterface OWLNamedClass) and anonymous classes (OWLAnonymousClass and its subinterfaces). Anonymous classes can be used to logically describe named classes. This means that the life cycle of any anonymous class completely depends on the named class, i.e., if the named class is deleted, then the depending anonymous classes will also be deleted. Anonymous classes can in some places also be used to describe properties, in particular in domain and range statements, and in these cases as well they depend on the host properties.

Here is an overview of the anonymous class interfaces in the Protege-OWL API:


RDFSClass
  OWLClass
    OWLAnonymousClass
      OWLRestriction
        OWLQuantifierRestriction
          OWLAllValuesFrom
          OWLSomeValuesFrom
        OWLHasValue
        OWLCardinalityBase
          OWLCardinality
          OWLMinCardinality
          OWLMaxCardinality
  OWLEnumeratedClass
  OWLLogicalClass
    OWLComplementClass
    OWLNAryLogicalClass
      OWLIntersectionClass
      OWLUnionClass


The subinterfaces of OWLRestriction are used to define restrictions on property values. For example, an OWLMinCardinality restriction on a property at a class indicates that for instances of the class, a minimum number of values is required. The following code segment creates such a restriction on the property hasChildren at the class Parent:


OWLNamedClass personClass = owlModel.createOWLNamedClass("Person");
OWLObjectProperty hasChildrenProperty = owlModel.createOWLObjectProperty("hasChildren");
hasChildrenProperty.setDomain(personClass);

OWLNamedClass parentClass = owlModel.createOWLNamedSubclass("Parent", personClass);
OWLMinCardinality minCardinality = owlModel.createOWLMinCardinality(hasChildrenProperty, 1);
parentClass.addSuperclass(minCardinality);


The above code results in the following OWL model:


<owl:Class rdf:ID="Person"/>
<owl:Class rdf:ID="Parent">
  <rdfs:subClassOf>
    <owl:Restriction>
      <owl:onProperty>
        <owl:ObjectProperty rdf:ID="hasChildren"/>
      </owl:onProperty>
      <owl:minCardinality
          rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1
      </owl:minCardinality>
    </owl:Restriction>
  </rdfs:subClassOf>
  <rdfs:subClassOf rdf:resource="#Person"/>
</owl:Class>
<owl:ObjectProperty rdf:about="#hasChildren">
  <rdfs:domain rdf:resource="#Person"/>
</owl:ObjectProperty>


The class Parent now has two superclasses; the named class Person and the anonymous restriction. Subsequent calls to RDFSClass.getSuperclasses() will return both of them, while RDFSClass.getNamedSuperclasses() will only return Person.

Now consider the following, more complicated scenario. The named class Superclass has a subclass Subclass and both have an owl:allValuesFrom restriction on a property:


<owl:Class rdf:ID="Superclass">
  <rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
  <rdfs:subClassOf>
    <owl:Restriction>
      <owl:allValuesFrom>
        <owl:Class rdf:ID="Subclass"/>
      </owl:allValuesFrom>
      <owl:onProperty>
        <owl:ObjectProperty rdf:ID="property"/>
      </owl:onProperty>
    </owl:Restriction>
  </rdfs:subClassOf>
</owl:Class>
<owl:Class rdf:about="#Subclass">
  <rdfs:subClassOf>
    <owl:Restriction>
      <owl:allValuesFrom rdf:resource="#Subclass"/>
      <owl:onProperty>
        <owl:ObjectProperty rdf:about="#property"/>
      </owl:onProperty>
    </owl:Restriction>
  </rdfs:subClassOf>
  <rdfs:subClassOf rdf:resource="#Superclass"/>
</owl:Class>


The code to produce this scenario looks as follows:


OWLNamedClass superclass = owlModel.createOWLNamedClass("Superclass");
OWLNamedClass subclass = owlModel.createOWLNamedSubclass("Subclass", superclass);
OWLObjectProperty property = owlModel.createOWLObjectProperty("property");

OWLAllValuesFrom superRestriction = owlModel.createOWLAllValuesFrom(property, subclass);
superclass.addSuperclass(superRestriction);
OWLAllValuesFrom subRestriction = owlModel.createOWLAllValuesFrom(property, subclass);
subclass.addSuperclass(subRestriction);


Now, if we want to ask the subclass about its restrictions, we are probably not interested in the owl:allValuesFrom restriction defined for the superclass, because the restriction at the subclass "overloads" the superclass restriction. Therefore, the following code only returns the subclass restriction:


Collection restrictions = subclass.getRestrictions(true);
assert (1 == restrictions.size());

assert (restrictions.contains(subRestriction);


If we want to know which types of values for the property are allowed at the subclass, we can ask for the corresponding owl:allValuesFrom restrictions. The following code iterates on all restrictions that are defined on the given class and checks if any of them are owl:allValuesFrom restrictions. If success, the filler of that restriction is the result. If no matching restriction has been found, then the global range of the property is returned:


public RDFResource getAllValuesFrom(OWLNamedClass cls, RDFProperty property) {
    Collection restrictions = cls.getRestrictions(property, true);
    for (Iterator it = restrictions.iterator(); it.hasNext();) {
        OWLRestriction restriction = (OWLRestriction) it.next();
        if(restriction instanceof OWLAllValuesFrom) {
            return ((OWLAllValuesFrom)restriction).getAllValuesFrom();
        }
    }
    return property.getRange();
}


In fact there is a pre-defined convenience method OWLNamedClass.getAllValuesFrom() for exactly this case:


assert (subclass.equals(subclass.getAllValuesFrom(property)));


Similar mechanisms exist for cardinality restrictions, which we will look at in the following scenario. A superclass defines a maximum cardinality restriction of 3 for the property. This restriction is overloaded in the subclass to 2 (note that according to the semantics of OWL, subclasses can only narrow restrictions from their superclasses):


<owl:Class rdf:ID="Superclass">
  <rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
  <rdfs:subClassOf>
    <owl:Restriction>
      <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int">3</owl:maxCardinality>
      <owl:onProperty>
        <owl:ObjectProperty rdf:ID="property"/>
      </owl:onProperty>
    </owl:Restriction>
  </rdfs:subClassOf>
</owl:Class>
<owl:Class rdf:ID="Subclass">
  <rdfs:subClassOf>
    <owl:Restriction>
      <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int">2</owl:maxCardinality>
      <owl:onProperty rdf:resource="#property"/>
    </owl:Restriction>
  </rdfs:subClassOf>
  <rdfs:subClassOf rdf:resource="#Superclass"/>
</owl:Class>


The following code creates this scenario:


OWLNamedClass superclass = owlModel.createOWLNamedClass("Superclass");
OWLNamedClass subclass = owlModel.createOWLNamedSubclass("Subclass", superclass);
OWLObjectProperty property = owlModel.createOWLObjectProperty("property");
OWLMaxCardinality superRestriction = owlModel.createOWLMaxCardinality(property, 3);
superclass.addSuperclass(superRestriction);
OWLMaxCardinality subRestriction = owlModel.createOWLMaxCardinality(property, 2);
subclass.addSuperclass(subRestriction);


In this scenario, the following condition is true:


assert (2 == subclass.getMaxCardinality(property));


Note that in addition to owl:maxCardinality restrictions, there is another way in OWL to enforce maximum cardinalities. If a property is declared to be an owl:FunctionalProperty, then it can only take one global (distinct) value. In our example scenario, if we make the property functional, then the following code is also correct, no matter which other restrictions are defined at the class level:


property.setFunctional(true);
assert (1 == subclass.getMaxCardinality(property));


Logical Class Definitions (Unions, Intersections, Complements)

Logical class definitions can be used to build complex class expressions out of restrictions and named classes. Like restrictions, logical classes are only meaningful if they are attached to a specific named class or property. This time, we will start with a complex example right away in which we have a class Person with subclasses Man and Woman. In addition, there is a class Kid, which is a subclass of an anonymous expression that describes all individuals that are Persons and at the same time neither Men nor Women:


<owl:Class rdf:ID="Person"/>
<owl:Class rdf:ID="Woman">
  <rdfs:subClassOf rdf:resource="#Person"/>
</owl:Class>
<owl:Class rdf:ID="Man">
  <rdfs:subClassOf rdf:resource="#Person"/>
</owl:Class>
<owl:Class rdf:ID="Kid">
  <rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
  <rdfs:subClassOf>
    <owl:Class>
      <owl:intersectionOf rdf:parseType="Collection">
        <owl:Class rdf:about="#Person"/>
        <owl:Class>
          <owl:complementOf>
            <owl:Class>
              <owl:unionOf rdf:parseType="Collection">
                <owl:Class rdf:about="#Man"/>
                <owl:Class rdf:about="#Woman"/>
              </owl:unionOf>
            </owl:Class>
          </owl:complementOf>
        </owl:Class>
      </owl:intersectionOf>
    </owl:Class>
  </rdfs:subClassOf>
</owl:Class>


The Java code to create these definitions is as follows:


OWLNamedClass personClass = owlModel.createOWLNamedClass("Person");
OWLNamedClass manClass = owlModel.createOWLNamedSubclass("Man", personClass);
OWLNamedClass womanClass = owlModel.createOWLNamedSubclass("Woman", personClass);

// Create expression (PersonClass & !(Man | Woman))
OWLUnionClass unionClass = owlModel.createOWLUnionClass();
unionClass.addOperand(manClass);
unionClass.addOperand(womanClass);
OWLComplementClass complementClass = owlModel.createOWLComplementClass(unionClass);
OWLIntersectionClass intersectionClass = owlModel.createOWLIntersectionClass();
intersectionClass.addOperand(personClass);
intersectionClass.addOperand(complementClass);

OWLNamedClass kidClass = owlModel.createOWLNamedClass("Kid");
kidClass.addSuperclass(intersectionClass);


Analyzing these complex structures can be achieved by means of the corresponding interface methods from the Protege-OWL API. For example, there is a getOperands() method defined for OWLUnionClass and OWLIntersectionClass.

While the construction of such expressions is often complicated, the process can be made easier by using the OWLModel.createRDFSClassFromExpression() method. The intersection shown above can also be created in the following way:


owlModel.createRDFSClassFromExpression("Person & !(Man | Woman)");


The Protege-OWL API can be used to build arbitrarily complex expressions from existing expressions. The following code first converts the existing intersection class into an expression, and then adds a complement symbol (!) to create a new class.


String parsable = intersectionClass.getParsableExpression();
System.out.println("Expression: " + parsable);

RDFSClass c = owlModel.createRDFSClassFromExpression("!(" + parsable + ")");
System.out.println("New expression: " + c.getParsableExpression());


The program output is:


Expression: Person & !(Man | Woman)
New expression: !(Person & !(Man | Woman)) 


If you want to print expressions using the logical symbols, you can use the getBrowserText() method.


Enumerated Classes

An enumerated class is an anonymous class that lists all of its individuals explicitly. A typical example is a class TrafficLightColor consisting of the individuals red, yellow, and green:


<owl:Class rdf:ID="Color"/>
<Color rdf:ID="black"/>
<owl:Class rdf:ID="TrafficLightColor">
  <owl:equivalentClass>
    <owl:Class>
      <owl:oneOf rdf:parseType="Collection">
        <Color rdf:ID="red"/>
        <Color rdf:ID="yellow"/>
        <Color rdf:ID="green"/>
      </owl:oneOf>
    </owl:Class>
  </owl:equivalentClass>
</owl:Class>


The code to generate the OWL model shown above is as follows:


OWLNamedClass colorClass = owlModel.createOWLNamedClass("Color");
OWLIndividual red = colorClass.createOWLIndividual("red");
OWLIndividual yellow = colorClass.createOWLIndividual("yellow");
OWLIndividual green = colorClass.createOWLIndividual("green");
OWLIndividual black = colorClass.createOWLIndividual("black");

OWLNamedClass trafficLightColor = owlModel.createOWLNamedClass("TrafficLightColor");
OWLEnumeratedClass enum = owlModel.createOWLEnumeratedClass();
enum.addOneOf(red);
enum.addOneOf(yellow);
enum.addOneOf(green);
trafficLightColor.setDefinition(enum);


Note that this example does not use rdfs:subClassOf to associate the named class with the anonymous class. Instead, the example uses owl:equivalentClass, which is described next.


Creating Defined Classes

In Description Logics languages like OWL, classes can be either primitive or defined. Primitive classes only have necessary conditions, i.e., superclasses. Defined classes have necessary and sufficient conditions, i.e., equivalent classes. For a more comprehensive discussion of these aspects, please read the Protege-OWL Tutorial by Matthew Horridge.

A typical example for a defined class is the Parent concept, defined as the set of all persons that have at least one child:


<owl:Class rdf:ID="Person"/>
<owl:Class rdf:ID="Parent">
  <owl:equivalentClass>
    <owl:Class>
      <owl:intersectionOf rdf:parseType="Collection">
        <owl:Class rdf:about="#Person"/>
        <owl:Restriction>
          <owl:onProperty>
            <owl:ObjectProperty rdf:ID="hasChildren"/>
          </owl:onProperty>
          <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int"
          >1</owl:minCardinality>
        </owl:Restriction>
      </owl:intersectionOf>
    </owl:Class>
  </owl:equivalentClass>
</owl:Class>
<owl:ObjectProperty rdf:about="#hasChildren">
  <rdfs:domain rdf:resource="#Person"/>
</owl:ObjectProperty>


Technically, the class Parent is equivalent to the anonymous intersection of Person and the minimum cardinality restriction. The following code snippet creates the example from above:


OWLNamedClass personClass = owlModel.createOWLNamedClass("Person");
OWLObjectProperty hasChildrenProperty = owlModel.createOWLObjectProperty("hasChildren");
hasChildrenProperty.setDomain(personClass);

OWLNamedClass parentClass = owlModel.createOWLNamedClass("Parent");
OWLMinCardinality minCardinality = owlModel.createOWLMinCardinality(hasChildrenProperty, 1);
OWLIntersectionClass intersectionClass = owlModel.createOWLIntersectionClass();
intersectionClass.addOperand(personClass);
intersectionClass.addOperand(minCardinality);
parentClass.setDefinition(intersectionClass);


The important call here is OWLNamedClass.setDefinition(), which declares the named class to be equivalent to the anonymous class. Following this call, the class Person is also explicitly made a superclass of Parent. As a general rule, whenever an intersection class is made equivalent to a named class N, then all named operands of the intersection are also made superclasses of N. This makes it much easier to handle these defined classes because it makes the obvious subclass relationship explicit without the need to execute a reasoner first. Without this behavior, it would be much more difficult to display and walk through the class hierarchy.