-
Notifications
You must be signed in to change notification settings - Fork 25
Extracting the Canonical Model
ELK works by deriving conclusions of the ontology using certain inference rules, and, it is, theoretically, possible to extract a canonical model of the ontology from the closure (also known as saturation) under these inference rules. Constructing of such a model using the ELK reasoner is one of the common user questions.
A canonical model of the ontology is a model that satisfies all and only logical consequences of the ontology of the certain form, for example, subsumptions between atomic concepts, or between concepts appearing in the ontology. The canonical model construction is described in detail in Section 3.3 of the ELK Paper. While the provided definition of the canonical model is rather simple, it is not easy to extract the required information from the internal ELK data structures because that task was not the main focus of the reasoner. For simple ontology that use only EL syntax, i.e., only concept conjunctions and existential restrictions, this task can be accomplished relatively easy.
Below is a Java program that computes the canonical model of an EL ontology as an (ABox) ontology, in which concept assertions represent interpretation of atomic concept, and role assertions represent interpretation of roles.
Note that to compile, the file should be placed within the package org.semanticweb.elk.reasoner.stages
.
Warning! The following Example uses some internal ELK API, which is subject to change. The example was tested with ELK v.0.6.0 and OWL API v.5.1.20.
package org.semanticweb.elk.reasoner.stages;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.semanticweb.elk.exceptions.ElkException;
import org.semanticweb.elk.owlapi.ElkConverter;
import org.semanticweb.elk.owlapi.ElkReasoner;
import org.semanticweb.elk.owlapi.ElkReasonerFactory;
import org.semanticweb.elk.reasoner.ElkInconsistentOntologyException;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClass;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClassExpression;
import org.semanticweb.elk.reasoner.indexing.model.IndexedContextRoot;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectProperty;
import org.semanticweb.elk.reasoner.saturation.SaturationState;
import org.semanticweb.elk.reasoner.saturation.context.Context;
import org.semanticweb.elk.reasoner.saturation.context.SubContextPremises;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.formats.ManchesterSyntaxDocumentFormat;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLIndividual;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLOntologyStorageException;
/**
* This example shows how to extract a EL canonical model from the saturation
* computed by the ELK reasoner.
*/
public class CanonicalModelExtraction {
public static void main(String[] args) throws OWLOntologyCreationException,
ElkInconsistentOntologyException, ElkException,
OWLOntologyStorageException {
// Prerequisites:
// We need OWL manager and factory from OWL API
OWLOntologyManager man = OWLManager.createOWLOntologyManager();
OWLDataFactory df = man.getOWLDataFactory();
// We need a converter from ELK objects to OWL Objects
ElkConverter cnv = ElkConverter.getInstance();
// Load your ontology.
OWLOntology ont = man.loadOntologyFromOntologyDocument(
new File("/Users/ecull/Ontologies/pizza.owl"));
// We are going to represent the canonical model by a set of concept and
// role assertions, e.g., by an ABox:
OWLOntology model = man.createOntology();
// Create an ELK reasoner.
ElkReasonerFactory reasonerFactory = new ElkReasonerFactory();
// Notice: not OWLReasoner!
ElkReasoner reasoner = reasonerFactory.createReasoner(ont);
// Trigger some reasoning task to compute the saturation.
// Note that they can be different saturations, and therefore
// different "canonical" models for different reasoning tasks.
// Usually the canonical model is computed for the result of
// classification. However, ELK reasoner is lazy, so it
// will not compute reasoning results before they are explicitly
// requested. Therefore, we need to force computation of the
// taxonomy using the "internal" reasoner state.
// Get internal reasoner
AbstractReasonerState elk = reasoner.getInternalReasoner();
// Force computation of the taxonomy
elk.getTaxonomy();
// Saturation represents the computed closure under inference rules
// It is only package-visible, so this code should be placed
// inside the package org.semanticweb.elk.reasoner.stages
SaturationState<? extends Context> saturation = elk.saturationState;
// Saturation is partitioned on "contexts", which, intuitively,
// correspond to left-hand sides of derived subsumptions.
// In EL canonical models each such context correspond to a model
// element, which we are going to represent by OWL individuals.
// We assign each context to a fresh individual using a simple factory
// (see below).
ContextToIndividuals c2Ind = new ContextToIndividuals(df);
for (Context c : saturation.getContexts()) {
// Subsumers correspond to right-hand sides of the derived
// subsumptions (where the left-hand side is the context).
// They can be "composed", when derived from its subconcepts,
// or "decomposed", when derived from concepts containing them.
// For further details see documentation of classes
// ComposedSubsumer and DecomposedSubsumer.
// For canonical model, we are interested in subsumers which are,
// atomic classes, so they cannot be "composed".
for (IndexedClassExpression sub : c.getDecomposedSubsumers()) {
if (!(sub instanceof IndexedClass)) {
// skip complex concepts
continue;
}
IndexedClass asub = (IndexedClass) sub;
// In canonical model, each subsumer results in a concept
// assertion, that represents that an individual belongs
// to the interpretation of the atomic concept.
model.add(df.getOWLClassAssertionAxiom(
cnv.convert(asub.getElkEntity()), c2Ind.getInd(c)));
}
// Interpretation of roles correspond to derived subsumptions with
// existential restrictions on the right. They are stored in
// saturation as "forward" and "backward" links between contexts.
// Semantically, they both represent the same information, but with
// different access patterns. For further details, see documentation
// of classes SubClassInclusionComposed and
// SubClassInclusionDecomposed.
// Forward links are only created if the role in existential
// restriction appears in role compositions, so we extract this
// information from backward links.
// This is going to be a little adventurous as this information is
// buried under several levels of abstraction.
// After all, saturation is designed to efficiently store derived
// conclusions, but retrieving (all) of them is unnecessary for
// computing the reasoning results. For details, see documentation
// of ConclusionSet.
for (Entry<IndexedObjectProperty, ? extends SubContextPremises> e : c
.getSubContextPremisesByObjectProperty().entrySet()) {
// List the roles involved in backward links with the context
IndexedObjectProperty op = e.getKey();
// All context related by this property with the current context
// are grouped together in "linked roots".
for (IndexedContextRoot r : e.getValue().getLinkedRoots()) {
Context d = r.getContext(); // a linked context
// Finally, we can create the corresponding role assertion.
model.add(df.getOWLObjectPropertyAssertionAxiom(
cnv.convert(op.getElkEntity()), c2Ind.getInd(d),
c2Ind.getInd(c)));
}
}
}
// Finally, we can print out model, e.g., in Manchester syntax
// but one can clearly visualized the model more nicely
man.setOntologyFormat(model, new ManchesterSyntaxDocumentFormat());
man.saveOntology(model, System.out);
// That is, in principle, all that is needed for the EL ontology.
// If the ontology contains other kinds of axioms, such as (complex)
// role inclusions, reflexive roles, role ranges, etc.,
// more work would be required to construct the canonical model.
}
/**
* Assigning context to fresh individuals
*/
static class ContextToIndividuals {
private final OWLDataFactory df;
private final Map<Context, OWLIndividual> map = new HashMap<>();
ContextToIndividuals(OWLDataFactory df) {
this.df = df;
}
public OWLIndividual getInd(Context c) {
OWLIndividual result = map.get(c);
if (result != null) {
return result;
}
// else assign fresh individual
// for simplicity, we use fresh anonymous individuals
result = df.getOWLAnonymousIndividual();
map.put(c, result);
return result;
}
}
}
- About ELK
- ELK User Pages
- ELK Developer Pages
- Feedback