Repository: ontodev/robot
Pull request #533 (2019-07-09 16:28:06)

Some refactoring of ReasonOperation. 
Create specialized axiom generators to handle direct vs. indirect. This will eliminate some cases of materializing axioms twice.


Patch:
diff --git a/docs/reason.md b/docs/reason.md
index 36a28376..19fa2e2e 100644
--- a/docs/reason.md
+++ b/docs/reason.md
@@ -26,9 +26,9 @@ robot reason --reasoner ELK \
   --output results/reasoned.owl
 ```
 
-Finally, `reason` includes two more options to help clean the reasoned output:
-* `--exclude-duplicate-axioms`: if set to true, the axioms will not be added to the output if they exist in an import (default `false`).
-* `--remove-redundant-subclass-axioms`: if set to false, redundant axioms (those that have been asserted and were also inferred) are included in the output (default `true`).
+Finally, `reason` includes two more options to help clean the reasoned output (by default, these are `false`):
+* `--exclude-duplicate-axioms`: if set to true, the axioms will not be added to the output if they exist in an import.
+* `--remove-redundant-subclass-axioms`: if set to true, any redundant axioms (those that have been asserted and were also inferred) will be removed from the output.
 
 If no `--reasoner` is provided, ROBOT will default to ELK. The following other reasoner options are supported:
   
diff --git a/robot-core/src/main/java/org/obolibrary/robot/ReasonOperation.java b/robot-core/src/main/java/org/obolibrary/robot/ReasonOperation.java
index 18d395a2..c9b794a6 100644
--- a/robot-core/src/main/java/org/obolibrary/robot/ReasonOperation.java
+++ b/robot-core/src/main/java/org/obolibrary/robot/ReasonOperation.java
@@ -10,6 +10,7 @@ import org.obolibrary.robot.checks.InvalidReferenceViolation;
 import org.obolibrary.robot.exceptions.*;
 import org.obolibrary.robot.reason.EquivalentClassReasoning;
 import org.obolibrary.robot.reason.EquivalentClassReasoningMode;
+import org.obolibrary.robot.reason.InferredSubClassAxiomGeneratorIncludingIndirect;
 import org.semanticweb.owlapi.apibinding.OWLManager;
 import org.semanticweb.owlapi.model.*;
 import org.semanticweb.owlapi.model.parameters.Imports;
@@ -233,8 +234,9 @@ public class ReasonOperation {
       Map<String, String> options) {
     String axGeneratorString = OptionsHelper.getOption(options, "axiom-generators", "subclass");
     List<String> axGenerators = Arrays.asList(axGeneratorString.split(" "));
+    boolean direct = !OptionsHelper.optionIsTrue(options, "include-indirect");
     List<InferredAxiomGenerator<? extends OWLAxiom>> gens =
-        ReasonerHelper.getInferredAxiomGenerators(axGenerators);
+        ReasonerHelper.getInferredAxiomGenerators(axGenerators, direct);
     logger.info("Using these axiom generators:");
     for (InferredAxiomGenerator<?> inf : gens) {
       logger.info("    " + inf);
@@ -263,38 +265,28 @@ public class ReasonOperation {
     OWLOntologyManager manager = ontology.getOWLOntologyManager();
     OWLDataFactory dataFactory = manager.getOWLDataFactory();
     boolean direct = !OptionsHelper.optionIsTrue(options, "include-indirect");
-
-    boolean subClass = false;
-    boolean classAssertion = false;
-    boolean subObjectProperty = false;
-    for (InferredAxiomGenerator g : gens) {
-      if (g instanceof InferredSubClassAxiomGenerator) {
-        subClass = true;
-      } else if (g instanceof InferredClassAssertionAxiomGenerator) {
-        classAssertion = true;
-      } else if (g instanceof InferredSubObjectPropertyAxiomGenerator) {
-        subObjectProperty = true;
-      }
-    }
+    boolean subClass =
+        gens.stream()
+            .anyMatch(
+                g ->
+                    (g instanceof InferredSubClassAxiomGenerator)
+                        || (g instanceof InferredSubClassAxiomGeneratorIncludingIndirect));
 
     // we first place all inferred axioms into a new ontology;
     // these will be later transferred into the main ontology,
     // unless the create new ontology option is passed
-    OWLOntology newAxiomOntology;
-    newAxiomOntology = manager.createOntology();
-
+    OWLOntology newAxiomOntology = manager.createOntology();
     InferredOntologyGenerator generator = new InferredOntologyGenerator(reasoner, gens);
     generator.fillOntology(dataFactory, newAxiomOntology);
 
     // If EMR, add expressions instead of just classes, etc...
-    ExpressionMaterializingReasoner emr = null;
     if (reasoner instanceof ExpressionMaterializingReasoner) {
       logger.info("Creating expression materializing reasoner...");
-      emr = (ExpressionMaterializingReasoner) reasoner;
+      ExpressionMaterializingReasoner emr = (ExpressionMaterializingReasoner) reasoner;
       emr.materializeExpressions();
       // Maybe add direct/indirect class expressions
       if (subClass) {
-        for (OWLClass c : ontology.getClassesInSignature()) {
+        for (OWLClass c : ontology.getClassesInSignature(Imports.INCLUDED)) {
           // Look at the superclasses because otherwise we would lose the anonymous exprs
           Set<OWLClassExpression> sces = emr.getSuperClassExpressions(c, direct);
           for (OWLClassExpression sce : sces) {
@@ -306,50 +298,6 @@ public class ReasonOperation {
           }
         }
       }
-    } else if (subClass) {
-      // if not EMR and still using subclasses, do not use expressions
-      for (OWLClass c : ontology.getClassesInSignature()) {
-        Set<OWLClass> scs = reasoner.getSubClasses(c, direct).getFlattened();
-        for (OWLClass sc : scs) {
-          if (!sc.isOWLNothing()) {
-            OWLAxiom ax = dataFactory.getOWLSubClassOfAxiom(sc, c);
-            logger.debug("NEW:" + ax);
-            manager.addAxiom(newAxiomOntology, ax);
-          }
-        }
-      }
-    }
-
-    // Maybe add direct/indirect class assertions
-    if (classAssertion) {
-      for (OWLClass c : ontology.getClassesInSignature()) {
-        Set<OWLNamedIndividual> inds = reasoner.getInstances(c, direct).getFlattened();
-        for (OWLNamedIndividual i : inds) {
-          OWLAxiom ax = dataFactory.getOWLClassAssertionAxiom(c, i);
-          logger.debug("NEW:" + ax);
-          manager.addAxiom(newAxiomOntology, ax);
-        }
-      }
-    }
-
-    // Maybe add direct/indirect object property expressions
-    if (subObjectProperty) {
-      for (OWLObjectProperty op : ontology.getObjectPropertiesInSignature()) {
-        // Look at superproperties so we can get the expressions
-        Set<OWLObjectPropertyExpression> sopes;
-        if (emr != null) {
-          sopes = emr.getSuperObjectProperties(op, direct).getFlattened();
-        } else {
-          sopes = reasoner.getSuperObjectProperties(op, direct).getFlattened();
-        }
-        for (OWLObjectPropertyExpression sope : sopes) {
-          if (!sope.getSignature().contains(dataFactory.getOWLTopObjectProperty())) {
-            OWLAxiom ax = dataFactory.getOWLSubObjectPropertyOfAxiom(op, sope);
-            logger.debug("NEW:" + ax);
-            manager.addAxiom(newAxiomOntology, ax);
-          }
-        }
-      }
     }
 
     return newAxiomOntology;
diff --git a/robot-core/src/main/java/org/obolibrary/robot/ReasonerHelper.java b/robot-core/src/main/java/org/obolibrary/robot/ReasonerHelper.java
index a1d3965b..a902f47c 100644
--- a/robot-core/src/main/java/org/obolibrary/robot/ReasonerHelper.java
+++ b/robot-core/src/main/java/org/obolibrary/robot/ReasonerHelper.java
@@ -6,6 +6,9 @@ import java.util.stream.Collectors;
 import org.obolibrary.robot.exceptions.IncoherentRBoxException;
 import org.obolibrary.robot.exceptions.IncoherentTBoxException;
 import org.obolibrary.robot.exceptions.InconsistentOntologyException;
+import org.obolibrary.robot.reason.InferredClassAssertionAxiomGeneratorDirectOnly;
+import org.obolibrary.robot.reason.InferredSubClassAxiomGeneratorIncludingIndirect;
+import org.obolibrary.robot.reason.InferredSubObjectPropertyAxiomGeneratorIncludingIndirect;
 import org.semanticweb.owlapi.model.*;
 import org.semanticweb.owlapi.model.parameters.Imports;
 import org.semanticweb.owlapi.reasoner.OWLReasoner;
@@ -233,21 +236,38 @@ public class ReasonerHelper {
    * Given a list of axiom generator strings, return a list of InferredAxiomGenerator objects.
    *
    * @param axGenerators list of strings to get InferredAxiomGenerators
+   * @param direct return axiom generators which include only direct
+   *     superclass/superproperties/types
    * @return list of InferredAxiomGenerators
    */
   public static List<InferredAxiomGenerator<? extends OWLAxiom>> getInferredAxiomGenerators(
-      List<String> axGenerators) {
+      List<String> axGenerators, boolean direct) {
     List<InferredAxiomGenerator<? extends OWLAxiom>> gens = new ArrayList<>();
     if (axGenerators == null || axGenerators.isEmpty()) {
-      gens.add(new InferredSubClassAxiomGenerator());
+      if (direct) {
+        gens.add(new InferredSubClassAxiomGenerator());
+      } else {
+        gens.add(new InferredSubClassAxiomGeneratorIncludingIndirect());
+      }
       return gens;
     }
     for (String ax : axGenerators) {
-      gens.add(getInferredAxiomGenerator(ax));
+      gens.add(getInferredAxiomGenerator(ax, direct));
     }
     return gens;
   }
 
+  /**
+   * Given a list of axiom generator strings, return a list of InferredAxiomGenerator objects.
+   *
+   * @param axGenerators list of strings to get InferredAxiomGenerators
+   * @return list of InferredAxiomGenerators
+   */
+  public static List<InferredAxiomGenerator<? extends OWLAxiom>> getInferredAxiomGenerators(
+      List<String> axGenerators) {
+    return getInferredAxiomGenerators(axGenerators, true);
+  }
+
   /**
    * Given an axiom generator as a string, return the InferredAxiomGenerator object.
    *
@@ -256,10 +276,27 @@ public class ReasonerHelper {
    */
   public static InferredAxiomGenerator<? extends OWLAxiom> getInferredAxiomGenerator(
       String axGenerator) {
+    return getInferredAxiomGenerator(axGenerator, true);
+  }
+
+  /**
+   * Given an axiom generator as a string, return the InferredAxiomGenerator object.
+   *
+   * @param axGenerator name of InferredAxiomGenerator
+   * @param direct return axiom generators which include only direct
+   *     superclass/superproperties/types
+   * @return InferredAxiomGenerator
+   */
+  public static InferredAxiomGenerator<? extends OWLAxiom> getInferredAxiomGenerator(
+      String axGenerator, boolean direct) {
     switch (axGenerator.toLowerCase()) {
       case "subclass":
       case "":
-        return new InferredSubClassAxiomGenerator();
+        if (direct) {
+          return new InferredSubClassAxiomGenerator();
+        } else {
+          return new InferredSubClassAxiomGeneratorIncludingIndirect();
+        }
       case "disjointclasses":
         return new InferredDisjointClassesAxiomGenerator();
       case "equivalentclass":
@@ -271,7 +308,11 @@ public class ReasonerHelper {
       case "subdataproperty":
         return new InferredSubDataPropertyAxiomGenerator();
       case "classassertion":
-        return new InferredClassAssertionAxiomGenerator();
+        if (direct) {
+          return new InferredClassAssertionAxiomGeneratorDirectOnly();
+        } else {
+          return new InferredClassAssertionAxiomGenerator();
+        }
       case "propertyassertion":
         return new InferredPropertyAssertionGenerator();
       case "equivalentobjectproperty":
@@ -281,7 +322,11 @@ public class ReasonerHelper {
       case "objectpropertycharacteristic":
         return new InferredObjectPropertyCharacteristicAxiomGenerator();
       case "subobjectproperty":
-        return new InferredSubObjectPropertyAxiomGenerator();
+        if (direct) {
+          return new InferredSubObjectPropertyAxiomGenerator();
+        } else {
+          return new InferredSubObjectPropertyAxiomGeneratorIncludingIndirect();
+        }
       default:
         throw new IllegalArgumentException(String.format(axiomGeneratorError, axGenerator));
     }
diff --git a/robot-core/src/main/java/org/obolibrary/robot/reason/InferredClassAssertionAxiomGeneratorDirectOnly.java b/robot-core/src/main/java/org/obolibrary/robot/reason/InferredClassAssertionAxiomGeneratorDirectOnly.java
new file mode 100644
index 00000000..4715ff1f
--- /dev/null
+++ b/robot-core/src/main/java/org/obolibrary/robot/reason/InferredClassAssertionAxiomGeneratorDirectOnly.java
@@ -0,0 +1,31 @@
+package org.obolibrary.robot.reason;
+
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.semanticweb.owlapi.model.OWLClass;
+import org.semanticweb.owlapi.model.OWLClassAssertionAxiom;
+import org.semanticweb.owlapi.model.OWLDataFactory;
+import org.semanticweb.owlapi.model.OWLNamedIndividual;
+import org.semanticweb.owlapi.reasoner.OWLReasoner;
+import org.semanticweb.owlapi.util.InferredIndividualAxiomGenerator;
+
+/** An InferredAxiomGenerator which returns only direct class assettion axioms. */
+public class InferredClassAssertionAxiomGeneratorDirectOnly
+    extends InferredIndividualAxiomGenerator<OWLClassAssertionAxiom> {
+
+  @Override
+  protected void addAxioms(
+      @Nonnull OWLNamedIndividual entity,
+      @Nonnull OWLReasoner reasoner,
+      @Nonnull OWLDataFactory dataFactory,
+      @Nonnull Set<OWLClassAssertionAxiom> result) {
+    for (OWLClass type : reasoner.getTypes(entity, true).getFlattened()) {
+      result.add(dataFactory.getOWLClassAssertionAxiom(type, entity));
+    }
+  }
+
+  @Override
+  public String getLabel() {
+    return "Class assertions (individual direct types)";
+  }
+}
diff --git a/robot-core/src/main/java/org/obolibrary/robot/reason/InferredSubClassAxiomGeneratorIncludingIndirect.java b/robot-core/src/main/java/org/obolibrary/robot/reason/InferredSubClassAxiomGeneratorIncludingIndirect.java
new file mode 100644
index 00000000..02f44766
--- /dev/null
+++ b/robot-core/src/main/java/org/obolibrary/robot/reason/InferredSubClassAxiomGeneratorIncludingIndirect.java
@@ -0,0 +1,34 @@
+package org.obolibrary.robot.reason;
+
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.semanticweb.owlapi.model.OWLClass;
+import org.semanticweb.owlapi.model.OWLDataFactory;
+import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
+import org.semanticweb.owlapi.reasoner.OWLReasoner;
+import org.semanticweb.owlapi.util.InferredClassAxiomGenerator;
+
+/** An InferredAxiomGenerator which returns both direct and indirect inferred subclass axioms. */
+public class InferredSubClassAxiomGeneratorIncludingIndirect
+    extends InferredClassAxiomGenerator<OWLSubClassOfAxiom> {
+
+  @Override
+  protected void addAxioms(
+      @Nonnull OWLClass entity,
+      @Nonnull OWLReasoner reasoner,
+      @Nonnull OWLDataFactory dataFactory,
+      @Nonnull Set<OWLSubClassOfAxiom> result) {
+    if (reasoner.isSatisfiable(entity)) {
+      for (OWLClass superclass : reasoner.getSuperClasses(entity, false).getFlattened()) {
+        result.add(dataFactory.getOWLSubClassOfAxiom(entity, superclass));
+      }
+    } else {
+      result.add(dataFactory.getOWLSubClassOfAxiom(entity, dataFactory.getOWLNothing()));
+    }
+  }
+
+  @Override
+  public String getLabel() {
+    return "Subclasses including indirect";
+  }
+}
diff --git a/robot-core/src/main/java/org/obolibrary/robot/reason/InferredSubObjectPropertyAxiomGeneratorIncludingIndirect.java b/robot-core/src/main/java/org/obolibrary/robot/reason/InferredSubObjectPropertyAxiomGeneratorIncludingIndirect.java
new file mode 100644
index 00000000..63cdc2de
--- /dev/null
+++ b/robot-core/src/main/java/org/obolibrary/robot/reason/InferredSubObjectPropertyAxiomGeneratorIncludingIndirect.java
@@ -0,0 +1,36 @@
+package org.obolibrary.robot.reason;
+
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.semanticweb.owlapi.model.OWLDataFactory;
+import org.semanticweb.owlapi.model.OWLObjectProperty;
+import org.semanticweb.owlapi.model.OWLObjectPropertyExpression;
+import org.semanticweb.owlapi.model.OWLSubObjectPropertyOfAxiom;
+import org.semanticweb.owlapi.reasoner.OWLReasoner;
+import org.semanticweb.owlapi.util.InferredObjectPropertyAxiomGenerator;
+
+/**
+ * An InferredAxiomGenerator which returns both direct and indirect inferred subobjectproperty
+ * axioms.
+ */
+public class InferredSubObjectPropertyAxiomGeneratorIncludingIndirect
+    extends InferredObjectPropertyAxiomGenerator<OWLSubObjectPropertyOfAxiom> {
+
+  @Override
+  protected void addAxioms(
+      @Nonnull OWLObjectProperty entity,
+      @Nonnull OWLReasoner reasoner,
+      @Nonnull OWLDataFactory dataFactory,
+      @Nonnull Set<OWLSubObjectPropertyOfAxiom> result,
+      @Nonnull Set<OWLObjectPropertyExpression> nonSimpleProperties) {
+    for (OWLObjectPropertyExpression prop :
+        reasoner.getSuperObjectProperties(entity, false).getFlattened()) {
+      result.add(dataFactory.getOWLSubObjectPropertyOfAxiom(entity, prop));
+    }
+  }
+
+  @Override
+  public String getLabel() {
+    return "Sub object properties including indirect";
+  }
+}


Test patch:
diff --git a/robot-core/src/test/java/org/obolibrary/robot/ReasonOperationTest.java b/robot-core/src/test/java/org/obolibrary/robot/ReasonOperationTest.java
index 39cdd16a..7cc87194 100644
--- a/robot-core/src/test/java/org/obolibrary/robot/ReasonOperationTest.java
+++ b/robot-core/src/test/java/org/obolibrary/robot/ReasonOperationTest.java
@@ -7,13 +7,9 @@ import java.util.HashMap;
 import java.util.Map;
 import org.geneontology.reasoner.ExpressionMaterializingReasonerFactory;
 import org.junit.Test;
+import org.semanticweb.HermiT.ReasonerFactory;
 import org.semanticweb.elk.owlapi.ElkReasonerFactory;
-import org.semanticweb.owlapi.model.AddImport;
-import org.semanticweb.owlapi.model.IRI;
-import org.semanticweb.owlapi.model.OWLAxiom;
-import org.semanticweb.owlapi.model.OWLImportsDeclaration;
-import org.semanticweb.owlapi.model.OWLOntology;
-import org.semanticweb.owlapi.model.OWLOntologyManager;
+import org.semanticweb.owlapi.model.*;
 import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
 import uk.ac.manchester.cs.jfact.JFactFactory;
 
@@ -353,6 +349,74 @@ public class ReasonOperationTest extends CoreTest {
             "ObjectPropertyAssertion(<http://purl.obolibrary.org/obo/OP_08> <http://purl.obolibrary.org/obo/IND_02> <http://purl.obolibrary.org/obo/IND_03>)"));
   }
 
+  /**
+   * Test direct axiom generators.
+   *
+   * @throws Exception on any problem
+   */
+  @Test
+  public void testDirectAxiomGenerators() throws Exception {
+    OWLOntology ontology = loadOntology("/reason-direct-indirect.ofn");
+    Map<String, String> options = ReasonOperation.getDefaultOptions();
+    options.put("axiom-generators", "Subclass SubObjectProperty ClassAssertion");
+    options.put("include-indirect", "false");
+    options.put("remove-redundant-subclass-axioms", "false");
+    OWLReasonerFactory reasonerFactory = new ReasonerFactory();
+    ReasonOperation.reason(ontology, reasonerFactory, options);
+    assertTrue(
+        checkContains(ontology, "ClassAssertion(<http://example.org/C> <http://example.org/c>)"));
+    assertFalse(
+        checkContains(ontology, "ClassAssertion(<http://example.org/B> <http://example.org/c>)"));
+    assertFalse(
+        checkContains(ontology, "ClassAssertion(<http://example.org/A> <http://example.org/c>)"));
+    assertTrue(
+        checkContains(ontology, "SubClassOf(<http://example.org/C> <http://example.org/B>)"));
+    assertFalse(
+        checkContains(ontology, "SubClassOf(<http://example.org/C> <http://example.org/A>)"));
+    assertFalse(
+        checkContains(ontology, "SubClassOf(<http://example.org/C> <http://example.org/D>)"));
+    assertTrue(
+        checkContains(
+            ontology, "SubObjectPropertyOf(<http://example.org/t> <http://example.org/s>)"));
+    assertFalse(
+        checkContains(
+            ontology, "SubObjectPropertyOf(<http://example.org/t> <http://example.org/r>)"));
+  }
+
+  /**
+   * Test indirect axiom generators.
+   *
+   * @throws Exception on any problem
+   */
+  @Test
+  public void testIndirectAxiomGenerators() throws Exception {
+    OWLOntology ontology = loadOntology("/reason-direct-indirect.ofn");
+    Map<String, String> options = ReasonOperation.getDefaultOptions();
+    options.put("axiom-generators", "Subclass SubObjectProperty ClassAssertion");
+    options.put("include-indirect", "true");
+    options.put("remove-redundant-subclass-axioms", "false");
+    OWLReasonerFactory reasonerFactory = new ReasonerFactory();
+    ReasonOperation.reason(ontology, reasonerFactory, options);
+    assertTrue(
+        checkContains(ontology, "ClassAssertion(<http://example.org/C> <http://example.org/c>)"));
+    assertTrue(
+        checkContains(ontology, "ClassAssertion(<http://example.org/B> <http://example.org/c>)"));
+    assertTrue(
+        checkContains(ontology, "ClassAssertion(<http://example.org/A> <http://example.org/c>)"));
+    assertTrue(
+        checkContains(ontology, "SubClassOf(<http://example.org/C> <http://example.org/B>)"));
+    assertTrue(
+        checkContains(ontology, "SubClassOf(<http://example.org/C> <http://example.org/A>)"));
+    assertTrue(
+        checkContains(ontology, "SubClassOf(<http://example.org/C> <http://example.org/D>)"));
+    assertTrue(
+        checkContains(
+            ontology, "SubObjectPropertyOf(<http://example.org/t> <http://example.org/s>)"));
+    assertTrue(
+        checkContains(
+            ontology, "SubObjectPropertyOf(<http://example.org/t> <http://example.org/r>)"));
+  }
+
   private boolean checkContains(OWLOntology reasoned, String axStr) {
     for (OWLAxiom a : reasoned.getLogicalAxioms()) {
       if (a.toString().equals(axStr)) return true;
diff --git a/robot-core/src/test/resources/reason-direct-indirect.ofn b/robot-core/src/test/resources/reason-direct-indirect.ofn
new file mode 100644
index 00000000..dc34e1e3
--- /dev/null
+++ b/robot-core/src/test/resources/reason-direct-indirect.ofn
@@ -0,0 +1,54 @@
+Prefix(:=<http://example.org/>)
+Prefix(owl:=<http://www.w3.org/2002/07/owl#>)
+Prefix(rdf:=<http://www.w3.org/1999/02/22-rdf-syntax-ns#>)
+Prefix(xml:=<http://www.w3.org/XML/1998/namespace>)
+Prefix(xsd:=<http://www.w3.org/2001/XMLSchema#>)
+Prefix(rdfs:=<http://www.w3.org/2000/01/rdf-schema#>)
+
+
+Ontology(<http://example.org/>
+
+Declaration(Class(:A))
+Declaration(Class(:B))
+Declaration(Class(:C))
+Declaration(Class(:D))
+Declaration(ObjectProperty(:r))
+Declaration(ObjectProperty(:s))
+Declaration(ObjectProperty(:t))
+Declaration(NamedIndividual(:c))
+############################
+#   Object Properties
+############################
+
+# Object Property: :s (:s)
+
+SubObjectPropertyOf(:s :r)
+
+# Object Property: :t (:t)
+
+SubObjectPropertyOf(:t :s)
+
+
+############################
+#   Classes
+############################
+
+# Class: :B (:B)
+
+EquivalentClasses(:B ObjectIntersectionOf(:A :D))
+
+# Class: :C (:C)
+
+SubClassOf(:C ObjectIntersectionOf(:B :D))
+
+
+############################
+#   Named Individuals
+############################
+
+# Individual: :c (:c)
+
+ClassAssertion(:C :c)
+
+
+)
\ No newline at end of file