/* Copyright 2009 by the Oxford University Computing Laboratory
This file is part of HermiT.
HermiT is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
HermiT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with HermiT. If not, see <http://www.gnu.org/licenses/>.
*/
package org.semanticweb.HermiT.hierarchy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.semanticweb.HermiT.model.Atom;
import org.semanticweb.HermiT.model.AtomicConcept;
import org.semanticweb.HermiT.model.Individual;
import org.semanticweb.HermiT.tableau.ExtensionTable;
import org.semanticweb.HermiT.tableau.Node;
import org.semanticweb.HermiT.tableau.ReasoningTaskDescription;
import org.semanticweb.HermiT.tableau.Tableau;
public class DeterministicClassification {
protected final Tableau m_tableau;
protected final ClassificationProgressMonitor m_progressMonitor;
protected final AtomicConcept m_topElement;
protected final AtomicConcept m_bottomElement;
protected final Set<AtomicConcept> m_elements;
public DeterministicClassification(Tableau tableau,ClassificationProgressMonitor progressMonitor,AtomicConcept topElement,AtomicConcept bottomElement,Set<AtomicConcept> elements) {
m_tableau=tableau;
m_progressMonitor=progressMonitor;
m_topElement=topElement;
m_bottomElement=bottomElement;
m_elements=elements;
}
public Hierarchy<AtomicConcept> classify() {
if (!m_tableau.isDeterministic())
throw new IllegalStateException("Internal error: DeterministicClassificationManager can be used only with a deterministic tableau.");
Individual freshIndividual=Individual.createAnonymous("fresh-individual");
if (!m_tableau.isSatisfiable(true,Collections.singleton(Atom.create(m_topElement,freshIndividual)),null,null,null,null,ReasoningTaskDescription.isConceptSatisfiable(m_topElement)))
return Hierarchy.emptyHierarchy(m_elements,m_topElement,m_bottomElement);
Map<AtomicConcept,GraphNode<AtomicConcept>> allSubsumers=new HashMap<AtomicConcept,GraphNode<AtomicConcept>>();
for (AtomicConcept element : m_elements) {
Set<AtomicConcept> subsumers;
Map<Individual,Node> nodesForIndividuals=new HashMap<Individual,Node>();
nodesForIndividuals.put(freshIndividual,null);
if (!m_tableau.isSatisfiable(true,Collections.singleton(Atom.create(element,freshIndividual)),null,null,null,nodesForIndividuals,ReasoningTaskDescription.isConceptSatisfiable(element)))
subsumers=m_elements;
else {
subsumers=new HashSet<AtomicConcept>();
subsumers.add(m_topElement);
ExtensionTable.Retrieval retrieval=m_tableau.getExtensionManager().getBinaryExtensionTable().createRetrieval(new boolean[] { false,true },ExtensionTable.View.TOTAL);
retrieval.getBindingsBuffer()[1]=nodesForIndividuals.get(freshIndividual).getCanonicalNode();
retrieval.open();
while (!retrieval.afterLast()) {
Object subsumer=retrieval.getTupleBuffer()[0];
if (subsumer instanceof AtomicConcept && m_elements.contains(subsumer))
subsumers.add((AtomicConcept)subsumer);
retrieval.next();
}
}
allSubsumers.put(element,new GraphNode<AtomicConcept>(element,subsumers));
m_progressMonitor.elementClassified(element);
}
return buildHierarchy(m_topElement,m_bottomElement,allSubsumers);
}
public static <T> Hierarchy<T> buildHierarchy(T topElement,T bottomElement,Map<T,GraphNode<T>> graphNodes) {
HierarchyNode<T> topNode=new HierarchyNode<T>(topElement);
HierarchyNode<T> bottomNode=new HierarchyNode<T>(bottomElement);
Hierarchy<T> hierarchy=new Hierarchy<T>(topNode,bottomNode);
// Compute SCCs (strongly connected components), create hierarchy nodes, and topologically order them
List<HierarchyNode<T>> topologicalOrder=new ArrayList<HierarchyNode<T>>();
visit(new Stack<GraphNode<T>>(),new DFSIndex(),graphNodes,graphNodes.get(bottomElement),hierarchy,topologicalOrder);
// Process the nodes in the topological order
Map<HierarchyNode<T>,Set<HierarchyNode<T>>> reachableFrom=new HashMap<HierarchyNode<T>,Set<HierarchyNode<T>>>();
List<GraphNode<T>> allSuccessors=new ArrayList<GraphNode<T>>();
for (int index=0;index<topologicalOrder.size();index++) {
HierarchyNode<T> node=topologicalOrder.get(index);
Set<HierarchyNode<T>> reachableFromNode=new HashSet<HierarchyNode<T>>();
reachableFromNode.add(node);
reachableFrom.put(node,reachableFromNode);
allSuccessors.clear();
for (T element : node.m_equivalentElements) {
GraphNode<T> graphNode=graphNodes.get(element);
for (T successor : graphNode.m_successors) {
GraphNode<T> successorGraphNode=graphNodes.get(successor);
if (successorGraphNode!=null)
allSuccessors.add(successorGraphNode);
}
}
Collections.sort(allSuccessors,TopologicalOrderComparator.INSTANCE);
for (int successorIndex=allSuccessors.size()-1;successorIndex>=0;--successorIndex) {
GraphNode<T> successorGraphNode=allSuccessors.get(successorIndex);
HierarchyNode<T> successorNode=hierarchy.m_nodesByElements.get(successorGraphNode.m_element);
if (!reachableFromNode.contains(successorNode)) {
node.m_parentNodes.add(successorNode);
successorNode.m_childNodes.add(node);
reachableFromNode.add(successorNode);
reachableFromNode.addAll(reachableFrom.get(successorNode));
}
}
}
return hierarchy;
}
protected static <T> void visit(Stack<GraphNode<T>> stack,DFSIndex dfsIndex,Map<T,GraphNode<T>> graphNodes,GraphNode<T> graphNode,Hierarchy<T> hierarchy,List<HierarchyNode<T>> topologicalOrder) {
graphNode.m_dfsIndex=dfsIndex.m_value++;
graphNode.m_SCChead=graphNode;
stack.push(graphNode);
for (T successor : graphNode.m_successors) {
GraphNode<T> successorGraphNode=graphNodes.get(successor);
if (successorGraphNode!=null) {
if (successorGraphNode.notVisited())
visit(stack,dfsIndex,graphNodes,successorGraphNode,hierarchy,topologicalOrder);
if (!successorGraphNode.isAssignedToSCC() && successorGraphNode.m_SCChead.m_dfsIndex<graphNode.m_SCChead.m_dfsIndex)
graphNode.m_SCChead=successorGraphNode.m_SCChead;
}
}
if (graphNode.m_SCChead==graphNode) {
int nextTopologicalOrderIndex=topologicalOrder.size();
Set<T> equivalentElements=new HashSet<T>();
GraphNode<T> poppedNode;
do {
poppedNode=stack.pop();
poppedNode.m_topologicalOrderIndex=nextTopologicalOrderIndex;
equivalentElements.add(poppedNode.m_element);
} while (poppedNode!=graphNode);
HierarchyNode<T> hierarchyNode;
if (equivalentElements.contains(hierarchy.getTopNode().m_representative))
hierarchyNode=hierarchy.getTopNode();
else if (equivalentElements.contains(hierarchy.getBottomNode().m_representative))
hierarchyNode=hierarchy.getBottomNode();
else
hierarchyNode=new HierarchyNode<T>(graphNode.m_element);
for (T element : equivalentElements) {
hierarchyNode.m_equivalentElements.add(element);
hierarchy.m_nodesByElements.put(element,hierarchyNode);
}
topologicalOrder.add(hierarchyNode);
}
}
public static class GraphNode<T> {
public final T m_element;
public final Set<T> m_successors;
public int m_dfsIndex;
public GraphNode<T> m_SCChead;
public int m_topologicalOrderIndex;
public GraphNode(T element,Set<T> successors) {
m_element=element;
m_successors=successors;
m_dfsIndex=-1;
m_SCChead=null;
m_topologicalOrderIndex=-1;
}
public boolean notVisited() {
return m_dfsIndex==-1;
}
public boolean isAssignedToSCC() {
return m_topologicalOrderIndex!=-1;
}
}
protected static class TopologicalOrderComparator implements Comparator<GraphNode<?>> {
public static final TopologicalOrderComparator INSTANCE=new TopologicalOrderComparator();
public int compare(GraphNode<?> o1,GraphNode<?> o2) {
return o1.m_topologicalOrderIndex-o2.m_topologicalOrderIndex;
}
}
protected static class DFSIndex {
public int m_value;
}
}