/* 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 org.semanticweb.HermiT.model.AtomicConcept; import org.semanticweb.HermiT.model.AtomicNegationConcept; import org.semanticweb.HermiT.model.AtomicRole; import org.semanticweb.HermiT.model.Concept; import org.semanticweb.HermiT.model.DescriptionGraph; import org.semanticweb.HermiT.model.ExistentialConcept; import org.semanticweb.HermiT.model.NegatedAtomicRole; import org.semanticweb.HermiT.monitor.TableauMonitor; /** * An extension table keeps track of the assertions in the ABox during a run of * the tableau. For this purpose, it holds a binary (concept, node) and a * ternary (role, node, node) tuple table, which represent concept and role * assertions respectively. Since this is one of the most crucial parts * regarding memory usage, reusing already allocated space is the main design * goal. In case of backtracking during the expansion, we just set the pointer * to a previous entry in the table that then becomes the current one. When * merging or pruning, we leave the entries for the merged/pruned nodes in the * table so that we do not have holes in there. The tuple tables are indexed * (tries/prefix trees) to speed-up the search for matching atoms during rule * applications. */ public abstract class ExtensionTable implements Serializable { private static final long serialVersionUID=-5029938218056017193L; public static enum View { EXTENSION_THIS,EXTENSION_OLD,DELTA_OLD,TOTAL }; protected final Tableau m_tableau; protected final TableauMonitor m_tableauMonitor; protected final int m_tupleArity; protected final TupleTable m_tupleTable; protected final DependencySetManager m_dependencySetManager; protected final CoreManager m_coreManager; protected int m_afterExtensionOldTupleIndex; protected int m_afterExtensionThisTupleIndex; protected int m_afterDeltaNewTupleIndex; protected int[] m_indicesByBranchingPoint; public ExtensionTable(Tableau tableau,int tupleArity,boolean needsDependencySets) { m_tableau=tableau; m_tableauMonitor=m_tableau.m_tableauMonitor; m_tupleArity=tupleArity; m_tupleTable=new TupleTable(m_tupleArity+(needsDependencySets ? 1 : 0)); m_dependencySetManager=needsDependencySets ? new LastObjectDependencySetManager(this) : new DeterministicDependencySetManager(this); if (m_tupleArity==2) m_coreManager=new RealCoreManager(); else m_coreManager=new NoCoreManager(); m_indicesByBranchingPoint=new int[2*3]; } public abstract int sizeInMemory(); public int getArity() { return m_tupleArity; } public void retrieveTuple(Object[] tupleBuffer,int tupleIndex) { m_tupleTable.retrieveTuple(tupleBuffer,tupleIndex); } public Object getTupleObject(int tupleIndex,int objectIndex) { return m_tupleTable.getTupleObject(tupleIndex,objectIndex); } public DependencySet getDependencySet(int tupleIndex) { return m_dependencySetManager.getDependencySet(tupleIndex); } public boolean isCore(int tupleIndex) { return m_coreManager.isCore(tupleIndex); } public abstract boolean addTuple(Object[] tuple,DependencySet dependencySet,boolean isCore); /** * This method is called each time a fresh tuple is added. The method is not called if the tuple * was already contained in the extension table. The method updates a couple of relevant data structures * and notifies all relevant parties of the tuple's addition. */ protected void postAdd(Object[] tuple,DependencySet dependencySet,int tupleIndex,boolean isCore) { Object dlPredicateObject=tuple[0]; if (dlPredicateObject instanceof Concept) { Node node=(Node)tuple[1]; if (dlPredicateObject instanceof AtomicConcept) node.m_numberOfPositiveAtomicConcepts++; else if (dlPredicateObject instanceof ExistentialConcept) node.addToUnprocessedExistentials((ExistentialConcept)dlPredicateObject); else if (dlPredicateObject instanceof AtomicNegationConcept) node.m_numberOfNegatedAtomicConcepts++; m_tableau.m_existentialExpansionStrategy.assertionAdded((Concept)dlPredicateObject,node,isCore); } else if (dlPredicateObject instanceof AtomicRole) m_tableau.m_existentialExpansionStrategy.assertionAdded((AtomicRole)dlPredicateObject,(Node)tuple[1],(Node)tuple[2],isCore); else if (dlPredicateObject instanceof NegatedAtomicRole) ((Node)tuple[1]).m_numberOfNegatedRoleAssertions++; else if (dlPredicateObject instanceof DescriptionGraph) m_tableau.m_descriptionGraphManager.descriptionGraphTupleAdded(tupleIndex,tuple); m_tableau.m_clashManager.tupleAdded(this,tuple,dependencySet,isCore); } public abstract boolean containsTuple(Object[] tuple); public Retrieval createRetrieval(boolean[] bindingPattern,View extensionView) { int[] bindingPositions=new int[bindingPattern.length]; for (int index=0;index<bindingPattern.length;index++) if (bindingPattern[index]) bindingPositions[index]=index; else bindingPositions[index]=-1; return createRetrieval(bindingPositions,new Object[bindingPattern.length],new Object[bindingPattern.length],true,extensionView); } public abstract Retrieval createRetrieval(int[] bindingPositions,Object[] bindingsBuffer,Object[] tupleBuffer,boolean ownsBuffers,View extensionView); public abstract DependencySet getDependencySet(Object[] tuple); public abstract boolean isCore(Object[] tuple); public boolean propagateDeltaNew() { boolean deltaNewNotEmpty=(m_afterExtensionThisTupleIndex!=m_afterDeltaNewTupleIndex); m_afterExtensionOldTupleIndex=m_afterExtensionThisTupleIndex; m_afterExtensionThisTupleIndex=m_afterDeltaNewTupleIndex; m_afterDeltaNewTupleIndex=m_tupleTable.getFirstFreeTupleIndex(); return deltaNewNotEmpty; } public void branchingPointPushed() { int start=m_tableau.getCurrentBranchingPoint().m_level*3; int requiredSize=start+3; if (requiredSize>m_indicesByBranchingPoint.length) { int newSize=m_indicesByBranchingPoint.length*3/2; while (requiredSize>newSize) newSize=newSize*3/2; int[] newIndicesByBranchingPoint=new int[newSize]; System.arraycopy(m_indicesByBranchingPoint,0,newIndicesByBranchingPoint,0,m_indicesByBranchingPoint.length); m_indicesByBranchingPoint=newIndicesByBranchingPoint; } m_indicesByBranchingPoint[start]=m_afterExtensionOldTupleIndex; m_indicesByBranchingPoint[start+1]=m_afterExtensionThisTupleIndex; m_indicesByBranchingPoint[start+2]=m_afterDeltaNewTupleIndex; } public void backtrack() { int start=m_tableau.getCurrentBranchingPoint().m_level*3; int newAfterDeltaNewTupleIndex=m_indicesByBranchingPoint[start+2]; for (int tupleIndex=m_afterDeltaNewTupleIndex-1;tupleIndex>=newAfterDeltaNewTupleIndex;--tupleIndex) { removeTuple(tupleIndex); m_dependencySetManager.forgetDependencySet(tupleIndex); m_tupleTable.nullifyTuple(tupleIndex); } m_tupleTable.truncate(newAfterDeltaNewTupleIndex); m_afterExtensionOldTupleIndex=m_indicesByBranchingPoint[start]; m_afterExtensionThisTupleIndex=m_indicesByBranchingPoint[start+1]; m_afterDeltaNewTupleIndex=newAfterDeltaNewTupleIndex; } protected abstract void removeTuple(int tupleIndex); protected void postRemove(Object[] tuple,int tupleIndex) { Object dlPredicateObject=tuple[0]; if (dlPredicateObject instanceof Concept) { Node node=(Node)tuple[1]; m_tableau.m_existentialExpansionStrategy.assertionRemoved((Concept)dlPredicateObject,node,m_coreManager.isCore(tupleIndex)); if (dlPredicateObject instanceof AtomicConcept) node.m_numberOfPositiveAtomicConcepts--; else if (dlPredicateObject instanceof ExistentialConcept) node.removeFromUnprocessedExistentials((ExistentialConcept)dlPredicateObject); else if (dlPredicateObject instanceof AtomicNegationConcept) node.m_numberOfNegatedAtomicConcepts--; } else if (dlPredicateObject instanceof AtomicRole) m_tableau.m_existentialExpansionStrategy.assertionRemoved((AtomicRole)dlPredicateObject,(Node)tuple[1],(Node)tuple[2],m_coreManager.isCore(tupleIndex)); else if (dlPredicateObject instanceof NegatedAtomicRole) ((Node)tuple[1]).m_numberOfNegatedRoleAssertions--; else if (dlPredicateObject instanceof DescriptionGraph) m_tableau.m_descriptionGraphManager.descriptionGraphTupleRemoved(tupleIndex,tuple); if (m_tableauMonitor!=null) m_tableauMonitor.tupleRemoved(tuple); } public void clear() { m_tupleTable.clear(); m_afterExtensionOldTupleIndex=0; m_afterExtensionThisTupleIndex=0; m_afterDeltaNewTupleIndex=0; } public boolean isTupleActive(Object[] tuple) { for (int objectIndex=m_tupleArity-1;objectIndex>0;--objectIndex) if (!((Node)tuple[objectIndex]).isActive()) return false; return true; } public boolean isTupleActive(int tupleIndex) { for (int objectIndex=m_tupleArity-1;objectIndex>0;--objectIndex) if (!((Node)m_tupleTable.getTupleObject(tupleIndex,objectIndex)).isActive()) return false; return true; } public static interface Retrieval { ExtensionTable getExtensionTable(); View getExtensionView(); void clear(); int[] getBindingPositions(); Object[] getBindingsBuffer(); Object[] getTupleBuffer(); DependencySet getDependencySet(); boolean isCore(); void open(); boolean afterLast(); int getCurrentTupleIndex(); void next(); } protected class UnindexedRetrieval implements Retrieval,Serializable { private static final long serialVersionUID=6395072458663267969L; protected final ExtensionTable.View m_extensionView; protected final int[] m_bindingPositions; protected final Object[] m_bindingsBuffer; protected final Object[] m_tupleBuffer; protected final boolean m_ownsBuffers; protected final boolean m_checkTupleSelection; protected int m_currentTupleIndex; protected int m_afterLastTupleIndex; public UnindexedRetrieval(int[] bindingPositions,Object[] bindingsBuffer,Object[] tupleBuffer,boolean ownsBuffers,ExtensionTable.View extensionView) { m_bindingPositions=bindingPositions; m_extensionView=extensionView; m_bindingsBuffer=bindingsBuffer; m_tupleBuffer=tupleBuffer; m_ownsBuffers=ownsBuffers; int numberOfBoundPositions=0; for (int index=m_bindingPositions.length-1;index>=0;--index) if (m_bindingPositions[index]!=-1) numberOfBoundPositions++; m_checkTupleSelection=(numberOfBoundPositions>0); } public ExtensionTable getExtensionTable() { return ExtensionTable.this; } public ExtensionTable.View getExtensionView() { return m_extensionView; } public void clear() { if (m_ownsBuffers) { for (int index=m_bindingsBuffer.length-1;index>=0;--index) m_bindingsBuffer[index]=null; for (int index=m_tupleBuffer.length-1;index>=0;--index) m_tupleBuffer[index]=null; } } public int[] getBindingPositions() { return m_bindingPositions; } public Object[] getBindingsBuffer() { return m_bindingsBuffer; } public Object[] getTupleBuffer() { return m_tupleBuffer; } public DependencySet getDependencySet() { return m_dependencySetManager.getDependencySet(m_currentTupleIndex); } public boolean isCore() { return m_coreManager.isCore(m_currentTupleIndex); } public void open() { switch (m_extensionView) { case EXTENSION_THIS: m_currentTupleIndex=0; m_afterLastTupleIndex=m_afterExtensionThisTupleIndex; break; case EXTENSION_OLD: m_currentTupleIndex=0; m_afterLastTupleIndex=m_afterExtensionOldTupleIndex; break; case DELTA_OLD: m_currentTupleIndex=m_afterExtensionOldTupleIndex; m_afterLastTupleIndex=m_afterExtensionThisTupleIndex; break; case TOTAL: m_currentTupleIndex=0; m_afterLastTupleIndex=m_afterDeltaNewTupleIndex; break; } while (m_currentTupleIndex<m_afterLastTupleIndex) { m_tupleTable.retrieveTuple(m_tupleBuffer,m_currentTupleIndex); if (isTupleActive()) return; m_currentTupleIndex++; } } public boolean afterLast() { return m_currentTupleIndex>=m_afterLastTupleIndex; } public int getCurrentTupleIndex() { return m_currentTupleIndex; } public void next() { if (m_currentTupleIndex<m_afterLastTupleIndex) { m_currentTupleIndex++; while (m_currentTupleIndex<m_afterLastTupleIndex) { m_tupleTable.retrieveTuple(m_tupleBuffer,m_currentTupleIndex); if (isTupleActive()) return; m_currentTupleIndex++; } } } protected boolean isTupleActive() { if (!ExtensionTable.this.isTupleActive(m_tupleBuffer)) return false; if (m_checkTupleSelection) for (int index=m_bindingPositions.length-1;index>=0;--index) if (m_bindingPositions[index]!=-1 && !m_tupleBuffer[index].equals(m_bindingsBuffer[m_bindingPositions[index]])) return false; return true; } } protected static interface DependencySetManager { DependencySet getDependencySet(int tupleIndex); void setDependencySet(int tupleIndex,DependencySet dependencySet); void forgetDependencySet(int tupleIndex); } protected static class DeterministicDependencySetManager implements DependencySetManager,Serializable { private static final long serialVersionUID=7982627098607954806L; protected final DependencySetFactory m_dependencySetFactory; public DeterministicDependencySetManager(ExtensionTable extensionTable) { m_dependencySetFactory=extensionTable.m_tableau.getDependencySetFactory(); } public DependencySet getDependencySet(int tupleIndex) { return m_dependencySetFactory.emptySet(); } public void setDependencySet(int tupleIndex,DependencySet dependencySet) { } public void forgetDependencySet(int tupleIndex) { } } protected class LastObjectDependencySetManager implements DependencySetManager,Serializable { private static final long serialVersionUID=-8097612469749016470L; protected final DependencySetFactory m_dependencySetFactory; public LastObjectDependencySetManager(ExtensionTable extensionTable) { m_dependencySetFactory=extensionTable.m_tableau.getDependencySetFactory(); } public DependencySet getDependencySet(int tupleIndex) { return (DependencySet)m_tupleTable.getTupleObject(tupleIndex,m_tupleArity); } public void setDependencySet(int tupleIndex,DependencySet dependencySet) { PermanentDependencySet permanentDependencySet=m_dependencySetFactory.getPermanent(dependencySet); m_tupleTable.setTupleObject(tupleIndex,m_tupleArity,permanentDependencySet); m_dependencySetFactory.addUsage(permanentDependencySet); } public void forgetDependencySet(int tupleIndex) { PermanentDependencySet permanentDependencySet=(PermanentDependencySet)m_tupleTable.getTupleObject(tupleIndex,m_tupleArity); m_dependencySetFactory.removeUsage(permanentDependencySet); } } protected static interface CoreManager { boolean isCore(int tupleIndex); void addCore(int tupleIndex); void setCore(int tupleIndex,boolean isCore); } protected static class NoCoreManager implements CoreManager,Serializable { private static final long serialVersionUID=3252994135060928432L; public boolean isCore(int tupleIndex) { return true; } public void addCore(int tupleIndex) { } public void setCore(int tupleIndex,boolean isCore) { } } protected static class RealCoreManager implements CoreManager,Serializable { private static final long serialVersionUID=3276377301185845284L; protected int[] m_bits; public RealCoreManager() { m_bits=new int[TupleTable.PAGE_SIZE/32]; } public boolean isCore(int tupleIndex) { int frameIndex=tupleIndex/32; int mask=1 << (tupleIndex % 32); return (m_bits[frameIndex] & mask)!=0; } public void addCore(int tupleIndex) { int frameIndex=tupleIndex/32; int mask=1 << (tupleIndex % 32); m_bits[frameIndex]|=mask; } public void setCore(int tupleIndex,boolean isCore) { int frameIndex=tupleIndex/32; int mask=1 << (tupleIndex % 32); if (frameIndex>=m_bits.length) { int newSize=3*m_bits.length/2; while (frameIndex>=newSize) newSize=3*newSize/2; int[] newBits=new int[newSize]; System.arraycopy(m_bits,0,newBits,0,m_bits.length); m_bits=newBits; } if (isCore) m_bits[frameIndex]|=mask; else m_bits[frameIndex]&=~mask; } } }