All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.ikm.reasoner.hybrid.StatementClassifier Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
package dev.ikm.reasoner.hybrid;

import java.util.List;
import java.util.Set;

import org.semanticweb.elk.owl.interfaces.ElkClass;
import org.semanticweb.elk.owl.interfaces.ElkObject;
import org.semanticweb.elk.owl.iris.ElkFullIri;
import org.semanticweb.elk.owl.managers.ElkObjectEntityRecyclingFactory;
import org.semanticweb.elk.owlapi.ElkConverter;
import org.semanticweb.elk.owlapi.ElkReasoner;
import org.semanticweb.elk.owlapi.wrapper.OwlConverter;
import org.semanticweb.elk.reasoner.completeness.Incompleteness;
import org.semanticweb.elk.reasoner.taxonomy.ConcurrentClassTaxonomy;
import org.semanticweb.elk.reasoner.taxonomy.impl.NonBottomGenericTaxonomyNode;
import org.semanticweb.elk.reasoner.taxonomy.model.TaxonomyNode;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class StatementClassifier {

	private static final Logger log = LoggerFactory.getLogger(StatementClassifier.class);

	private static boolean debug = false;

	private OWLOntology kernelOntology;
	private ElkReasoner kernelElkReasoner;
	private OWLOntology statementOntology;
	private OWLReasoner statementOwlReasoner;
	private HybridReasonerProperties properties;

	private ElkObject.Factory kernelElkObjectFactory;
	private ConcurrentClassTaxonomy kernelTaxonomy;

	private SubsumptionNormalFormBuilder subsumptionNormalFormBuilder;
	private HybridSubsumptionTester subsumptionTester;

	private int countClassified = 0;

	public StatementClassifier(OWLOntology kernelOntology, ElkReasoner kernelElkReasoner, OWLOntology statementOntology,
			OWLReasoner statementOwlReasoner, HybridReasonerProperties properties) throws Exception {
		this.kernelOntology = kernelOntology;
		this.kernelElkReasoner = kernelElkReasoner;
		this.statementOntology = statementOntology;
		this.statementOwlReasoner = statementOwlReasoner;
		this.properties = properties;
		kernelElkObjectFactory = new ElkObjectEntityRecyclingFactory();
		kernelTaxonomy = (ConcurrentClassTaxonomy) Incompleteness
				.getValue(kernelElkReasoner.getInternalReasoner().getTaxonomy());
		createSubsumptionNormalFormBuilder();
		subsumptionTester = new HybridSubsumptionTester(kernelOntology, properties.temporalAnnotationOwlIri());
	}

	private void createSubsumptionNormalFormBuilder() {
		subsumptionNormalFormBuilder = new SubsumptionNormalFormBuilder(kernelOntology, kernelElkReasoner,
				statementOntology, statementOwlReasoner, properties);
		subsumptionNormalFormBuilder.init();
		subsumptionNormalFormBuilder.generate();
	}

	public void classifyStatementConcepts() throws Exception {
		if (debug) {
			log.info("ORIGINAL KERNEL REASONER TAXONOMY - IN CLASSIFIER - BEFORE REMOVAL OF STATEMENT-CONCEPT");
			ReasonerExplorer.printCurrentReasonerTaxonomy(kernelElkReasoner, false);
		}
		String statementConceptIri = properties.statementConcept();
		ElkFullIri iri = new ElkFullIri(properties.statementConcept());
		ElkClass kernelStatementConcept = kernelElkObjectFactory.getClass(iri);
		NonBottomGenericTaxonomyNode.Projection kernelStatementNode = kernelTaxonomy
				.getNonBottomNode(kernelStatementConcept);
		if (kernelStatementNode == null)
			throw new Exception("Not found: " + statementConceptIri);
		if (kernelStatementNode.equals(kernelTaxonomy.getTopNode()))
			throw new Exception("isTopNode " + kernelStatementNode);
		if (kernelStatementNode.equals(kernelTaxonomy.getBottomNode()))
			throw new Exception("isBottomNode " + kernelStatementNode);
		// Remove statement hierarchy from kernelTaxonomy, keeping statementNode
		removeTaxonomySubTree(kernelStatementNode, kernelTaxonomy, false);
		if (debug) {
			log.info("ORIGINAL KERNEL REASONER TAXONOMY - IN CLASSIFIER - AFTER REMOVAL OF STATEMENT-CONCEPT");
			ReasonerExplorer.printCurrentReasonerTaxonomy((ElkReasoner) kernelElkReasoner, false);
		}
		// Iterate through each statement concept from the statement hierarchy and
		// correctly classify it in the kernel hierarchy, below the statementConcept
		for (OWLClass candidateClass : getStatementClassificationConcepts()) {
			ElkClass candidateElkClass = OwlConverter.getInstance().convert(candidateClass);
			NonBottomGenericTaxonomyNode.Projection kernelCandidateNode = kernelTaxonomy
					.getCreateNode(List.of(candidateElkClass));
			SubsumptionNormalForm candidateSNF = subsumptionNormalFormBuilder.getSNF(candidateClass);
			if (debug)
				log.info("Classify: " + kernelCandidateNode.getCanonicalMember().getIri());
			classifyStatementConcept(kernelCandidateNode, candidateSNF, kernelStatementNode);
			if (debug) {
				if (++countClassified % 10 == 0)
					log.info("Classified " + countClassified);
			}
		}
	}

	private void removeTaxonomySubTree(NonBottomGenericTaxonomyNode.Projection node,
			ConcurrentClassTaxonomy taxonomy, boolean removeNode) {
		for (NonBottomGenericTaxonomyNode.Projection childOfTaxonomyNode : Set
				.copyOf(node.getDirectNonBottomSubNodes())) {
			// perform depth-first removal of all nodes in sub-tree, i.e., remove
			// descendants before disconnecting/removing this node
			removeTaxonomySubTree(childOfTaxonomyNode, taxonomy, true);
		}
		if (removeNode) {
			for (NonBottomGenericTaxonomyNode.Projection superNode : Set
					.copyOf(node.getDirectNonBottomSuperNodes())) {
				superNode.removeDirectSubNode(node);
				node.removeDirectSuperNode(superNode);
			}
			taxonomy.removeNode(node.getCanonicalMember());
		}
	}

	private boolean classifyStatementConcept(NonBottomGenericTaxonomyNode.Projection candidateNode,
			SubsumptionNormalForm candidateSNF, NonBottomGenericTaxonomyNode.Projection predicateNode) {
		boolean successfullyClassified = false;
		OWLClass predicateClass = ElkConverter.getInstance().convert(predicateNode.getCanonicalMember());
		SubsumptionNormalForm predicateSNF = subsumptionNormalFormBuilder.getSNF(predicateClass);
		if (debug) {
			log.info("Testing subsumption (MostSpecificSubsumers)=> Candidate: "
					+ candidateNode.getCanonicalMember().getIri() + "  Predicate: "
					+ predicateNode.getCanonicalMember().getIri());
		}
		if (subsumptionTester.isSubsumedBy(candidateSNF, predicateSNF, kernelElkReasoner, statementOwlReasoner)
				&& !candidateNode.equals(predicateNode)) {
			if (debug)
				log.info("       Answer:  YES");
			for (NonBottomGenericTaxonomyNode.Projection predicateChildNode : predicateNode
					.getDirectNonBottomSubNodes()) {
				// Recurses here
				if (classifyStatementConcept(candidateNode, candidateSNF, predicateChildNode)) {
					successfullyClassified = true;
				}
			}
			if (successfullyClassified == false) {
				// candidate was not subsumed by any of the predicates' descendants,
				// so connect it as a new child of the predicate itself
				if (!nodeAlreadyConnectedTo(candidateNode, predicateNode)) {
					// Could occur if the candidateNode can arrive at the predicateNode via multiple
					// paths (i.e., if multiple inheritance exists)
					// connect candidate to predicate, then go on to find most general subsumees of
					// the candidate node
					connectNodes(candidateNode, predicateNode);
					if (debug)
						log.info(" ==> Classifying " + candidateNode.getCanonicalMember().getIri() + " below "
								+ predicateNode.getCanonicalMember().getIri());
					findAndConnectMostGeneralSubsumees(candidateNode, candidateSNF, predicateNode);
					// We know that, if any exist, they must be descendants of the predicate node
				} else {
					if (debug)
						log.info(" ==> Not Connecting " + candidateNode.getCanonicalMember().getIri()
								+ " already connected to " + predicateNode.getCanonicalMember().getIri());

				}
			}
			return true;
		} else {
			if (debug)
				log.info("       Answer:  NO");
			return false;
		}
	}

	private void findAndConnectMostGeneralSubsumees(NonBottomGenericTaxonomyNode.Projection predicateNode,
			SubsumptionNormalForm predicateSNF,
			NonBottomGenericTaxonomyNode.Projection parentCandidateNode) {
		for (NonBottomGenericTaxonomyNode.Projection candidateNode : Set
				.copyOf(parentCandidateNode.getDirectNonBottomSubNodes())) {
			// for each child of the parentCandidateNode, check if it is
			// subsumed by the predicateNode
//			if (candidateNode instanceof UpdateableBottomNode) {
			// Do nothing and exit; no need to connect the bottom node to the predicate node
			// -- it already has a bottom node below it by default if it has no children
//			} else {
			OWLClass candidateClass = ElkConverter.getInstance().convert(candidateNode.getCanonicalMember());
			SubsumptionNormalForm candidateSNF = subsumptionNormalFormBuilder.getSNF(candidateClass);
			if (debug)
				log.info("Testing subsumption (MostGeneralSubsumees) => Candidate: "
						+ candidateNode.getCanonicalMember().getIri() + "  Predicate: "
						+ predicateNode.getCanonicalMember().getIri());
			// Check if the current child of parentCandidateNode is subsumed by the
			// predicateNode
			// Only want properly subsumed concepts, not self-subsumed
			if (subsumptionTester.isSubsumedBy(candidateSNF, predicateSNF, kernelElkReasoner, statementOwlReasoner)
					&& !candidateNode.equals(predicateNode)) {
				if (debug)
					log.info("       Answer:  YES");
				// Another path may already exist from predicateNode to candidateNode if
				// multiple inheritance
				if (!nodeAlreadySubsumedBy(candidateNode, predicateNode)) {
					connectNodes(candidateNode, predicateNode);
					// Want to connect the *top-most* subsumee, so don't recurse below here
					if (debug)
						log.info(" ==> Classifying " + predicateNode.getCanonicalMember().getIri() + " above "
								+ candidateNode.getCanonicalMember().getIri());
				}
				if (predicateNodeAlsoHasParentCandidateNodeAsASuperClass(predicateNode, parentCandidateNode)) {
					// i.e., if candidateNode and predicateNode are currently siblings
					// the existing link provides no additional information and is redundant
					disconnectNodes(candidateNode, parentCandidateNode);
					// the existing link provides no additional information and is redundant
					if (debug)
						log.info(" ==> Disconnecting link [ONE] between " + candidateNode.getCanonicalMember().getIri()
								+ " and its parent " + parentCandidateNode.getCanonicalMember().getIri());
				}
				for (NonBottomGenericTaxonomyNode.Projection candidateChildNode : candidateNode
						.getDirectNonBottomSubNodes()) {
					// for each child of the parentCandidateNode, check if it is subsumed by the
					// predicateNode
					if (predicateNodeAlsoHasCandidatesChildNodeAsASubClass(predicateNode, candidateChildNode)) {
						// i.e., if candidateNode and predicateNode are currently siblings
						disconnectNodes(candidateChildNode, predicateNode);
						// the existing link provides no additional information and is redundant
						if (debug)
							log.info(" ==> Disconnecting link [TWO] between "
									+ candidateChildNode.getCanonicalMember().getIri() + " and its parent "
									+ predicateNode.getCanonicalMember().getIri());
					}
				}
			} else {
				if (debug)
					log.info("       Answer:  NO");
				findAndConnectMostGeneralSubsumees(predicateNode, predicateSNF, candidateNode);
				// if the current child of parentCandidateNode is not subsumed by the
				// predicateNode, recurse to the current child's children and continue looking
			}
		}
	}

	private void connectNode(NonBottomGenericTaxonomyNode.Projection node,
			List> superNodeList) {
		for (NonBottomGenericTaxonomyNode.Projection superNode : superNodeList) {
			superNode.addDirectSubNode(node);
			node.addDirectSuperNode(superNode);
		}
	}

	private void connectNodes(NonBottomGenericTaxonomyNode.Projection subNode,
			NonBottomGenericTaxonomyNode.Projection superNode) {
		superNode.addDirectSubNode(subNode);
		subNode.addDirectSuperNode(superNode);

	}

	private void disconnectNodes(NonBottomGenericTaxonomyNode.Projection subNode,
			NonBottomGenericTaxonomyNode.Projection superNode) {
		superNode.removeDirectSubNode(subNode);
		subNode.removeDirectSuperNode(superNode);

	}

	private boolean predicateNodeAlsoHasParentCandidateNodeAsASuperClass(TaxonomyNode predicateNode,
			TaxonomyNode parentCandidateNode) {
		if (predicateNode.getDirectSuperNodes().contains(parentCandidateNode)) {
			return true;
		} else
			return false;
	}

	private boolean predicateNodeAlsoHasCandidatesChildNodeAsASubClass(TaxonomyNode predicateNode,
			TaxonomyNode candidateChildNode) {
		if (predicateNode.getDirectSubNodes().contains(candidateChildNode)) {
			return true;
		} else
			return false;
	}

	private boolean nodeAlreadyConnectedTo(TaxonomyNode candidateNode, TaxonomyNode predicateNode) {
		if (candidateNode.getDirectSuperNodes().contains(predicateNode)) {
			return true;
		} else
			return false;
	}

	private boolean nodeAlreadySubsumedBy(TaxonomyNode candidateNode, TaxonomyNode predicateNode) {
		if (candidateNode.getAllSuperNodes().contains(predicateNode)) {
			return true;
		} else
			return false;
	}

	public Set getStatementClassificationConcepts() {
		OWLDataFactory factory = statementOntology.getOWLOntologyManager().getOWLDataFactory();
		OWLClass statementConcept = factory.getOWLClass(IRI.create(properties.statementConcept()));
		Set ret = statementOwlReasoner.getSubClasses(statementConcept, false).getFlattened();
		ret.remove(factory.getOWLNothing());
		return ret;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy