diff --git a/CHANGELOG.md b/CHANGELOG.md index fbd6bb01e..829fcbc6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bug was fixed that caused logical axioms with axiom annotations not to be processed correctly when merging axiom annotations [#1223] - Correctly merge unannotated and annotated duplicated axioms [#1239] - Fix reading of default queries from embedded Jar resources [#1212] +- Subproperties being ignored when evaluating redundancy in `reduce` [#1014], [#1208] ## [1.9.7] - 2024-10-30 diff --git a/docs/examples/reduced-redundant-over-subproperties.ofn b/docs/examples/reduced-redundant-over-subproperties.ofn new file mode 100644 index 000000000..1718cb8bd --- /dev/null +++ b/docs/examples/reduced-redundant-over-subproperties.ofn @@ -0,0 +1,40 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) + + +############################ +# Classes +############################ + +# Class: () + +SubClassOf( ObjectSomeValuesFrom( )) + +# Class: () + +SubClassOf( ObjectSomeValuesFrom( )) + + +SubObjectPropertyOf(ObjectPropertyChain( ) ) +) \ No newline at end of file diff --git a/docs/examples/redundant-over-subproperties.ofn b/docs/examples/redundant-over-subproperties.ofn new file mode 100644 index 000000000..58d85d816 --- /dev/null +++ b/docs/examples/redundant-over-subproperties.ofn @@ -0,0 +1,42 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) + + +############################ +# Classes +############################ + +# Class: () + +SubClassOf( ObjectSomeValuesFrom( )) +SubClassOf( ObjectSomeValuesFrom( )) + +# Class: () + +SubClassOf( ObjectSomeValuesFrom( )) +SubClassOf( ObjectSomeValuesFrom( )) + + +SubObjectPropertyOf(ObjectPropertyChain( ) ) +) \ No newline at end of file diff --git a/docs/reduce.md b/docs/reduce.md index aef57c257..1f03277d2 100644 --- a/docs/reduce.md +++ b/docs/reduce.md @@ -15,4 +15,23 @@ Available options for `reduce`: ### Warning Reciprocal subclass axioms (e.g. `A SubClassOf B`, `B SubClassOf A`), entailing equivalence between `A` and `B`, may be removed by `reduce`. In this case it is important to -assert an equivalence axiom (`A EquivalentTo B`) using the `reason` command before running reduce. \ No newline at end of file +assert an equivalence axiom (`A EquivalentTo B`) using the `reason` command before running reduce. + +### Subproperties and property chains + +For backwards compatibility reasons, by default subObjectPropertyOf axioms are ignored when evaluating the redundancy of subClassOf axioms. This means that, given the following example: + +``` +C SubClassOf P some D +C SubClassOf Q some D +P SubObjectPropertyOf Q +``` + +the first subClassOf axiom would by default _not_ be considered redundant and therefore _not_ be removed. + +To force the `reduce` command to factor in subproperties (including property chains) when evaluating redundancy, use the `--include-subproperties` option: + + robot reduce --reasoner ELK \ + --input redundant-over-subproperties.ofn \ + --include-subproperties true \ + --output results/reduced-redundant-over-subproperties.ofn \ No newline at end of file diff --git a/robot-command/src/main/java/org/obolibrary/robot/ReduceCommand.java b/robot-command/src/main/java/org/obolibrary/robot/ReduceCommand.java index 1209804bf..38227bf8b 100644 --- a/robot-command/src/main/java/org/obolibrary/robot/ReduceCommand.java +++ b/robot-command/src/main/java/org/obolibrary/robot/ReduceCommand.java @@ -31,6 +31,11 @@ public ReduceCommand() { "preserve annotated axioms when removing redundant subclass axioms"); o.addOption( "c", "named-classes-only", true, "only reduce subclass axioms between named classes"); + o.addOption( + "s", + "include-subproperties", + true, + "take subproperties into account to evaluate redundancy"); o.addOption("i", "input", true, "reduce ontology from a file"); o.addOption("I", "input-iri", true, "reduce ontology from an IRI"); o.addOption("o", "output", true, "save reduceed ontology to a file"); diff --git a/robot-core/src/main/java/org/obolibrary/robot/ReduceOperation.java b/robot-core/src/main/java/org/obolibrary/robot/ReduceOperation.java index 8c511bde4..190717873 100644 --- a/robot-core/src/main/java/org/obolibrary/robot/ReduceOperation.java +++ b/robot-core/src/main/java/org/obolibrary/robot/ReduceOperation.java @@ -55,6 +55,7 @@ public static Map getDefaultOptions() { Map options = new HashMap<>(); options.put("preserve-annotated-axioms", "false"); options.put("named-classes-only", "false"); + options.put("include-subproperties", "false"); return options; } @@ -87,7 +88,9 @@ public static void reduce( if (namedClassesOnly) { reduceNamedOnly(ontology, reasonerFactory, preserveAnnotatedAxioms); } else { - reduceAllClassExpressions(ontology, reasonerFactory, preserveAnnotatedAxioms); + boolean includeSubproperties = OptionsHelper.optionIsTrue(options, "include-subproperties"); + reduceAllClassExpressions( + ontology, reasonerFactory, preserveAnnotatedAxioms, includeSubproperties); } } @@ -97,10 +100,15 @@ public static void reduce( * @param ontology The ontology to reduce. * @param reasonerFactory The reasoner factory to use. * @param preserveAnnotatedAxioms Whether to not remove redundant, but annotated, axioms. + * @param includeSubproperties Whether to include subPropertyOf axioms (including property chains) + * for evaluating redundancy. * @throws OWLOntologyCreationException on ontology problem */ private static void reduceAllClassExpressions( - OWLOntology ontology, OWLReasonerFactory reasonerFactory, boolean preserveAnnotatedAxioms) + OWLOntology ontology, + OWLReasonerFactory reasonerFactory, + boolean preserveAnnotatedAxioms, + boolean includeSubproperties) throws OWLOntologyCreationException { OWLOntologyManager manager = OWLManager.createOWLOntologyManager(); @@ -108,10 +116,15 @@ private static void reduceAllClassExpressions( // we treat an axiom as redundant if its is redundant within the // subClassOf graph, including OP characteristic axioms (e.g. transitivity) + // and subproperty axioms OWLOntology subOntology = manager.createOntology(); for (OWLAxiom a : ontology.getAxioms(Imports.INCLUDED)) { if (a instanceof OWLSubClassOfAxiom || a instanceof OWLObjectPropertyCharacteristicAxiom) { manager.addAxiom(subOntology, a); + } else if (includeSubproperties + && (a instanceof OWLSubObjectPropertyOfAxiom + || a instanceof OWLSubPropertyChainOfAxiom)) { + manager.addAxiom(subOntology, a); } }