org.obolibrary.robot.ExtractOperation Maven / Gradle / Ivy
package org.obolibrary.robot;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.renci.relationgraph.RelationGraph.Config;
import org.renci.relationgraph.RelationGraphUtil;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.*;
import org.semanticweb.owlapi.model.parameters.Imports;
import org.semanticweb.owlapi.search.EntitySearcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.ac.manchester.cs.owl.owlapi.OWLDataFactoryImpl;
import uk.ac.manchester.cs.owlapi.modularity.ModuleType;
import uk.ac.manchester.cs.owlapi.modularity.SyntacticLocalityModuleExtractor;
/**
* Extract a set of OWLEntities from the input ontology to an output ontology. Uses the OWLAPI's
* SyntacticLocalityModuleExtractor (SLME).
*
* @author James A. Overton
*/
public class ExtractOperation {
/** Logger. */
private static final Logger logger = LoggerFactory.getLogger(ExtractOperation.class);
/** Shared data factory. */
private static OWLDataFactory dataFactory = new OWLDataFactoryImpl();
/** RDFS isDefinedBy annotation property. */
private static OWLAnnotationProperty isDefinedBy = dataFactory.getRDFSIsDefinedBy();
/** Namespace for errors. */
private static final String NS = "extract#";
/** Error message when an invalid intermediates opiton is provided. */
private static final String unknownIntermediatesError =
NS + "UNKNOWN INTERMEDIATES ERROR '%s' is not a valid --intermediates arg";
/** Error message when an invalid argument is passed to --individuals. */
private static final String unknownIndividualsError =
NS + "UNKNOWN INDIVIDUALS ERROR %s is not a valid --individuals argument";
/**
* Return a map from option name to default option value.
*
* @return a map with default values for all available options
*/
public static Map getDefaultOptions() {
Map options = new HashMap<>();
options.put("individuals", "include");
options.put("imports", "include");
options.put("copy-ontology-annotations", "false");
options.put("annotate-with-source", "false");
options.put("intermediates", "all");
options.put("force", "false");
return options;
}
/**
* Extract a set of terms from an ontology using the OWLAPI's SyntacticLocalityModuleExtractor
* (SLME) with default options. The input ontology is not changed.
*
* @param inputOntology the ontology to extract from
* @param terms a set of IRIs for terms to extract
* @param outputIRI the OntologyIRI of the new ontology
* @param moduleType determines the type of extraction; defaults to STAR
* @return a new ontology (with a new manager)
* @throws OWLOntologyCreationException on any OWLAPI problem
*/
public static OWLOntology extract(
OWLOntology inputOntology, Set terms, IRI outputIRI, ModuleType moduleType)
throws OWLOntologyCreationException {
return extract(inputOntology, terms, outputIRI, moduleType, getDefaultOptions(), null);
}
public static OWLOntology extract(
OWLOntology inputOntology,
Set terms,
IRI outputIRI,
ModuleType moduleType,
Map options)
throws OWLOntologyCreationException {
return extract(inputOntology, terms, outputIRI, moduleType, options, null);
}
/**
* Extract a set of terms from an ontology using the OWLAPI's SyntacticLocalityModuleExtractor
* (SLME). The input ontology is not changed.
*
* @param inputOntology the ontology to extract from
* @param terms a set of IRIs for terms to extract
* @param outputIRI the OntologyIRI of the new ontology
* @param moduleType determines the type of extraction; defaults to STAR
* @param options map of extract options
* @param sourceMap map of term IRI to source IRI, or null (only used with annotate-with-source)
* @return a new ontology (with a new manager)
* @throws OWLOntologyCreationException on any OWLAPI problem
*/
public static OWLOntology extract(
OWLOntology inputOntology,
Set terms,
IRI outputIRI,
ModuleType moduleType,
Map options,
Map sourceMap)
throws OWLOntologyCreationException {
if (options == null) {
options = getDefaultOptions();
}
String intermediates = OptionsHelper.getOption(options, "intermediates", "all");
String individuals = OptionsHelper.getOption(options, "individuals", "include");
boolean excludeInstances;
if (individuals.equalsIgnoreCase("exclude")
|| individuals.equalsIgnoreCase("minimal")
|| individuals.equalsIgnoreCase("definitions")) {
excludeInstances = true;
} else if (individuals.equalsIgnoreCase("include")) {
excludeInstances = false;
} else {
throw new IllegalArgumentException(String.format(unknownIndividualsError, individuals));
}
String importsString = OptionsHelper.getOption(options, "imports", "include");
Imports imports;
if ("include".equalsIgnoreCase(importsString)) {
imports = Imports.INCLUDED;
} else {
imports = Imports.EXCLUDED;
}
logger.debug("Extracting...");
Set entities = new HashSet<>();
for (IRI term : terms) {
entities.addAll(inputOntology.getEntitiesInSignature(term, imports));
}
// Default moduleType is STAR
ModuleType type = moduleType;
if (type == null) {
type = ModuleType.STAR;
}
// Get all axioms from the ontology
Set axs = new HashSet<>(inputOntology.getAxioms());
if (imports.equals(Imports.INCLUDED)) {
// Maybe get the axioms from the imported ontologies
for (OWLOntology importedOnt : inputOntology.getImportsClosure()) {
axs.addAll(importedOnt.getAxioms());
}
}
// Maybe get an IRI
IRI ontIRI = inputOntology.getOntologyID().getOntologyIRI().orNull();
SyntacticLocalityModuleExtractor extractor =
new SyntacticLocalityModuleExtractor(
inputOntology.getOWLOntologyManager(), ontIRI, axs, type, excludeInstances);
// Create the output with the extracted terms
OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
OWLOntology outputOntology = manager.createOntology(extractor.extract(entities), outputIRI);
// Maybe add the axioms belonging to individuals of class types included
if (individuals.equalsIgnoreCase("minimal")) {
addMinimalIndividualAxioms(inputOntology, outputOntology, imports);
} else if (individuals.equalsIgnoreCase("definitions")) {
addDefinitionIndividualAxioms(inputOntology, outputOntology, imports);
} else if ("exclude".equalsIgnoreCase(individuals)) {
// Make sure to completely remove individuals
Set indivs = new HashSet<>(outputOntology.getIndividualsInSignature());
Set indivAxioms =
RelatedObjectsHelper.getCompleteAxioms(outputOntology, indivs, null, true);
manager.removeAxioms(outputOntology, indivAxioms);
}
// Maybe copy ontology annotations
boolean copyOntologyAnnotations =
OptionsHelper.optionIsTrue(options, "copy-ontology-annotations");
if (copyOntologyAnnotations) {
for (OWLAnnotation annotation : inputOntology.getAnnotations()) {
OntologyHelper.addOntologyAnnotation(outputOntology, annotation);
}
}
// Maybe annotate entities with rdfs:isDefinedBy
if (OptionsHelper.optionIsTrue(options, "annotate-with-source")) {
annotateWithSource(sourceMap, outputOntology, manager);
}
// Determine what to do based on intermediates
if ("all".equalsIgnoreCase(intermediates)) {
return outputOntology;
} else if ("none".equalsIgnoreCase(intermediates)) {
removeIntermediates(outputOntology, entities);
return outputOntology;
} else if ("minimal".equalsIgnoreCase(intermediates)) {
OntologyHelper.collapseOntology(outputOntology, terms);
return outputOntology;
} else {
throw new IllegalArgumentException(String.format(unknownIntermediatesError, intermediates));
}
}
/**
* Annotates entities of the outputOntology with rdfs:isDefinedBy.
*
* @param sourceMap map of term IRI to source IRI, or null.
* @param outputOntology output ontology.
* @param manager OWL ontology manager.
*/
private static void annotateWithSource(
Map sourceMap, OWLOntology outputOntology, OWLOntologyManager manager) {
Set sourceAxioms = new HashSet<>();
for (OWLEntity entity : OntologyHelper.getEntities(outputOntology)) {
// Check if rdfs:isDefinedBy already exists
Set existingValues =
OntologyHelper.getAnnotationValues(outputOntology, isDefinedBy, entity.getIRI());
if (existingValues == null || existingValues.size() == 0) {
// If not, add it
OWLAnnotationAxiom def = getIsDefinedBy(entity, sourceMap);
if (def != null) {
sourceAxioms.add(def);
}
}
}
manager.addAxioms(outputOntology, sourceAxioms);
}
/**
* Extracts a materialized sub-ontology from the given ontology that only contains the given terms
* and the relations between them. The input ontology is not changed.
*
* @param inputOntology the ontology to extract from
* @param terms a set of IRIs for terms to extract
* @param outputIRI the OntologyIRI of the new ontology
* @param options map of extract options
* @param sourceMap map of term IRI to source IRI, or null (only used with annotate-with-source)
* @param imports handle imports option (default: include)
* @return a new ontology (with a new manager)
* @throws OWLOntologyCreationException on any OWLAPI problem
*/
public static OWLOntology extractSubset(
OWLOntology inputOntology,
Set terms,
IRI outputIRI,
Map options,
Map sourceMap,
Imports imports)
throws OWLOntologyCreationException {
Set relations =
terms.stream()
.filter(t -> inputOntology.containsObjectPropertyInSignature(t, imports))
.collect(Collectors.toSet());
OWLOntology materializedOnt = materialize(inputOntology, relations, outputIRI);
OWLOntology filteredOnt = filter(materializedOnt, terms, outputIRI);
copyPropertyAnnotations(inputOntology, filteredOnt);
ReduceOperation.reduce(filteredOnt, new org.semanticweb.elk.owlapi.ElkReasonerFactory());
// Maybe annotate entities with rdfs:isDefinedBy
if (OptionsHelper.optionIsTrue(options, "annotate-with-source")) {
annotateWithSource(sourceMap, filteredOnt, OWLManager.createOWLOntologyManager());
}
return filteredOnt;
}
/**
* Materializes the given ontology using the relation-graph.Creates a new ontology that contains
* the input ontology axioms and the materialized axioms. The input ontology is not changed.
*
* @param ontology ontology to materialize.
* @param relations a set of IRIs for the relations to materialize
* @param outputIRI IRI of the output ontology.
* @return materialized ontology.
* @throws OWLOntologyCreationException
*/
private static OWLOntology materialize(OWLOntology ontology, Set relations, IRI outputIRI)
throws OWLOntologyCreationException {
Config config = new Config(null, true, false, true, true, true, false);
Set materializedAxioms =
RelationGraphUtil.computeRelationGraph(ontology, relations, config);
if (outputIRI == null) {
outputIRI = ontology.getOntologyID().getOntologyIRI().orNull();
}
OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
OWLOntology materializedOntology = manager.createOntology(outputIRI);
manager.addAxioms(materializedOntology, ontology.getAxioms());
manager.addAxioms(materializedOntology, materializedAxioms);
return materializedOntology;
}
/**
* Filters the terms from the given ontology and returns a new filtered ontology. Copies ontology
* annotations, import declarations and the term annotations from the input ontology to the new
* ontology.
*
* @param inputOntology ontology to filter
* @param terms list of term IRIs to filter.
* @param outputIRI IRI of the output ontology.
* @return a filtered new ontology object
* @throws OWLOntologyCreationException
*/
private static OWLOntology filter(OWLOntology inputOntology, Set terms, IRI outputIRI)
throws OWLOntologyCreationException {
Set relatedObjects = new HashSet<>();
relatedObjects.addAll(OntologyHelper.getEntities(inputOntology, terms));
if (outputIRI == null) {
outputIRI = inputOntology.getOntologyID().getOntologyIRI().orNull();
}
OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
OWLOntology outputOntology = manager.createOntology(outputIRI);
List axiomSelectors = new ArrayList<>();
axiomSelectors.add("all");
OWLOntologyManager mng = OWLManager.createOWLOntologyManager();
mng.addAxioms(
outputOntology,
RelatedObjectsHelper.filterAxioms(
inputOntology.getAxioms(),
relatedObjects,
axiomSelectors,
new ArrayList<>(),
false,
true));
// Add annotations on the related objects
manager.addAxioms(
outputOntology, RelatedObjectsHelper.getAnnotationAxioms(inputOntology, relatedObjects));
// Copy the import declarations to the output ontology
for (OWLImportsDeclaration imp : inputOntology.getImportsDeclarations()) {
manager.applyChange(new AddImport(outputOntology, imp));
}
return outputOntology;
}
/**
* Copies annotations of the ObjectProperties, DataProperties, AnnotationProperties and
* NamedIndividuals from source ontology to the target ontology.
*
* @param sourceOntology ontology that contains the annotations.
* @param targetOntology ontology to add annotations.
*/
private static void copyPropertyAnnotations(
OWLOntology sourceOntology, OWLOntology targetOntology) {
OWLOntologyManager targetOntManager = targetOntology.getOWLOntologyManager();
targetOntManager.addAxioms(
targetOntology,
RelatedObjectsHelper.getAnnotationAxioms(
sourceOntology,
targetOntology.getObjectPropertiesInSignature().stream()
.map(prop -> (OWLObject) prop)
.collect(Collectors.toSet())));
targetOntManager.addAxioms(
targetOntology,
RelatedObjectsHelper.getAnnotationAxioms(
sourceOntology,
targetOntology.getAnnotationPropertiesInSignature().stream()
.map(prop -> (OWLObject) prop)
.collect(Collectors.toSet())));
targetOntManager.addAxioms(
targetOntology,
RelatedObjectsHelper.getAnnotationAxioms(
sourceOntology,
targetOntology.getDataPropertiesInSignature().stream()
.map(prop -> (OWLObject) prop)
.collect(Collectors.toSet())));
}
/**
* Given an input ontology, an output ontology, and an Imports specification, copy individuals
* used in logical definitions from the input to the output ontology.
*
* @param inputOntology OWLOntology to copy axioms from
* @param outputOntology OWLOntology to copy axioms to
* @param imports Imports.INCLUDED or Imports.EXCLUDED
*/
private static void addDefinitionIndividualAxioms(
OWLOntology inputOntology, OWLOntology outputOntology, Imports imports) {
Set individuals = new HashSet<>();
Set classes = outputOntology.getClassesInSignature();
for (OWLClass cls : classes) {
for (OWLClassExpression expr : EntitySearcher.getEquivalentClasses(cls, inputOntology)) {
if (!expr.isAnonymous()) {
continue;
}
individuals.addAll(expr.getIndividualsInSignature());
}
for (OWLClassExpression expr : EntitySearcher.getSubClasses(cls, inputOntology)) {
if (!expr.isAnonymous()) {
continue;
}
individuals.addAll(expr.getIndividualsInSignature());
}
}
addIndiviudalsAxioms(inputOntology, outputOntology, individuals, imports);
}
/**
* Given an input ontology and an output ontology, copy any individual axioms and their
* annotations from the input to the output ontology as long as the class type is included in the
* output ontology.
*
* @param inputOntology OWLOntology to copy axioms from
* @param outputOntology OWLOntology to copy axioms to
* @param imports Imports.INCLUDED or Imports.EXCLUDED
*/
private static void addMinimalIndividualAxioms(
OWLOntology inputOntology, OWLOntology outputOntology, Imports imports) {
Set individuals = new HashSet<>();
Set classes = outputOntology.getClassesInSignature();
// Get the individuals for each of the included classes
for (OWLClass cls : classes) {
individuals.addAll(EntitySearcher.getIndividuals(cls, inputOntology));
}
addIndiviudalsAxioms(inputOntology, outputOntology, individuals, imports);
}
/**
* Given an input ontology, an output ontology, a set of individuals, and an Imports
* specification, copy the individual axioms for the set of individuals from the input to the
* output ontology.
*
* @param inputOntology OWLOntology to copy axioms from
* @param outputOntology OWLOntology to copy axioms to
* @param individuals Set of OWLIndividuals to copy axioms for
* @param imports Imports.INCLUDED or Imports.EXCLUDED
*/
private static void addIndiviudalsAxioms(
OWLOntology inputOntology,
OWLOntology outputOntology,
Set individuals,
Imports imports) {
if (imports == null) {
imports = Imports.INCLUDED;
}
Set axioms = new HashSet<>();
for (OWLIndividual individual : individuals) {
if (individual.isNamed()) {
// Add axioms about named individuals (includes assertions)
OWLNamedIndividual namedIndividual = individual.asOWLNamedIndividual();
axioms.addAll(inputOntology.getAnnotationAssertionAxioms(namedIndividual.getIRI()));
axioms.addAll(inputOntology.getAxioms(namedIndividual, imports));
} else {
axioms.addAll(inputOntology.getAxioms(individual, imports));
}
}
outputOntology.getOWLOntologyManager().addAxioms(outputOntology, axioms);
}
/**
* Given an OWLEntity, return an OWLAnnotationAssertionAxiom indicating the source ontology with
* rdfs:isDefinedBy.
*
* @param entity entity to get source of
* @param sourceMap map of term IRI to source IRI, or null
* @return OWLAnnotationAssertionAxiom with rdfs:isDefinedBy as the property
*/
protected static OWLAnnotationAxiom getIsDefinedBy(OWLEntity entity, Map sourceMap) {
String iri = entity.getIRI().toString();
IRI base;
if (sourceMap != null && sourceMap.containsKey(entity.getIRI())) {
// IRI exists in the prefixes
base = sourceMap.get(entity.getIRI());
} else {
// Brute force edit the IRI string
// Warning - this may not work with non-OBO Foundry terms, depending on the IRI format!
if (iri.contains("#")) {
if (iri.contains(".owl#")) {
String baseStr = iri.substring(0, iri.lastIndexOf("#")).toLowerCase();
base = IRI.create(baseStr);
} else {
String baseStr = iri.substring(0, iri.lastIndexOf("#")).toLowerCase() + ".owl";
base = IRI.create(baseStr);
}
} else if (iri.contains("_")) {
String baseStr = iri.substring(0, iri.lastIndexOf("_")).toLowerCase() + ".owl";
base = IRI.create(baseStr);
} else if (iri.contains("/")) {
String baseStr = iri.substring(0, iri.lastIndexOf("/")).toLowerCase() + ".owl";
base = IRI.create(baseStr);
} else {
logger.warn("Unable to get source for IRI " + iri);
return null;
}
}
return dataFactory.getOWLAnnotationAssertionAxiom(isDefinedBy, entity.getIRI(), base);
}
/**
* Given an input ontology, an extracted output ontology, and a set of entities, remove all
* intermediates. This leaves only the classes directly used in the logic of any input entities.
*
* @param outputOntology extracted module
* @param entities Set of extracted entities
*/
private static void removeIntermediates(OWLOntology outputOntology, Set entities) {
Set precious = new HashSet<>();
OWLOntologyManager manager = outputOntology.getOWLOntologyManager();
for (OWLEntity e : entities) {
if (!e.isOWLClass()) {
continue;
}
OWLClass cls = e.asOWLClass();
precious.add(cls);
for (OWLClassExpression expr : EntitySearcher.getSuperClasses(cls, outputOntology)) {
precious.addAll(expr.getClassesInSignature());
}
for (OWLClassExpression expr : EntitySearcher.getEquivalentClasses(cls, outputOntology)) {
precious.addAll(expr.getClassesInSignature());
}
for (OWLClassExpression expr : EntitySearcher.getDisjointClasses(cls, outputOntology)) {
precious.addAll(expr.getClassesInSignature());
}
}
Set removeAxioms =
RelatedObjectsHelper.getPartialAxioms(
outputOntology,
RelatedObjectsHelper.selectClasses(
RelatedObjectsHelper.selectComplement(outputOntology, precious)),
null);
manager.removeAxioms(outputOntology, removeAxioms);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy