diff --git a/.gitignore b/.gitignore
index ea8c4bf..628a391 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
/target
+semantic.cache
+LOG*
diff --git a/bin/emr b/bin/emr
new file mode 100755
index 0000000..269c9d9
--- /dev/null
+++ b/bin/emr
@@ -0,0 +1,147 @@
+#!/bin/sh
+DIRNAME=`dirname $0`
+#PATH_TO_ME=`which $0`;
+# Give a space-separated list of classpath items RELATIVE TO THE CURRENT SCRIPT
+# These will be resolved into absolute pathnames
+# Wildcards are allowed
+CLASSPATH_RELATIVE=emr.jar
+
+## Check for Cygwin, use grep for a case-insensitive search
+IS_CYGWIN="FALSE"
+if uname | grep -iq cygwin; then
+ IS_CYGWIN="TRUE"
+fi
+
+# If literal classpath values are needed, uncomment the line below
+# This can be useful if the classpath contains URLs
+# CLASSPATH_LITERAL=""
+
+# To set a specific default Java path, set the JAVAPATH variable below.
+# This value can be overridden with the -Jvm= option.
+# If JAVAPATH is not set, the script will use whatever version of Java is on the
+# path. If there is no copy of Java on the path, the JAVA_HOME environment
+# variable will be used. If that fails, we just use "java" in the hopes that the
+# failure message will make a little more sense.
+# JAVAPATH="java"
+
+if [ $IS_CYGWIN = "TRUE" ]
+then
+ PATH_SEP=";"
+else
+ PATH_SEP=":"
+fi
+
+JAVAARGS=" "
+CMDARGS=" "
+
+# Remove the name of this script from the end of the path
+PATH_TO_ME=`which $0`;
+PATH_TO_ME=`echo $PATH_TO_ME | sed -e "s/\(.*\)\/.*/\1/g"`
+
+if [ $IS_CYGWIN = "TRUE" ]
+then
+ LAUNCHER_DIR="`cygpath --mixed $PATH_TO_ME`"
+else
+ LAUNCHER_DIR="$PATH_TO_ME"
+fi
+
+
+# Just the name of the script.
+SCRIPTNAME=`echo $PATH_TO_ME | sed -e "s/.*\/\(.*\)/\1/g"`
+
+## Add vmoptions to JAVAARGS if the proper file is available.
+if [ -e "$PATH_TO_ME/$SCRIPTNAME.vmoptions" ]
+then
+ VMOPTIONS=`cat $PATH_TO_ME/$SCRIPTNAME.vmoptions`
+ for OPTION in "$VMOPTIONS"
+ do
+ JAVAARGS="$JAVAARGS '${OPTION}'"
+ done
+else
+ if [ $EMR_MEMORY ]
+ then
+ JAVAARGS="$JAVAARGS -Xmx$EMR_MEMORY"
+ else
+ JAVAARGS="$JAVAARGS -Xmx12G"
+ fi
+fi
+
+## Walk through all the command line arguments and add them to
+## the CMDARGS depending.
+
+for ARG in "$@"
+do
+ CMDARGS="${CMDARGS} '${ARG}'"
+ shift 1;
+done
+
+## Add to CLASSPATH using CLASSPATH_RELATIVE.
+CLASSPATH=""
+for ARG in "$CLASSPATH_RELATIVE"
+do
+ DEREFERENCED_CLASSPATH=`ls -1 -L $PATH_TO_ME/$ARG | grep -v ontologyrelease`
+ for CP_ENTRY in $DEREFERENCED_CLASSPATH
+ do
+ if [ $IS_CYGWIN = "TRUE" ]
+ then
+ CP_ENTRY="`cygpath --mixed \"$CP_ENTRY\"`"
+ fi
+ if [ -z "$CLASSPATH" ]
+ then
+ CLASSPATH="$CP_ENTRY"
+ else
+ CLASSPATH="$CLASSPATH$PATH_SEP$CP_ENTRY"
+ fi
+ done
+done
+
+## Add to CLASSPATH using CLASSPATH_LITERAL.
+if [ -n "$CLASSPATH_LITERAL" ]
+then
+ for CP_ENTRY in $CLASSPATH_LITERAL
+ do
+ if [ -z "$CLASSPATH" ]
+ then
+ CLASSPATH="$CP_ENTRY"
+ else
+ CLASSPATH="$CLASSPATH$PATH_SEP$CP_ENTRY"
+ fi
+ done
+fi
+
+## Figure out which java to use.
+if [ -z "$JAVAPATH" ]
+then
+ JAVAPATH=`which java`
+ if [ -z "$JAVAPATH" ]
+ then
+ if [ -n "$JAVA_HOME" && -e "$JAVA_HOME" ]
+ then
+ JAVAPATH=$JAVA_HOME/bin/java
+ else
+ JAVAPATH="java"
+ fi
+ fi
+fi
+
+## Assemble and run the final command.
+CMD="\"$JAVAPATH\" -Xms2048M -DentityExpansionLimit=4086000 -Djava.awt.headless=true -classpath \"$CLASSPATH\" -DlauncherDir=\"$LAUNCHER_DIR\" $JAVAARGS org.geneontology.reasoner.EmrRunner $CMDARGS"
+
+## DEBUG
+## Let's see a little of the environment if we declare DEBUG=1.
+if [ $DEBUG ]
+then
+ for MYVAR in DEBUG PATH_TO_ME SCRIPTNAME DIRNAME JAVAARGS CMDARGS CLASSPATH_RELATIVE CMD
+ do
+ LETVAR=`echo "$"$MYVAR`
+ TMPVAR=`eval echo $LETVAR`
+ echo "${MYVAR}: \"${TMPVAR}\""
+ done
+fi
+
+## Run the final command.
+if [ $DEBUG ]
+then
+ echo "$CMD"
+fi
+sh -c "$CMD"
diff --git a/pom.xml b/pom.xml
index 4b5c6a1..9dcf754 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
org.geneontology
expression-materializing-reasoner
- 0.1.3
+ 0.2.0
jar
${project.groupId}:${project.artifactId}
An OWL reasoner extension for quering over existential restrictions
@@ -38,7 +38,7 @@
HEAD
-
+
UTF-8
4.2.5
@@ -51,6 +51,35 @@
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+ emr
+ false
+ false
+ ${project.basedir}/bin
+
+ jar-with-dependencies
+
+
+
+ true
+ org.geneontology.reasoner.EmrRunner
+
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
org.sonatype.plugins
nexus-staging-maven-plugin
@@ -150,7 +179,6 @@
org.semanticweb.elk
elk-owlapi
0.4.3
- test
net.sourceforge.owlapi
@@ -159,10 +187,9 @@
test
- org.slf4j
- slf4j-log4j12
- 1.7.10
- test
+ com.beust
+ jcommander
+ 1.48
diff --git a/src/main/java/org/geneontology/reasoner/EmrRunner.java b/src/main/java/org/geneontology/reasoner/EmrRunner.java
new file mode 100644
index 0000000..7b596dc
--- /dev/null
+++ b/src/main/java/org/geneontology/reasoner/EmrRunner.java
@@ -0,0 +1,154 @@
+package org.geneontology.reasoner;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.semanticweb.elk.owlapi.ElkReasonerFactory;
+import org.semanticweb.owlapi.apibinding.OWLManager;
+import org.semanticweb.owlapi.formats.FunctionalSyntaxDocumentFormat;
+import org.semanticweb.owlapi.model.IRI;
+import org.semanticweb.owlapi.model.OWLDataFactory;
+import org.semanticweb.owlapi.model.OWLObjectProperty;
+import org.semanticweb.owlapi.model.OWLOntology;
+import org.semanticweb.owlapi.model.OWLOntologyCreationException;
+import org.semanticweb.owlapi.model.OWLOntologyManager;
+import org.semanticweb.owlapi.model.OWLOntologyStorageException;
+import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
+
+import com.beust.jcommander.JCommander;
+import com.beust.jcommander.Parameter;
+
+/**
+ * Command line wrapper
+ *
+ * @author cjm
+ *
+ */
+public class EmrRunner {
+
+ private static Logger LOG = Logger.getLogger(EmrRunner.class);
+
+ @Parameter(names = { "-v", "--verbose" }, description = "Level of verbosity")
+ private Integer verbose = 1;
+
+ @Parameter(names = { "-o", "--out"}, description = "output OWL file")
+ private String outpath;
+
+ @Parameter(names = { "-p", "--properties"}, description = "OWL file containing declarations of properties to be used")
+ private String propOntPath;
+
+ @Parameter(names = { "-m", "--method"}, description = "one of: closure, svf")
+ private String method = "closure";
+
+ @Parameter(names = { "-t", "--tabfile"}, description = "output tabfile")
+ private String outtabfile;
+
+ @Parameter(description = "Files")
+ private List files = new ArrayList<>();
+
+ OWLOntologyManager manager;
+
+
+ public static void main(String ... args) throws OWLOntologyCreationException, IOException, OWLOntologyStorageException {
+ EmrRunner main = new EmrRunner();
+ new JCommander(main, args);
+ main.run();
+ }
+
+ public void run() throws OWLOntologyCreationException, IOException, OWLOntologyStorageException {
+
+ Logger.getRootLogger().setLevel(Level.INFO);
+ Logger.getLogger("org.semanticweb.elk").setLevel(Level.OFF);
+
+ OWLOntology ontology = this.loadOWL(files.get(0));
+
+ OWLOntology propOnt = null;
+ Set props = null;
+ if (propOntPath != null) {
+ propOnt = loadOWL(propOntPath);
+ props = propOnt.getObjectPropertiesInSignature();
+ LOG.info("PROPS: "+props);
+ }
+
+ ElkReasonerFactory ef = new ElkReasonerFactory();
+ OWLExtendedReasonerFactory elkEmrFactory;
+ elkEmrFactory = new ExpressionMaterializingReasonerFactory(ef);
+ LOG.info("creating extended reasoner");
+ OWLExtendedReasoner xreasoner = elkEmrFactory.createReasoner(ontology);
+ LOG.info("Done creating reasoner");
+
+ LOG.info("materializing props");
+ if (props != null) {
+ for (OWLObjectProperty p : props) {
+ ((ExpressionMaterializingReasoner)xreasoner).materializeExpressions(p);
+ }
+ }
+ else {
+ LOG.info("materializing ALL props");
+ ((ExpressionMaterializingReasoner)xreasoner).materializeExpressions();
+ }
+ LOG.info("DONE materializing props");
+
+
+ OWLOntologyManager mgr = ontology.getOWLOntologyManager();
+ OWLDataFactory df = mgr.getOWLDataFactory();
+ OWLOntology newOntology = mgr.createOntology();
+ newOntology = mgr.createOntology();
+ if (method.equals("svf")) {
+ LOG.info("Getting all subsumptions");
+ Set axioms = ExtenderReasonerUtils.getInferredSubClassOfGCIAxioms(xreasoner, props);
+ mgr.addAxioms(newOntology, axioms);
+ if (outtabfile != null) {
+ ExtenderReasonerUtils.writeInferredSubClassOfGCIAxioms(axioms, outtabfile);
+ }
+ }
+ else {
+ // assume method is closure
+ Set axioms = ExtenderReasonerUtils.getInferredSubClassOfAxiomsForNamedClasses(xreasoner, false);
+ mgr.addAxioms(newOntology, axioms);
+ }
+ File outfile = new File(outpath);
+ LOG.info("Saving to "+outfile);
+ System.out.println("Saving to "+outfile);
+ FunctionalSyntaxDocumentFormat fmt = new FunctionalSyntaxDocumentFormat();
+ mgr.saveOntology(newOntology, fmt, IRI.create(outfile));
+
+ }
+
+ private OWLOntologyManager getOWLOntologyManager() {
+ if (manager == null)
+ manager = OWLManager.createOWLOntologyManager();
+ return manager;
+ }
+ /**
+ * @param iri
+ * @return OWL Ontology
+ * @throws OWLOntologyCreationException
+ */
+ public OWLOntology loadOWL(IRI iri) throws OWLOntologyCreationException {
+ return getOWLOntologyManager().loadOntology(iri);
+ }
+
+ public OWLOntology loadOWL(String path) throws OWLOntologyCreationException {
+ File file = new File(path);
+ return loadOWL(file);
+ }
+
+
+
+
+ /**
+ * @param file
+ * @return OWL Ontology
+ * @throws OWLOntologyCreationException
+ */
+ public OWLOntology loadOWL(File file) throws OWLOntologyCreationException {
+ IRI iri = IRI.create(file);
+ return getOWLOntologyManager().loadOntologyFromOntologyDocument(iri);
+ }
+}
diff --git a/src/main/java/org/geneontology/reasoner/ExpressionMaterializingReasoner.java b/src/main/java/org/geneontology/reasoner/ExpressionMaterializingReasoner.java
index f9b061f..9fb622a 100644
--- a/src/main/java/org/geneontology/reasoner/ExpressionMaterializingReasoner.java
+++ b/src/main/java/org/geneontology/reasoner/ExpressionMaterializingReasoner.java
@@ -8,6 +8,8 @@
import java.util.Map;
import java.util.Set;
+import org.apache.log4j.Logger;
+import org.semanticweb.elk.owlapi.ElkReasoner;
import org.semanticweb.owlapi.model.AddImport;
import org.semanticweb.owlapi.model.AxiomType;
import org.semanticweb.owlapi.model.IRI;
@@ -17,17 +19,20 @@
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLDataPropertyExpression;
+import org.semanticweb.owlapi.model.OWLDisjointClassesAxiom;
import org.semanticweb.owlapi.model.OWLEquivalentClassesAxiom;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLObjectPropertyExpression;
+import org.semanticweb.owlapi.model.OWLObjectPropertyRangeAxiom;
import org.semanticweb.owlapi.model.OWLObjectSomeValuesFrom;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyChange;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyID;
import org.semanticweb.owlapi.model.OWLOntologyManager;
+import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
import org.semanticweb.owlapi.model.SetOntologyID;
import org.semanticweb.owlapi.model.UnknownOWLOntologyException;
import org.semanticweb.owlapi.model.parameters.Imports;
@@ -65,14 +70,17 @@
* to expressions of depth k
*
* In terms of performance the biggest impact are the number of {@link OWLObjectProperty}
- * for which the materialization is required.
+ * for which the materialization is required.
+ *
* It is usually *NOT* recommended to use all properties of an ontology signature.
+ * Instead call materializeExpressions
*
* @author cjm
*
*/
public class ExpressionMaterializingReasoner extends OWLReasonerBase implements OWLExtendedReasoner {
+ private static Logger LOG = Logger.getLogger(ExpressionMaterializingReasoner.class);
private OWLReasoner wrappedReasoner;
private final OWLDataFactory dataFactory;
@@ -80,7 +88,10 @@ public class ExpressionMaterializingReasoner extends OWLReasonerBase implements
private final OWLOntology expandedOntology;
final OWLOntologyManager manager;
final Set cachedProperties;
- private final Map cxMap;
+
+ // map between materialized named classes and their class expression equivalent
+ private final Map cxMap;
+ private final Map cxMapRev;
private boolean includeImports = false;
@@ -101,14 +112,89 @@ protected ExpressionMaterializingReasoner(OWLOntology rootOntology,
wrappedReasoner = reasonerFactory.createNonBufferingReasoner(expandedOntology, configuration);
}
-
} catch (UnknownOWLOntologyException e) {
throw new RuntimeException("Could not setup reasoner", e);
} catch (OWLOntologyCreationException e) {
throw new RuntimeException("Could not setup reasoner", e);
}
cachedProperties = new HashSet();
- cxMap = new HashMap();
+ cxMap = new HashMap();
+ cxMapRev = new HashMap();
+
+ // we can consider making this optional, but in general it seems to have
+ // no drawback in making inference slower
+ if (wrappedReasoner instanceof ElkReasoner) {
+ rewriteRangeAxioms();
+ }
+ }
+
+
+
+ /**
+ * Range axioms are very useful for curtailing the space of possible SVF expressions.
+ * E.g. forbid occurs-in SOME apoptosis
+ *
+ * Elk does not support Range axioms, so we use the range axioms to generate
+ * additional axioms that while not complete give us many benefits.
+ *
+ * E.g. if we have:
+ * - Range(occurs-in, material-entity)
+ * - material-entity SubClassOf* continuant
+ * - continuant DisjointWith occurrent
+ *
+ * then we can add an axiom
+ * Nothing = occurs-in some continuant
+ *
+ * Adding these do not appear to increase overall time for ontologies such as go-plus,
+ * and overall running is faster due to less expressions being generated.
+ *
+ *
+ *
+ */
+ public void rewriteRangeAxioms() {
+ OWLClass nothing = dataFactory.getOWLNothing();
+ Set newAxioms = new HashSet<>();
+ Set raxs = new HashSet<>();
+ Map> disjointMap = new HashMap<>();
+ for (OWLAxiom ax : rootOntology.getAxioms(Imports.INCLUDED)) {
+ if (ax instanceof OWLDisjointClassesAxiom) {
+ OWLDisjointClassesAxiom da = (OWLDisjointClassesAxiom)ax;
+ for (OWLClassExpression c1 : da.getClassExpressions()) {
+ if (!disjointMap.containsKey(c1))
+ disjointMap.put(c1, new HashSet<>());
+ for (OWLClassExpression c2 : da.getClassExpressionsMinus(c1)) {
+ disjointMap.get(c1).add(c2);
+ }
+
+ }
+ }
+ }
+ for (OWLAxiom ax : rootOntology.getAxioms(Imports.INCLUDED)) {
+ if (ax instanceof OWLObjectPropertyRangeAxiom) {
+ OWLObjectPropertyRangeAxiom rax = (OWLObjectPropertyRangeAxiom)ax;
+ raxs.add(rax);
+ OWLClassExpression rc = rax.getRange();
+ // elk does not support disjoint classes, so we follow hierarchy
+ Set disjointClasses = new HashSet<>();
+ Set rscs =
+ wrappedReasoner.getSuperClasses(rc, false).getFlattened();
+ for (OWLClass rsc : rscs) {
+ if (disjointMap.containsKey(rsc))
+ disjointClasses.addAll(disjointMap.get(rsc));
+ }
+ if (disjointMap.containsKey(rc)) {
+ disjointClasses.addAll(disjointMap.get(rc));
+ }
+
+ for (OWLClassExpression d : disjointClasses) {
+ OWLEquivalentClassesAxiom cax = dataFactory.getOWLEquivalentClassesAxiom(nothing,
+ dataFactory.getOWLObjectSomeValuesFrom(rax.getProperty(), d));
+ newAxioms.add(cax);
+ LOG.info("Translated "+rax+" --> "+cax);
+ }
+ }
+ }
+ expandedOntology.getOWLOntologyManager().addAxioms(expandedOntology, newAxioms);
}
/**
@@ -138,6 +224,7 @@ private OWLOntology createExpandedOntologyStub(OWLOntology rootOntology)
AddImport ai = new AddImport(expandedOntology,
dataFactory.getOWLImportsDeclaration(rootOntologyIRI));
manager.applyChange(ai);
+
return expandedOntology;
}
@@ -173,6 +260,7 @@ public boolean isIncludeImports() {
* @see ExpressionMaterializingReasoner#setIncludeImports(boolean) if it should include imports
*/
public void materializeExpressions() {
+ LOG.info("Materializing SVFs for ALL properties");
materializeExpressions(rootOntology.getObjectPropertiesInSignature(Imports.fromBoolean(includeImports)));
}
@@ -183,6 +271,7 @@ public void materializeExpressions() {
* @see ExpressionMaterializingReasoner#setIncludeImports(boolean) if it should include imports
*/
public void materializeExpressions(Collection properties) {
+ LOG.info("Materializing SVFs for: "+properties);
for (OWLObjectProperty p : properties) {
if (cachedProperties.contains(p)){
continue;
@@ -206,6 +295,8 @@ public void materializeExpressions(OWLObjectProperty p) {
}
private void materializeExpressionsInternal(OWLObjectProperty p) {
+ LOG.info("Materializing SVFs for: "+p);
+
for (OWLClass baseClass : rootOntology.getClassesInSignature(Imports.fromBoolean(includeImports))) {
// only materialize for non-helper classes
if (cxMap.containsKey(baseClass)) {
@@ -213,13 +304,14 @@ private void materializeExpressionsInternal(OWLObjectProperty p) {
}
OWLObjectSomeValuesFrom x = dataFactory.getOWLObjectSomeValuesFrom(p, baseClass);
IRI xciri = IRI.create(baseClass.getIRI()+"__"+saveIRItoString(p.getIRI()));
- OWLClass xc = dataFactory.getOWLClass(xciri);
- OWLEquivalentClassesAxiom eca = dataFactory.getOWLEquivalentClassesAxiom(xc, x);
+ OWLClass namedClassEquivalent = dataFactory.getOWLClass(xciri);
+ OWLEquivalentClassesAxiom eca = dataFactory.getOWLEquivalentClassesAxiom(namedClassEquivalent, x);
String lbl = p.getIRI().getShortForm()+" "+baseClass.getIRI().getShortForm();
manager.addAxiom(expandedOntology, dataFactory.getOWLAnnotationAssertionAxiom(xciri, dataFactory.getOWLAnnotation(dataFactory.getRDFSLabel(), dataFactory.getOWLLiteral(lbl))));
- cxMap.put(xc, x);
+ cxMap.put(namedClassEquivalent, x);
+ cxMapRev.put(x, namedClassEquivalent);
manager.addAxiom(expandedOntology, eca);
- manager.addAxiom(expandedOntology, dataFactory.getOWLDeclarationAxiom(xc));
+ manager.addAxiom(expandedOntology, dataFactory.getOWLDeclarationAxiom(namedClassEquivalent));
}
cachedProperties.add(p);
}
@@ -254,6 +346,57 @@ public Set getSuperClassExpressions(OWLClassExpression ce,
}
return ces;
}
+
+ public Set getSuperClassExpressionsForGCI(OWLClass baseClass,
+ OWLObjectProperty p,
+ boolean direct) throws InconsistentOntologyException,
+ ClassExpressionNotInProfileException, FreshEntitiesException,
+ ReasonerInterruptedException, TimeOutException {
+ return getSomeValuesFromSuperClasses(baseClass, p, direct, false);
+ }
+
+ boolean isIssuedAnonWarning = false;
+ public Set getSomeValuesFromSuperClasses(OWLClass baseClass,
+ OWLObjectProperty p,
+ boolean direct, boolean reflexive) throws InconsistentOntologyException,
+ ClassExpressionNotInProfileException, FreshEntitiesException,
+ ReasonerInterruptedException, TimeOutException {
+
+ Set ces = new HashSet<>();
+ wrappedReasoner.flush();
+
+ OWLObjectSomeValuesFrom svf = dataFactory.getOWLObjectSomeValuesFrom(p, baseClass);
+ OWLClassExpression queryExpression = svf;
+ // for efficiency, used a generated named class if available.
+ // Explanation: Elk is highly inefficient if queried using a class expression, as
+ // it needs to generate a new class and test with this. It's unnecessary to force Elk to
+ // do this, if we already have a NC equivalent for the query expression
+ // TODO: make a PR on Elk code
+ if (cxMapRev.containsKey(svf)) {
+ queryExpression = cxMapRev.get(svf);
+ }
+ else {
+ if (!isIssuedAnonWarning) {
+ LOG.warn("Unknown svf "+svf+" this forces Elk to make a new class which may be slow...");
+ }
+ isIssuedAnonWarning = true;
+ }
+ if (!wrappedReasoner.isSatisfiable(queryExpression)) {
+ return ces;
+ }
+ for (OWLClass c : wrappedReasoner.getSuperClasses(queryExpression, direct).getFlattened()) {
+ if (cxMap.containsKey(c)) {
+ OWLObjectSomeValuesFrom cx = cxMap.get(c);
+ ces.add(cx);
+ }
+ }
+ if (reflexive) {
+ ces.add(svf);
+ }
+ return ces;
+ }
+
+
public Set getSuperClassesOver(OWLClassExpression ce,
OWLObjectProperty p,
@@ -321,7 +464,8 @@ public Set getPendingAxiomRemovals() {
}
public OWLOntology getRootOntology() {
- return wrappedReasoner.getRootOntology();
+ return rootOntology;
+ //return wrappedReasoner.getRootOntology(); // note that additional classes injected here
}
public void interrupt() {
diff --git a/src/main/java/org/geneontology/reasoner/ExtenderReasonerUtils.java b/src/main/java/org/geneontology/reasoner/ExtenderReasonerUtils.java
new file mode 100644
index 0000000..38df51a
--- /dev/null
+++ b/src/main/java/org/geneontology/reasoner/ExtenderReasonerUtils.java
@@ -0,0 +1,146 @@
+package org.geneontology.reasoner;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
+import org.semanticweb.owlapi.model.OWLClass;
+import org.semanticweb.owlapi.model.OWLClassExpression;
+import org.semanticweb.owlapi.model.OWLDataFactory;
+import org.semanticweb.owlapi.model.OWLEntity;
+import org.semanticweb.owlapi.model.OWLObject;
+import org.semanticweb.owlapi.model.OWLObjectProperty;
+import org.semanticweb.owlapi.model.OWLObjectSomeValuesFrom;
+import org.semanticweb.owlapi.model.OWLOntology;
+import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
+import org.semanticweb.owlapi.model.parameters.Imports;
+import org.semanticweb.owlapi.reasoner.ClassExpressionNotInProfileException;
+import org.semanticweb.owlapi.reasoner.FreshEntitiesException;
+import org.semanticweb.owlapi.reasoner.InconsistentOntologyException;
+import org.semanticweb.owlapi.reasoner.ReasonerInterruptedException;
+import org.semanticweb.owlapi.reasoner.TimeOutException;
+
+/**
+ * @author cjm
+ *
+ */
+public class ExtenderReasonerUtils {
+
+ private static Logger LOG = Logger.getLogger(ExtenderReasonerUtils.class);
+
+ /**
+ * finds all inferred SubClassOf(C, R some D) using extended reasoner
+ *
+ * @param xr
+ * @param direct
+ * @return inferred axioms
+ */
+ public static Set getInferredSubClassOfAxiomsForNamedClasses(OWLExtendedReasoner xr, boolean direct) {
+ Set axioms = new HashSet<>();
+ OWLOntology ont = xr.getRootOntology();
+ OWLDataFactory df = ont.getOWLOntologyManager().getOWLDataFactory();
+ Set cs = ont.getClassesInSignature(Imports.INCLUDED);
+ for (OWLClass c : cs) {
+
+ for (OWLClassExpression sc : xr.getSuperClassExpressions(c, direct)) {
+ axioms.add(df.getOWLSubClassOfAxiom(c, sc));
+ }
+
+ }
+ return axioms;
+ }
+
+
+ /**
+ * Returns all axioms (p some c) SubClassOf (q some s)
+ * for a given p
+ *
+ * @param xr
+ * @param p
+ * @return subClassOf axioms between someValuesFrom expressions
+ * @throws InconsistentOntologyException
+ * @throws ClassExpressionNotInProfileException
+ * @throws FreshEntitiesException
+ * @throws ReasonerInterruptedException
+ * @throws TimeOutException
+ */
+ public static Set getInferredSubClassOfGCIAxioms(OWLExtendedReasoner xr, OWLObjectProperty p) throws InconsistentOntologyException,
+ ClassExpressionNotInProfileException, FreshEntitiesException,
+ ReasonerInterruptedException, TimeOutException {
+ Set axioms = new HashSet<>();
+ int n=0;
+ OWLOntology ont = xr.getRootOntology();
+ OWLDataFactory df = ont.getOWLOntologyManager().getOWLDataFactory();
+ Set allClasses = ont.getClassesInSignature(Imports.INCLUDED);
+ for (OWLClass c : allClasses) {
+ n++;
+ if (n % 1000 == 0) {
+ LOG.info("Class "+n+"/"+allClasses.size());
+ }
+ for (OWLObjectSomeValuesFrom sc : ((ExpressionMaterializingReasoner)xr).getSomeValuesFromSuperClasses(c, p, false, true)) {
+ axioms.add(df.getOWLSubClassOfAxiom(
+ df.getOWLObjectSomeValuesFrom(p, c),
+ sc));
+ }
+ }
+ return axioms;
+ }
+
+ public static Set getInferredSubClassOfGCIAxioms(OWLExtendedReasoner xr) throws InconsistentOntologyException,
+ ClassExpressionNotInProfileException, FreshEntitiesException,
+ ReasonerInterruptedException, TimeOutException {
+ OWLOntology ont = xr.getRootOntology();
+ OWLDataFactory df = ont.getOWLOntologyManager().getOWLDataFactory();
+ Set axioms = new HashSet<>();
+ for (OWLObjectProperty p : ont.getObjectPropertiesInSignature()) {
+ LOG.info("Calculating svf subsumptions for "+p);
+ axioms.addAll(getInferredSubClassOfGCIAxioms(xr, p));
+ }
+ return axioms;
+ }
+
+ public static Set getInferredSubClassOfGCIAxioms(OWLExtendedReasoner xr,
+ Set props) throws InconsistentOntologyException,
+ ClassExpressionNotInProfileException, FreshEntitiesException,
+ ReasonerInterruptedException, TimeOutException {
+ OWLOntology ont = xr.getRootOntology();
+ OWLDataFactory df = ont.getOWLOntologyManager().getOWLDataFactory();
+ Set axioms = new HashSet<>();
+ if (props == null)
+ props = ont.getObjectPropertiesInSignature();
+ for (OWLObjectProperty p : props) {
+ LOG.info("Calculating svf subsumptions for "+p);
+ axioms.addAll(getInferredSubClassOfGCIAxioms(xr, p));
+ }
+ return axioms;
+
+ }
+
+ public static void writeInferredSubClassOfGCIAxioms(Set axioms, String outpath) throws IOException {
+ File file = new File(outpath);
+ FileWriter writer = (new FileWriter(file));
+ for (OWLSubClassOfAxiom a : axioms) {
+ OWLObjectSomeValuesFrom c = (OWLObjectSomeValuesFrom)a.getSubClass();
+ OWLObjectSomeValuesFrom p = (OWLObjectSomeValuesFrom)a.getSuperClass();
+
+ writer.write(id(c.getProperty()) + "\t" + id(c.getFiller()) + "\t" +
+ id(p.getProperty()) + "\t" + id(p.getFiller()));
+ writer.write("\n");
+ }
+ writer.close();
+ }
+
+
+ private static String id(OWLObject obj) {
+ if (obj instanceof OWLEntity) {
+ return ((OWLEntity)obj).getIRI().getRemainder().get();
+ }
+ else {
+ return obj.toString();
+ }
+ }
+}
diff --git a/src/main/java/org/geneontology/reasoner/OWLExtendedReasoner.java b/src/main/java/org/geneontology/reasoner/OWLExtendedReasoner.java
index 57cd3cb..3b84006 100644
--- a/src/main/java/org/geneontology/reasoner/OWLExtendedReasoner.java
+++ b/src/main/java/org/geneontology/reasoner/OWLExtendedReasoner.java
@@ -5,6 +5,8 @@
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLObjectProperty;
+import org.semanticweb.owlapi.model.OWLObjectSomeValuesFrom;
+import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
import org.semanticweb.owlapi.reasoner.ClassExpressionNotInProfileException;
import org.semanticweb.owlapi.reasoner.FreshEntitiesException;
import org.semanticweb.owlapi.reasoner.InconsistentOntologyException;
@@ -20,8 +22,24 @@
*/
public interface OWLExtendedReasoner extends OWLReasoner {
+ /**
+ * Materialize expressions involving the property p
+ *
+ * Different implementations may handle this differently. Currently the only implementation generates
+ * SomeValuesFrom expressions
+ *
+ * @param p
+ */
+ public void materializeExpressions(OWLObjectProperty p);
+
/**
- * Note that this is not a standard reasoner method.
+ * Finds all superclasses of a class expression ce, where the returned
+ * classes may be either (a) a named (non-anonymous) superClass or (b)
+ * an anomymous class expression, typically of the form "P SOME Y"
+ *
+ *
+ * Note that this is not a standard reasoner method. The standard
+ * OWLReasoner API provides getSuperClasses, corresponding to (a) above.
*
* @param ce
* @param direct
@@ -57,4 +75,6 @@ public Set getSuperClassesOver(OWLClassExpression ce,
ReasonerInterruptedException, TimeOutException;
+
+
}
diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties
new file mode 100644
index 0000000..d1e9d6b
--- /dev/null
+++ b/src/main/resources/log4j.properties
@@ -0,0 +1,9 @@
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%d %-5p (%c{1}:%L) %m\n
+log4j.rootLogger=INFO, console
+
+# silence the reasoner warnings
+log4j.logger.org.semanticweb.elk = ERROR
+log4j.logger.org.obolibrary.obo2owl = ERROR
+
diff --git a/src/test/java/org/geneontology/reasoner/ExpressionMaterializingReasonerTest.java b/src/test/java/org/geneontology/reasoner/ExpressionMaterializingReasonerTest.java
index fafafc2..a4e4809 100644
--- a/src/test/java/org/geneontology/reasoner/ExpressionMaterializingReasonerTest.java
+++ b/src/test/java/org/geneontology/reasoner/ExpressionMaterializingReasonerTest.java
@@ -5,88 +5,207 @@
import java.io.File;
import java.util.Set;
+import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.semanticweb.elk.owlapi.ElkReasonerFactory;
import org.semanticweb.owlapi.apibinding.OWLManager;
+import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
+import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLObjectProperty;
+import org.semanticweb.owlapi.model.OWLObjectSomeValuesFrom;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyManager;
+import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
public class ExpressionMaterializingReasonerTest {
+ private static Logger LOG = Logger.getLogger(ExpressionMaterializingReasoner.class);
+
private OWLOntology ontology = null;
- private ExpressionMaterializingReasonerFactory elkFactory = null;
- private ExpressionMaterializingReasonerFactory hermitFactory = null;
- private ExpressionMaterializingReasoner elkReasoner = null;
- private ExpressionMaterializingReasoner hermitReasoner = null;
+ private ExpressionMaterializingReasonerFactory elkEmrFactory = null;
+ private ExpressionMaterializingReasonerFactory hermitEmrFactory = null;
+ private ExpressionMaterializingReasoner elkExtReasoner = null;
+ private ExpressionMaterializingReasoner hermitExtReasoner = null;
+ private OWLDataFactory owlDataFactory;
@Before
public void before() throws Exception {
OWLOntologyManager m = OWLManager.createOWLOntologyManager();
ontology = m.loadOntologyFromOntologyDocument(new File("src/test/resources","neuron.owl"));
- elkFactory = new ExpressionMaterializingReasonerFactory(new ElkReasonerFactory());
+ elkEmrFactory = new ExpressionMaterializingReasonerFactory(new ElkReasonerFactory());
OWLReasonerFactory hermit = new org.semanticweb.HermiT.ReasonerFactory();
- hermitFactory = new ExpressionMaterializingReasonerFactory(hermit);
- elkReasoner = elkFactory.createReasoner(ontology);
- hermitReasoner = hermitFactory.createReasoner(ontology);
+ hermitEmrFactory = new ExpressionMaterializingReasonerFactory(hermit);
+ elkExtReasoner = elkEmrFactory.createReasoner(ontology);
+ hermitExtReasoner = hermitEmrFactory.createReasoner(ontology);
+ owlDataFactory = ontology.getOWLOntologyManager().getOWLDataFactory();
}
@After
public void after() throws Exception {
- if (elkReasoner != null) {
- elkReasoner.dispose();
+ if (elkExtReasoner != null) {
+ elkExtReasoner.dispose();
}
- if (hermitReasoner != null) {
- hermitReasoner.dispose();
+ if (hermitExtReasoner != null) {
+ hermitExtReasoner.dispose();
}
- elkFactory = null;
- hermitFactory = null;
+ elkEmrFactory = null;
+ hermitEmrFactory = null;
ontology = null;
}
- // what we expect with Elk -
- // the following should be the only direct results for this class-property pair when direct is set
- //
// true =
// true =
// true =
// true =
- @Test
+ /**
+ * This test should run in <1s easily
+ *
+ * We set a timeout of 2000ms, to check for regression errors that lead to loss of efficiency
+ *
+ * @throws Exception
+ */
+ @Test(timeout=2000)
public void test1() throws Exception {
// step 1: materialize expressions, defaults to all
- elkReasoner.materializeExpressions();
+ // AUTOMATIC NOW: elkExtReasoner.rewriteRangeAxioms();
+ elkExtReasoner.materializeExpressions();
boolean[] bools = {true, false};
+ boolean okSvfTest1 = false;
// step 2: check inferences for all classes (or subset)
for(OWLClass cls : ontology.getClassesInSignature()) {
for (OWLObjectProperty p : ontology.getObjectPropertiesInSignature()) {
for (boolean isDirect : bools) {
- Set parents = elkReasoner.getSuperClassesOver(cls,p, isDirect);
+ Set parents = elkExtReasoner.getSuperClassesOver(cls,p, isDirect);
for (OWLClass par : parents) {
- // sorry Heiko...
- System.out.println(cls + " " + p + " "+isDirect + " = "+par);
+ //logger.info(cls + " " + p + " "+isDirect + " = "+par);
}
+ Set svfparents =
+ elkExtReasoner.getSuperClassExpressionsForGCI(cls, p, isDirect);
+ for (OWLClass par : parents) {
+ String s = p +" SOME " + cls + " SubClassOf: " +
+ p + " SOME " + par;
+ LOG.debug("SVF: " + s +" DIRECT: "+isDirect);
+ if (s.equals(" SOME SubClassOf: "+
+ " SOME ")) {
+ okSvfTest1 = true;
+ }
+ }
+
+
}
}
}
+ testSuperClassExpression(true, "mushroom-body", "overlaps", "cell");
+ testSuperClassExpression(true, "mushroom-body", "part-of", "nervous-system");
+ testSuperClassExpression(false, "neuron", "overlaps", "mushroom-body");
+
+
+ //assertTrue("SVF test failed", okSvfTest1);
+ Set axs = ExtenderReasonerUtils.getInferredSubClassOfGCIAxioms(elkExtReasoner);
+
+ for (OWLSubClassOfAxiom ax : axs) {
+ LOG.debug("AX: "+ax);
+ }
+ testSVFSubsumption(true, "part-of", "Purkinje-cell", "overlaps", "nervous-system", axs);
+ testSVFSubsumption(false, "overlaps", "Purkinje-cell", "part-of", "nervous-system", axs);
+ testSVFSubsumption(false, "enables", "Purkinje-cell", "part-of", "organism", axs);
+ testSVFSubsumptionEmpty("enables", "Purkinje-cell", axs);
+ testSVFSubsumptionEmpty("is-active-in", "apoptosis", axs);
+ testSVFSubsumption(true, "enables", "receptor-activity-in-apoptosis", "involved-in", "apoptosis", axs);
+ testSVFSubsumption(true, "enables", "receptor-activity-in-apoptosis", "acts-upstream-of-or-within", "apoptosis", axs);
+ testSVFSubsumption(false, "enables", "receptor-activity-in-apoptosis", "acts-upstream-of", "apoptosis", axs);
+ testSVFSubsumption(true, "acts-upstream-of", "receptor-activity-in-apoptosis", "acts-upstream-of", "receptor-activity-in-apoptosis", axs);
+ testSVFSubsumption(true, "acts-upstream-of", "receptor-activity-in-apoptosis", "acts-upstream-of", "receptor-activity", axs);
+ testSVFSubsumption(true, "acts-upstream-of", "receptor-activity-in-apoptosis", "acts-upstream-of-or-within", "receptor-activity-in-apoptosis", axs);
+ testSVFSubsumption(true, "acts-upstream-of", "receptor-activity-in-apoptosis", "acts-upstream-of-or-within", "apoptosis", axs);
+ testSVFSubsumption(false, "acts-upstream-of", "receptor-activity-in-apoptosis", "involved-in", "apoptosis", axs);
+ testSVFSubsumption(true, "involved-in", "regulation-of-apoptosis", "involved-in-regulation-of", "apoptosis", axs);
+ testSVFSubsumption(false, "involved-in", "regulation-of-apoptosis", "involved-in", "apoptosis", axs);
+
+ // this requires swrl
+ // todo: allow passing in chain of relations
+ //testSVFSubsumption(true, "involved-in-regulation-of", "regulation-of-apoptosis", "involved-in-regulation-of", "apoptosis", axs);
+
+ testSVFSubsumption(false, "involved-in-regulation-of", "regulation-of-apoptosis", "involved-in", "apoptosis", axs);
+ }
+
+ private void testSuperClassExpression(boolean isTrue, String c,
+ String p, String filler) {
+ OWLObjectSomeValuesFrom svf = getSVF(p, filler);
+ boolean ok = false;
+ for ( OWLClassExpression x : elkExtReasoner.getSuperClassExpressions(getClass(c), false) ) {
+ if (x.equals(svf)) {
+ ok = true;
+ }
+ }
+ assertTrue(ok == isTrue);
+ }
+
+ private void testSVFSubsumptionEmpty(String p1, String c1, Set axs) {
+ OWLObjectSomeValuesFrom svf1 = getSVF(p1, c1);
+ Set supers =
+ elkExtReasoner.getSomeValuesFromSuperClasses(getClass(c1), getProperty(p1), false, true);
+ assertTrue("expected empty", supers.size() == 0);
}
+
+ private void testSVFSubsumption(boolean isTrue, String p1, String c1,
+ String p2, String c2, Set axs) {
+ OWLObjectSomeValuesFrom svf1 = getSVF(p1, c1);
+ OWLObjectSomeValuesFrom svf2 = getSVF(p2, c2);
+
+ LOG.info("Expect "+svf1+ " SubClassOf "+svf2+" IS "+isTrue);
+ // first test: dynamic call
+ boolean ok1 = false;
+ for (OWLObjectSomeValuesFrom svf : elkExtReasoner.getSomeValuesFromSuperClasses(getClass(c1), getProperty(p1), false, true)) {
+ if (svf.equals(svf2))
+ ok1 = true;
+ }
+ assertTrue("could not find "+svf2, ok1 == isTrue);
+
+ // second test: check map
+ boolean ok2 = false;
+ for (OWLSubClassOfAxiom ax : axs) {
+ if (ax.getSubClass().equals(svf1)) {
+ if (ax.getSuperClass().equals(svf2)) {
+ ok2 = true;
+ }
+ }
+ }
+ //assertTrue("could not find "+svf2, ok2 == isTrue);
+ }
+
+ // too slow, do not run
//@Test
- public void test2() throws Exception {
+ public void testCompareHermitVsElk() throws Exception {
// step 1: materialize expressions, defaults to all
- elkReasoner.materializeExpressions();
- hermitReasoner.materializeExpressions();
+ elkExtReasoner.materializeExpressions();
+ hermitExtReasoner.materializeExpressions();
// step 2: check inferences for all classes (or subset)
for(OWLClass cls : ontology.getClassesInSignature()) {
- Set elkSuperClassExpressions = elkReasoner.getSuperClassExpressions(cls, true);
- Set hermitSuperClassExpressions = hermitReasoner.getSuperClassExpressions(cls, true);
+ Set elkSuperClassExpressions = elkExtReasoner.getSuperClassExpressions(cls, true);
+ Set hermitSuperClassExpressions = hermitExtReasoner.getSuperClassExpressions(cls, true);
// we expect Elk *not* to entail overlaps based on inverse axioms (neuron overlaps neuron)
//assertEquals("Check that the inferences are the same for cls: "+cls.getIRI(), hermitSuperClassExpressions, elkSuperClassExpressions);
}
}
+
+ // utils
+ private OWLClass getClass(String id) {
+ return owlDataFactory.getOWLClass(IRI.create("http://x.org/"+id));
+ }
+ private OWLObjectProperty getProperty(String id) {
+ return owlDataFactory.getOWLObjectProperty(IRI.create("http://x.org/"+id));
+ }
+
+ private OWLObjectSomeValuesFrom getSVF(String p, String c) {
+ return owlDataFactory.getOWLObjectSomeValuesFrom(getProperty(p), getClass(c));
+ }
+
}
diff --git a/src/test/resources/goprops.owl b/src/test/resources/goprops.owl
new file mode 100644
index 0000000..233e74e
--- /dev/null
+++ b/src/test/resources/goprops.owl
@@ -0,0 +1,17 @@
+[Typedef]
+id: RO:0002331 ! involved in
+
+[Typedef]
+id: RO:0002428 ! involved in regulation of
+
+[Typedef]
+id: RO:0002263 ! acts upstream of
+
+[Typedef]
+id: RO:0002333 ! enabled by
+
+[Typedef]
+id: BFO:0000050 ! part of
+
+[Typedef]
+id: RO:0002131 ! overlaps
diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties
index 6fb525e..d1e9d6b 100644
--- a/src/test/resources/log4j.properties
+++ b/src/test/resources/log4j.properties
@@ -1,6 +1,9 @@
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d %-5p (%c{1}:%L) %m\n
+log4j.rootLogger=INFO, console
-log4j.rootLogger=ERROR, console
+# silence the reasoner warnings
+log4j.logger.org.semanticweb.elk = ERROR
+log4j.logger.org.obolibrary.obo2owl = ERROR
diff --git a/src/test/resources/neuron.owl b/src/test/resources/neuron.owl
index e875c16..a602289 100644
--- a/src/test/resources/neuron.owl
+++ b/src/test/resources/neuron.owl
@@ -1,21 +1,38 @@
+
+
+ for testing EMR
+
+
-
-
-
-
-]>
+
+
-
-
+
+
+
+
+
+
+
+
+
+
@@ -30,12 +47,59 @@
+
+
+
+
+ cjm
+ 2018-04-29T00:02:33Z
+ acts-upstream-of
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T18:28:22Z
+ acts-upstream-of-or-within
+
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T18:25:22Z
+ enables
+
+
+
+
-
+
@@ -43,6 +107,7 @@
+
@@ -51,9 +116,72 @@
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T18:27:49Z
+ involved-in
+
+
+
+
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T18:28:11Z
+ involved-in-regulation-of
+
+
+
+
+
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T18:23:22Z
+ is-active-in
+
+
+
+
+
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T18:25:37Z
+ occurs-in
+
+
+
+
+
@@ -65,8 +193,43 @@
-
+
+
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T18:27:59Z
+ regulates
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T18:32:38Z
+ upstream-of
+
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T18:32:48Z
+ upstream-of-or-within
@@ -82,6 +245,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -110,8 +292,8 @@
- MSC part-parent is cerebellum
A fictitous class for testing
+ MSC part-parent is cerebellum
@@ -124,6 +306,17 @@
+
+
+
+
+ cjm
+ 2018-04-28T18:30:44Z
+ apoptosis
+
+
+
+
@@ -135,6 +328,7 @@
+
@@ -148,6 +342,7 @@
+
@@ -169,6 +364,7 @@
+
@@ -200,6 +396,7 @@
+
@@ -213,6 +410,7 @@
+
@@ -229,9 +427,39 @@
+
+
+
+
+ cjm
+ 2018-04-28T22:00:44Z
+ continuant
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T18:31:42Z
+ execution phase of apoptosis
+
@@ -246,6 +474,7 @@
+
@@ -280,9 +509,8 @@
- redundant
-
+
@@ -293,6 +521,7 @@
+ redundant
@@ -323,15 +552,15 @@
- This is an example of a redundant axiom
-
+
+ This is an example of a redundant axiom
@@ -344,6 +573,17 @@
+
+
+
+
+ cjm
+ 2018-04-28T22:01:21Z
+ molecular-entity
+
+
+
+
@@ -355,18 +595,13 @@
+
-
-
-
-
-
-
@@ -383,11 +618,16 @@
+
+
+
+
+
+
- redundant
-
+
@@ -398,6 +638,7 @@
+ redundant
@@ -405,14 +646,11 @@
+
-
-
-
-
-
-
+
+
@@ -423,14 +661,18 @@
-
-
+
+
+
+
+
+
-
-
+
+
@@ -440,6 +682,17 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -468,12 +721,25 @@
+
+
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T17:27:48Z
+ organism part
@@ -486,6 +752,68 @@
+
+
+
+ cjm
+ 2018-04-28T18:30:09Z
+ process
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T23:49:07Z
+ receptor-activity
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T23:49:33Z
+ receptor-activity-in-apoptosis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ cjm
+ 2018-04-28T18:31:02Z
+ 2018-04-28T23:54:26Z
+ regulation of apoptosis
+ regulation-of-apoptosis
+
+
+
+
@@ -519,48 +847,48 @@
-->
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
-
-
+
diff --git a/src/test/resources/props.owl b/src/test/resources/props.owl
new file mode 100644
index 0000000..37e9a43
--- /dev/null
+++ b/src/test/resources/props.owl
@@ -0,0 +1,16 @@
+Ontology(
+Declaration(ObjectProperty())
+Declaration(ObjectProperty())
+Declaration(ObjectProperty())
+Declaration(ObjectProperty())
+Declaration(ObjectProperty())
+Declaration(ObjectProperty())
+Declaration(ObjectProperty())
+Declaration(ObjectProperty())
+Declaration(ObjectProperty())
+Declaration(ObjectProperty())
+Declaration(ObjectProperty())
+Declaration(ObjectProperty())
+Declaration(ObjectProperty())
+Declaration(ObjectProperty())
+)