/* Copyright 2008, 2009, 2010 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.tableau; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.semanticweb.HermiT.model.Atom; import org.semanticweb.HermiT.model.AtomicConcept; import org.semanticweb.HermiT.model.AtomicRole; import org.semanticweb.HermiT.model.DLClause; import org.semanticweb.HermiT.model.DLPredicate; import org.semanticweb.HermiT.model.NodeIDLessEqualThan; import org.semanticweb.HermiT.model.NodeIDsAscendingOrEqual; import org.semanticweb.HermiT.model.Term; import org.semanticweb.HermiT.model.Variable; /** * Applies the rules during the expansion of a tableau. */ public final class HyperresolutionManager implements Serializable { private static final long serialVersionUID=-4880817508962130189L; protected final ExtensionManager m_extensionManager; protected final ExtensionTable.Retrieval[] m_deltaOldRetrievals; protected final ExtensionTable.Retrieval m_binaryTableRetrieval; protected final Map<DLPredicate,CompiledDLClauseInfo> m_tupleConsumersByDeltaPredicate; protected final Map<AtomicRole,CompiledDLClauseInfo> m_atomicRoleTupleConsumersUnguarded; protected final HashMap<AtomicRole,Map<AtomicConcept,CompiledDLClauseInfo>> m_atomicRoleTupleConsumersByGuardConcept1; protected final HashMap<AtomicRole,Map<AtomicConcept,CompiledDLClauseInfo>> m_atomicRoleTupleConsumersByGuardConcept2; protected final Object[][] m_buffersToClear; protected final UnionDependencySet[] m_unionDependencySetsToClear; protected final Object[] m_valuesBuffer; protected final int m_maxNumberOfVariables; public HyperresolutionManager(Tableau tableau,Set<DLClause> dlClauses) { InterruptFlag interruptFlag=tableau.m_interruptFlag; m_extensionManager=tableau.m_extensionManager; m_tupleConsumersByDeltaPredicate=new HashMap<DLPredicate,CompiledDLClauseInfo>(); m_atomicRoleTupleConsumersUnguarded=new HashMap<AtomicRole,CompiledDLClauseInfo>(); m_atomicRoleTupleConsumersByGuardConcept1=new HashMap<AtomicRole,Map<AtomicConcept,CompiledDLClauseInfo>>(); m_atomicRoleTupleConsumersByGuardConcept2=new HashMap<AtomicRole,Map<AtomicConcept,CompiledDLClauseInfo>>(); // Index DL clauses by body Map<DLClauseBodyKey,List<DLClause>> dlClausesByBody=new HashMap<DLClauseBodyKey,List<DLClause>>(); for (DLClause dlClause : dlClauses) { DLClauseBodyKey key=new DLClauseBodyKey(dlClause); List<DLClause> dlClausesForKey=dlClausesByBody.get(key); if (dlClausesForKey==null) { dlClausesForKey=new ArrayList<DLClause>(); dlClausesByBody.put(key,dlClausesForKey); } dlClausesForKey.add(dlClause); interruptFlag.checkInterrupt(); } // Compile the DL clauses Map<Integer,ExtensionTable.Retrieval> retrievalsByArity=new HashMap<Integer,ExtensionTable.Retrieval>(); DLClauseEvaluator.BufferSupply bufferSupply=new DLClauseEvaluator.BufferSupply(); Map<Term,Node> noTermsToNodes=Collections.emptyMap(); DLClauseEvaluator.ValuesBufferManager valuesBufferManager=new DLClauseEvaluator.ValuesBufferManager(dlClauses,noTermsToNodes); DLClauseEvaluator.GroundDisjunctionHeaderManager groundDisjunctionHeaderManager=new DLClauseEvaluator.GroundDisjunctionHeaderManager(); Map<Integer,UnionDependencySet> unionDependencySetsBySize=new HashMap<Integer,UnionDependencySet>(); ArrayList<Atom> guardingAtomicConceptAtoms1=new ArrayList<Atom>(); ArrayList<Atom> guardingAtomicConceptAtoms2=new ArrayList<Atom>(); for (Map.Entry<DLClauseBodyKey,List<DLClause>> entry : dlClausesByBody.entrySet()) { DLClause bodyDLClause=entry.getKey().m_dlClause; BodyAtomsSwapper bodyAtomsSwapper=new BodyAtomsSwapper(bodyDLClause); for (int bodyAtomIndex=0;bodyAtomIndex<bodyDLClause.getBodyLength();++bodyAtomIndex) if (isPredicateWithExtension(bodyDLClause.getBodyAtom(bodyAtomIndex).getDLPredicate())) { DLClause swappedDLClause=bodyAtomsSwapper.getSwappedDLClause(bodyAtomIndex); Atom deltaAtom=swappedDLClause.getBodyAtom(0); DLPredicate deltaDLPredicate=deltaAtom.getDLPredicate(); Integer arity=Integer.valueOf(deltaDLPredicate.getArity()+1); ExtensionTable.Retrieval firstTableRetrieval=retrievalsByArity.get(arity); if (firstTableRetrieval==null) { ExtensionTable extensionTable=m_extensionManager.getExtensionTable(arity.intValue()); firstTableRetrieval=extensionTable.createRetrieval(new boolean[extensionTable.getArity()],ExtensionTable.View.DELTA_OLD); retrievalsByArity.put(arity,firstTableRetrieval); } DLClauseEvaluator evaluator=new DLClauseEvaluator(tableau,swappedDLClause,entry.getValue(),firstTableRetrieval,bufferSupply,valuesBufferManager,groundDisjunctionHeaderManager,unionDependencySetsBySize); CompiledDLClauseInfo normalTupleConsumer=new CompiledDLClauseInfo(evaluator,m_tupleConsumersByDeltaPredicate.get(deltaDLPredicate)); m_tupleConsumersByDeltaPredicate.put(deltaDLPredicate,normalTupleConsumer); if (deltaDLPredicate instanceof AtomicRole && deltaAtom.getArgument(0) instanceof Variable && deltaAtom.getArgument(1) instanceof Variable) { AtomicRole deltaAtomicRole=(AtomicRole)deltaDLPredicate; getAtomicRoleClauseGuards(swappedDLClause,guardingAtomicConceptAtoms1,guardingAtomicConceptAtoms2); if (!guardingAtomicConceptAtoms1.isEmpty()) { Map<AtomicConcept,CompiledDLClauseInfo> compiledDLClauseInfos=m_atomicRoleTupleConsumersByGuardConcept1.get(deltaAtomicRole); if (compiledDLClauseInfos==null) { compiledDLClauseInfos=new HashMap<AtomicConcept,CompiledDLClauseInfo>(); m_atomicRoleTupleConsumersByGuardConcept1.put(deltaAtomicRole,compiledDLClauseInfos); } for (Atom guardingAtom : guardingAtomicConceptAtoms1) { AtomicConcept atomicConcept=(AtomicConcept)guardingAtom.getDLPredicate(); CompiledDLClauseInfo optimizedTupleConsumer=new CompiledDLClauseInfo(evaluator,compiledDLClauseInfos.get(atomicConcept)); compiledDLClauseInfos.put(atomicConcept,optimizedTupleConsumer); } } if (!guardingAtomicConceptAtoms2.isEmpty()) { Map<AtomicConcept,CompiledDLClauseInfo> compiledDLClauseInfos=m_atomicRoleTupleConsumersByGuardConcept2.get(deltaAtomicRole); if (compiledDLClauseInfos==null) { compiledDLClauseInfos=new HashMap<AtomicConcept,CompiledDLClauseInfo>(); m_atomicRoleTupleConsumersByGuardConcept2.put(deltaAtomicRole,compiledDLClauseInfos); } for (Atom guardingAtom : guardingAtomicConceptAtoms2) { AtomicConcept atomicConcept=(AtomicConcept)guardingAtom.getDLPredicate(); CompiledDLClauseInfo optimizedTupleConsumer=new CompiledDLClauseInfo(evaluator,compiledDLClauseInfos.get(atomicConcept)); compiledDLClauseInfos.put(atomicConcept,optimizedTupleConsumer); } } if (guardingAtomicConceptAtoms1.isEmpty() && guardingAtomicConceptAtoms2.isEmpty()) { CompiledDLClauseInfo unguardedTupleConsumer=new CompiledDLClauseInfo(evaluator,m_atomicRoleTupleConsumersUnguarded.get(deltaAtomicRole)); m_atomicRoleTupleConsumersUnguarded.put(deltaAtomicRole,unguardedTupleConsumer); } } bufferSupply.reuseBuffers(); interruptFlag.checkInterrupt(); } } m_deltaOldRetrievals=new ExtensionTable.Retrieval[retrievalsByArity.size()]; retrievalsByArity.values().toArray(m_deltaOldRetrievals); m_binaryTableRetrieval=m_extensionManager.getExtensionTable(2).createRetrieval(new boolean[] { false,true },ExtensionTable.View.EXTENSION_THIS); m_buffersToClear=bufferSupply.getAllBuffers(); m_unionDependencySetsToClear=new UnionDependencySet[unionDependencySetsBySize.size()]; unionDependencySetsBySize.values().toArray(m_unionDependencySetsToClear); m_valuesBuffer=valuesBufferManager.m_valuesBuffer; m_maxNumberOfVariables=valuesBufferManager.m_maxNumberOfVariables; } protected void getAtomicRoleClauseGuards(DLClause swappedDLClause,List<Atom> guardingAtomicConceptAtoms1,List<Atom> guardingAtomicConceptAtoms2) { guardingAtomicConceptAtoms1.clear(); guardingAtomicConceptAtoms2.clear(); Atom deltaOldAtom=swappedDLClause.getBodyAtom(0); Variable X=deltaOldAtom.getArgumentVariable(0); Variable Y=deltaOldAtom.getArgumentVariable(1); for (int bodyIndex=1;bodyIndex<swappedDLClause.getBodyLength();bodyIndex++) { Atom atom=swappedDLClause.getBodyAtom(bodyIndex); if (atom.getDLPredicate() instanceof AtomicConcept) { Variable variable=atom.getArgumentVariable(0); if (variable!=null) { if (X.equals(variable)) guardingAtomicConceptAtoms1.add(atom); if (Y.equals(variable)) guardingAtomicConceptAtoms2.add(atom); } } bodyIndex++; } } protected boolean isPredicateWithExtension(DLPredicate dlPredicate) { return !NodeIDLessEqualThan.INSTANCE.equals(dlPredicate) && !(dlPredicate instanceof NodeIDsAscendingOrEqual); } public void clear() { for (int retrievalIndex=m_deltaOldRetrievals.length-1;retrievalIndex>=0;--retrievalIndex) m_deltaOldRetrievals[retrievalIndex].clear(); m_binaryTableRetrieval.clear(); for (int bufferIndex=m_buffersToClear.length-1;bufferIndex>=0;--bufferIndex) { Object[] buffer=m_buffersToClear[bufferIndex]; for (int index=buffer.length-1;index>=0;--index) buffer[index]=null; } for (int unionDependencySetIndex=m_unionDependencySetsToClear.length-1;unionDependencySetIndex>=0;--unionDependencySetIndex) { DependencySet[] dependencySets=m_unionDependencySetsToClear[unionDependencySetIndex].m_dependencySets; for (int dependencySetIndex=dependencySets.length-1;dependencySetIndex>=0;--dependencySetIndex) dependencySets[dependencySetIndex]=null; } for (int variableIndex=0;variableIndex<m_maxNumberOfVariables;variableIndex++) m_valuesBuffer[variableIndex]=null; } public void applyDLClauses() { for (int index=0;index<m_deltaOldRetrievals.length;index++) { ExtensionTable.Retrieval deltaOldRetrieval=m_deltaOldRetrievals[index]; deltaOldRetrieval.open(); Object[] deltaOldTupleBuffer=deltaOldRetrieval.getTupleBuffer(); while (!deltaOldRetrieval.afterLast() && !m_extensionManager.containsClash()) { Object deltaOldPredicate=deltaOldTupleBuffer[0]; CompiledDLClauseInfo unoptimizedCompiledDLClauseInfo=m_tupleConsumersByDeltaPredicate.get(deltaOldPredicate); boolean applyUnoptimized=true; if (unoptimizedCompiledDLClauseInfo!=null && deltaOldTupleBuffer[0] instanceof AtomicRole) { CompiledDLClauseInfo unguardedCompiledDLClauseInfo=m_atomicRoleTupleConsumersUnguarded.get(deltaOldPredicate); if (unoptimizedCompiledDLClauseInfo.m_indexInList>((Node)deltaOldTupleBuffer[1]).getNumberOfPositiveAtomicConcepts()+((Node)deltaOldTupleBuffer[2]).getNumberOfPositiveAtomicConcepts()+(unguardedCompiledDLClauseInfo==null ? 0 : unguardedCompiledDLClauseInfo.m_indexInList)) { applyUnoptimized=false; while (unguardedCompiledDLClauseInfo!=null && !m_extensionManager.containsClash()) { unguardedCompiledDLClauseInfo.m_evaluator.evaluate(); unguardedCompiledDLClauseInfo=unguardedCompiledDLClauseInfo.m_next; } if (!m_extensionManager.containsClash()) { Map<AtomicConcept,CompiledDLClauseInfo> compiledDLClauseInfos=m_atomicRoleTupleConsumersByGuardConcept1.get(deltaOldPredicate); if (compiledDLClauseInfos!=null) { m_binaryTableRetrieval.getBindingsBuffer()[1]=deltaOldTupleBuffer[1]; m_binaryTableRetrieval.open(); Object[] binaryTableTupleBuffer=m_binaryTableRetrieval.getTupleBuffer(); while (!m_binaryTableRetrieval.afterLast() && !m_extensionManager.containsClash()) { Object atomicConceptObject=binaryTableTupleBuffer[0]; if (atomicConceptObject instanceof AtomicConcept) { CompiledDLClauseInfo optimizedCompiledDLClauseInfo=compiledDLClauseInfos.get(atomicConceptObject); while (optimizedCompiledDLClauseInfo!=null && !m_extensionManager.containsClash()) { optimizedCompiledDLClauseInfo.m_evaluator.evaluate(); optimizedCompiledDLClauseInfo=optimizedCompiledDLClauseInfo.m_next; } } m_binaryTableRetrieval.next(); } } } if (!m_extensionManager.containsClash()) { Map<AtomicConcept,CompiledDLClauseInfo> compiledDLClauseInfos=m_atomicRoleTupleConsumersByGuardConcept2.get(deltaOldPredicate); if (compiledDLClauseInfos!=null) { m_binaryTableRetrieval.getBindingsBuffer()[1]=deltaOldTupleBuffer[2]; m_binaryTableRetrieval.open(); Object[] binaryTableTupleBuffer=m_binaryTableRetrieval.getTupleBuffer(); while (!m_binaryTableRetrieval.afterLast() && !m_extensionManager.containsClash()) { Object atomicConceptObject=binaryTableTupleBuffer[0]; if (atomicConceptObject instanceof AtomicConcept) { CompiledDLClauseInfo optimizedCompiledDLClauseInfo=compiledDLClauseInfos.get(atomicConceptObject); while (optimizedCompiledDLClauseInfo!=null && !m_extensionManager.containsClash()) { optimizedCompiledDLClauseInfo.m_evaluator.evaluate(); optimizedCompiledDLClauseInfo=optimizedCompiledDLClauseInfo.m_next; } } m_binaryTableRetrieval.next(); } } } } } if (applyUnoptimized) { while (unoptimizedCompiledDLClauseInfo!=null && !m_extensionManager.containsClash()) { unoptimizedCompiledDLClauseInfo.m_evaluator.evaluate(); unoptimizedCompiledDLClauseInfo=unoptimizedCompiledDLClauseInfo.m_next; } } deltaOldRetrieval.next(); } } } protected static final class CompiledDLClauseInfo { protected final DLClauseEvaluator m_evaluator; protected final CompiledDLClauseInfo m_next; protected final int m_indexInList; public CompiledDLClauseInfo(DLClauseEvaluator evaluator,CompiledDLClauseInfo next) { m_evaluator=evaluator; m_next=next; if (m_next==null) m_indexInList=1; else m_indexInList=m_next.m_indexInList+1; } } public static final class BodyAtomsSwapper { protected final DLClause m_dlClause; protected final List<Atom> m_nodeIDComparisonAtoms; protected final boolean[] m_usedAtoms; protected final List<Atom> m_reorderedAtoms; protected final Set<Variable> m_boundVariables; public BodyAtomsSwapper(DLClause dlClause) { m_dlClause=dlClause; m_nodeIDComparisonAtoms=new ArrayList<Atom>(m_dlClause.getBodyLength()); m_usedAtoms=new boolean[m_dlClause.getBodyLength()]; m_reorderedAtoms=new ArrayList<Atom>(m_dlClause.getBodyLength()); m_boundVariables=new HashSet<Variable>(); } public DLClause getSwappedDLClause(int bodyIndex) { m_nodeIDComparisonAtoms.clear(); for (int index=m_usedAtoms.length-1;index>=0;--index) { m_usedAtoms[index]=false; Atom atom=m_dlClause.getBodyAtom(index); if (NodeIDLessEqualThan.INSTANCE.equals(atom.getDLPredicate())) m_nodeIDComparisonAtoms.add(atom); } m_reorderedAtoms.clear(); m_boundVariables.clear(); Atom atom=m_dlClause.getBodyAtom(bodyIndex); atom.getVariables(m_boundVariables); m_reorderedAtoms.add(atom); m_usedAtoms[bodyIndex]=true; while (m_reorderedAtoms.size()!=m_usedAtoms.length) { Atom bestAtom=null; int bestAtomIndex=-1; int bestAtomGoodness=-1000; for (int index=m_usedAtoms.length-1;index>=0;--index) if (!m_usedAtoms[index]) { atom=m_dlClause.getBodyAtom(index); int atomGoodness=getAtomGoodness(atom); if (atomGoodness>bestAtomGoodness) { bestAtom=atom; bestAtomGoodness=atomGoodness; bestAtomIndex=index; } } m_reorderedAtoms.add(bestAtom); m_usedAtoms[bestAtomIndex]=true; bestAtom.getVariables(m_boundVariables); m_nodeIDComparisonAtoms.remove(bestAtom); } Atom[] bodyAtoms=new Atom[m_reorderedAtoms.size()]; m_reorderedAtoms.toArray(bodyAtoms); return m_dlClause.getChangedDLClause(null,bodyAtoms); } protected int getAtomGoodness(Atom atom) { if (NodeIDLessEqualThan.INSTANCE.equals(atom.getDLPredicate())) { if (m_boundVariables.contains(atom.getArgumentVariable(0)) && m_boundVariables.contains(atom.getArgumentVariable(1))) return 1000; else return -2000; } else if (atom.getDLPredicate() instanceof NodeIDsAscendingOrEqual) { int numberOfUnboundVariables=0; for (int argumentIndex=atom.getArity()-1;argumentIndex>=0;--argumentIndex) { Term argument=atom.getArgument(argumentIndex); if (argument instanceof Variable) { if (!m_boundVariables.contains(argument)) numberOfUnboundVariables++; } } if (numberOfUnboundVariables>0) return -5000; else return 5000; } else { int numberOfBoundVariables=0; int numberOfUnboundVariables=0; for (int argumentIndex=atom.getArity()-1;argumentIndex>=0;--argumentIndex) { Term argument=atom.getArgument(argumentIndex); if (argument instanceof Variable) { if (m_boundVariables.contains(argument)) numberOfBoundVariables++; else numberOfUnboundVariables++; } } int goodness=numberOfBoundVariables*100-numberOfUnboundVariables*10; if (atom.getDLPredicate().getArity()==2 && numberOfUnboundVariables==1 && !m_nodeIDComparisonAtoms.isEmpty()) { Variable unboundVariable=atom.getArgumentVariable(0); if (m_boundVariables.contains(unboundVariable)) unboundVariable=atom.getArgumentVariable(1); // At this point, unboundVariable must be really unbound because // we have already established that numberOfUnboundVariables==1. for (int compareAtomIndex=m_nodeIDComparisonAtoms.size()-1;compareAtomIndex>=0;--compareAtomIndex) { Atom compareAtom=m_nodeIDComparisonAtoms.get(compareAtomIndex); Variable argument0=compareAtom.getArgumentVariable(0); Variable argument1=compareAtom.getArgumentVariable(1); if ((m_boundVariables.contains(argument0) || unboundVariable.equals(argument0)) && (m_boundVariables.contains(argument1) || unboundVariable.equals(argument1))) { goodness+=5; break; } } } return goodness; } } } protected static final class DLClauseBodyKey { protected final DLClause m_dlClause; protected final int m_hashCode; public DLClauseBodyKey(DLClause dlClause) { m_dlClause=dlClause; int hashCode=0; for (int atomIndex=0;atomIndex<m_dlClause.getBodyLength();atomIndex++) hashCode+=m_dlClause.getBodyAtom(atomIndex).hashCode(); m_hashCode=hashCode; } public boolean equals(Object that) { if (this==that) return true; DLClause thatDLClause=((DLClauseBodyKey)that).m_dlClause; if (m_dlClause.getBodyLength()!=thatDLClause.getBodyLength()) return false; for (int atomIndex=0;atomIndex<m_dlClause.getBodyLength();atomIndex++) if (!m_dlClause.getBodyAtom(atomIndex).equals(thatDLClause.getBodyAtom(atomIndex))) return false; return true; } public int hashCode() { return m_hashCode; } } }