/* 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.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; import org.semanticweb.HermiT.Prefixes; import org.semanticweb.HermiT.datatypes.DatatypeRegistry; import org.semanticweb.HermiT.datatypes.ValueSpaceSubset; import org.semanticweb.HermiT.model.AtomicDataRange; import org.semanticweb.HermiT.model.AtomicNegationDataRange; import org.semanticweb.HermiT.model.ConstantEnumeration; import org.semanticweb.HermiT.model.DLOntology; import org.semanticweb.HermiT.model.DataRange; import org.semanticweb.HermiT.model.DatatypeRestriction; import org.semanticweb.HermiT.model.Inequality; import org.semanticweb.HermiT.model.InternalDatatype; import org.semanticweb.HermiT.model.LiteralDataRange; import org.semanticweb.HermiT.monitor.TableauMonitor; public final class DatatypeManager implements Serializable { private static final long serialVersionUID=-5304869484553471737L; protected final InterruptFlag m_interruptFlag; protected final TableauMonitor m_tableauMonitor; protected final ExtensionManager m_extensionManager; protected final ExtensionTable.Retrieval m_assertionsDeltaOldRetrieval; protected final ExtensionTable.Retrieval m_inequalityDeltaOldRetrieval; protected final ExtensionTable.Retrieval m_inequality01Retrieval; protected final ExtensionTable.Retrieval m_inequality02Retrieval; protected final ExtensionTable.Retrieval m_assertions0Retrieval; protected final ExtensionTable.Retrieval m_assertions1Retrieval; protected final DConjunction m_conjunction; protected final List<DVariable> m_auxiliaryVariableList; protected final UnionDependencySet m_unionDependencySet; protected final boolean[] m_newVariableAdded; protected final Set<DatatypeRestriction> m_unknownDatatypeRestrictionsPermanent; protected Set<DatatypeRestriction> m_unknownDatatypeRestrictionsAdditional; public DatatypeManager(Tableau tableau) { m_interruptFlag=tableau.m_interruptFlag; m_tableauMonitor=tableau.m_tableauMonitor; m_extensionManager=tableau.m_extensionManager; m_assertionsDeltaOldRetrieval=m_extensionManager.getBinaryExtensionTable().createRetrieval(new boolean[] { false,false },ExtensionTable.View.DELTA_OLD); // The following retrieval should actually be created such that it has Inequality.INSTANCE as the first binding. // We don't do this because of the implementation of the ExtensionTable. Namely, each ExtensionTable keeps // only one index for all tuples, and then filters out the assertions that don't fit into the index. As a // side effect of such an implementation, this means that each time we go through the entire index for inequalities, // which can take up a lot of time. The current solution is more efficient: it goes through the actual delta-old // and then filters out inequalities "manually". m_inequalityDeltaOldRetrieval=m_extensionManager.getTernaryExtensionTable().createRetrieval(new boolean[] { false,false,false },ExtensionTable.View.DELTA_OLD); m_inequality01Retrieval=m_extensionManager.getTernaryExtensionTable().createRetrieval(new boolean[] { true,true,false },ExtensionTable.View.EXTENSION_THIS); m_inequality02Retrieval=m_extensionManager.getTernaryExtensionTable().createRetrieval(new boolean[] { true,false,true },ExtensionTable.View.EXTENSION_THIS); m_assertions0Retrieval=m_extensionManager.getBinaryExtensionTable().createRetrieval(new boolean[] { true,false },ExtensionTable.View.EXTENSION_THIS); m_assertions1Retrieval=m_extensionManager.getBinaryExtensionTable().createRetrieval(new boolean[] { false,true },ExtensionTable.View.EXTENSION_THIS); m_conjunction=new DConjunction(); m_auxiliaryVariableList=new ArrayList<DVariable>(); m_unionDependencySet=new UnionDependencySet(16); m_newVariableAdded=new boolean[1]; m_unknownDatatypeRestrictionsPermanent=tableau.m_permanentDLOntology.getAllUnknownDatatypeRestrictions(); if (tableau.m_additionalDLOntology!=null) additionalDLOntologySet(tableau.m_additionalDLOntology); } public void additionalDLOntologySet(DLOntology additionalDLOntology) { m_unknownDatatypeRestrictionsAdditional=additionalDLOntology.getAllUnknownDatatypeRestrictions(); } public void additionalDLOntologyCleared() { m_unknownDatatypeRestrictionsAdditional=null; } public void clear() { m_assertionsDeltaOldRetrieval.clear(); m_inequalityDeltaOldRetrieval.clear(); m_inequality01Retrieval.clear(); m_inequality02Retrieval.clear(); m_assertions0Retrieval.clear(); m_assertions1Retrieval.clear(); m_conjunction.clear(); m_auxiliaryVariableList.clear(); m_unionDependencySet.clearConstituents(); } public void applyUnknownDatatypeRestrictionSemantics() { Object[] tupleBuffer=m_assertionsDeltaOldRetrieval.getTupleBuffer(); m_assertionsDeltaOldRetrieval.open(); while (!m_extensionManager.containsClash() && !m_assertionsDeltaOldRetrieval.afterLast()) { Object dataRangeObject=tupleBuffer[0]; if (dataRangeObject instanceof DatatypeRestriction) { DatatypeRestriction datatypeRestriction=(DatatypeRestriction)dataRangeObject; if (m_unknownDatatypeRestrictionsPermanent.contains(datatypeRestriction) || (m_unknownDatatypeRestrictionsAdditional!=null && m_unknownDatatypeRestrictionsAdditional.contains(datatypeRestriction))) generateInequalitiesFor(datatypeRestriction,(Node)tupleBuffer[1],m_assertionsDeltaOldRetrieval.getDependencySet(),AtomicNegationDataRange.create(datatypeRestriction)); } else if (dataRangeObject instanceof AtomicNegationDataRange) { AtomicNegationDataRange negationDataRange=(AtomicNegationDataRange)dataRangeObject; DataRange negatedDataRange=negationDataRange.getNegatedDataRange(); if (negatedDataRange instanceof DatatypeRestriction) { DatatypeRestriction datatypeRestriction=(DatatypeRestriction)negatedDataRange; if (m_unknownDatatypeRestrictionsPermanent.contains(datatypeRestriction) || (m_unknownDatatypeRestrictionsAdditional!=null && m_unknownDatatypeRestrictionsAdditional.contains(datatypeRestriction))) generateInequalitiesFor(negationDataRange,(Node)tupleBuffer[1],m_assertionsDeltaOldRetrieval.getDependencySet(),datatypeRestriction); } } m_assertionsDeltaOldRetrieval.next(); } } protected void generateInequalitiesFor(DataRange dataRange1,Node node1,DependencySet dependencySet1,DataRange dataRange2) { m_unionDependencySet.clearConstituents(); m_unionDependencySet.addConstituent(dependencySet1); m_unionDependencySet.addConstituent(null); m_assertions0Retrieval.getBindingsBuffer()[0]=dataRange2; Object[] tupleBuffer=m_assertions0Retrieval.getTupleBuffer(); m_assertions0Retrieval.open(); while (!m_assertions0Retrieval.afterLast()) { Node node2=(Node)tupleBuffer[1]; m_unionDependencySet.m_dependencySets[1]=m_assertions0Retrieval.getDependencySet(); if (m_tableauMonitor!=null) m_tableauMonitor.unknownDatatypeRestrictionDetectionStarted(dataRange1,node1,dataRange2,node2); m_extensionManager.addAssertion(Inequality.INSTANCE,node1,node2,m_unionDependencySet,false); if (m_tableauMonitor!=null) m_tableauMonitor.unknownDatatypeRestrictionDetectionFinished(dataRange1,node1,dataRange2,node2); m_assertions0Retrieval.next(); } } public void checkDatatypeConstraints() { if (m_tableauMonitor!=null) m_tableauMonitor.datatypeCheckingStarted(); m_conjunction.clear(); Object[] tupleBuffer=m_assertionsDeltaOldRetrieval.getTupleBuffer(); m_assertionsDeltaOldRetrieval.open(); while (!m_extensionManager.containsClash() && !m_assertionsDeltaOldRetrieval.afterLast()) { if (tupleBuffer[0] instanceof DataRange) { // A data range was added in the last saturation step, so we check the D-conjunction hanging off of its node. Node node=(Node)tupleBuffer[1]; DVariable variable=getAndInitializeVariableFor(node,m_newVariableAdded); // m_newVariableAdded[0]==false means that 'variable' has already been checked in this iteration. if (m_newVariableAdded[0]) { m_conjunction.clearActiveVariables(); loadConjunctionFrom(variable); checkConjunctionSatisfiability(); } } m_assertionsDeltaOldRetrieval.next(); } tupleBuffer=m_inequalityDeltaOldRetrieval.getTupleBuffer(); m_inequalityDeltaOldRetrieval.open(); while (!m_extensionManager.containsClash() && !m_inequalityDeltaOldRetrieval.afterLast()) { // This is a part of the hack described in the constructor: m_inequalityDeltaOldRetrieval // iterates through the entire extension (for efficiency) and then we need to filter out // inequalities ourselves. if (Inequality.INSTANCE.equals(tupleBuffer[0])) { Node node1=(Node)tupleBuffer[1]; Node node2=(Node)tupleBuffer[2]; if (!node1.getNodeType().isAbstract() && !node2.getNodeType().isAbstract()) { m_conjunction.clearActiveVariables(); // An inequality between concrete was added in the last saturation step, so we check the D-conjunction hanging off of its node. DVariable variable1=getAndInitializeVariableFor(node1,m_newVariableAdded); // m_newVariableAdded[0]==false means that 'variable1' has already been checked in this iteration. if (m_newVariableAdded[0]) loadConjunctionFrom(variable1); DVariable variable2=getAndInitializeVariableFor(node2,m_newVariableAdded); // m_newVariableAdded[0]==false means that 'variable1' has already been checked in this iteration. if (m_newVariableAdded[0]) loadConjunctionFrom(variable2); m_conjunction.addInequality(variable1,variable2); checkConjunctionSatisfiability(); } } m_inequalityDeltaOldRetrieval.next(); } if (m_tableauMonitor!=null) m_tableauMonitor.datatypeCheckingFinished(!m_extensionManager.containsClash()); m_unionDependencySet.clearConstituents(); m_conjunction.clear(); m_auxiliaryVariableList.clear(); } protected void loadConjunctionFrom(DVariable startVariable) { m_auxiliaryVariableList.clear(); m_auxiliaryVariableList.add(startVariable); while (!m_extensionManager.containsClash() && !m_auxiliaryVariableList.isEmpty()) { DVariable reachedVariable=m_auxiliaryVariableList.remove(m_auxiliaryVariableList.size()-1); if (!m_conjunction.m_activeVariables.contains(reachedVariable)) { m_conjunction.m_activeVariables.add(reachedVariable); // Concrete root nodes are assigned a particular value, so they act as "breakers" in the conjunction: // the nodes that are unequal to them can be analyzed independently. if (reachedVariable.m_node.getNodeType()!=NodeType.ROOT_CONSTANT_NODE) { // Look for all inequalities where reachedNode occurs in the first position. m_inequality01Retrieval.getBindingsBuffer()[0]=Inequality.INSTANCE; m_inequality01Retrieval.getBindingsBuffer()[1]=reachedVariable.m_node; m_inequality01Retrieval.open(); Object[] tupleBuffer=m_inequality01Retrieval.getTupleBuffer(); while (!m_extensionManager.containsClash() && !m_inequality01Retrieval.afterLast()) { Node newNode=(Node)tupleBuffer[2]; DVariable newVariable=getAndInitializeVariableFor(newNode,m_newVariableAdded); m_auxiliaryVariableList.add(newVariable); m_conjunction.addInequality(reachedVariable,newVariable); m_inequality01Retrieval.next(); m_interruptFlag.checkInterrupt(); } // Look for all inequalities where reachedNode occurs in the second position. m_inequality02Retrieval.getBindingsBuffer()[0]=Inequality.INSTANCE; m_inequality02Retrieval.getBindingsBuffer()[2]=reachedVariable.m_node; m_inequality02Retrieval.open(); tupleBuffer=m_inequality02Retrieval.getTupleBuffer(); while (!m_extensionManager.containsClash() && !m_inequality02Retrieval.afterLast()) { Node newNode=(Node)tupleBuffer[1]; DVariable newVariable=getAndInitializeVariableFor(newNode,m_newVariableAdded); m_auxiliaryVariableList.add(newVariable); m_conjunction.addInequality(newVariable,reachedVariable); m_inequality02Retrieval.next(); m_interruptFlag.checkInterrupt(); } } } } } protected DVariable getAndInitializeVariableFor(Node node,boolean[] newVariableAdded) { DVariable variable=m_conjunction.getVariableForEx(node,newVariableAdded); if (m_newVariableAdded[0]) { m_assertions1Retrieval.getBindingsBuffer()[1]=variable.m_node; m_assertions1Retrieval.open(); Object[] tupleBuffer=m_assertions1Retrieval.getTupleBuffer(); while (!m_extensionManager.containsClash() && !m_assertions1Retrieval.afterLast()) { Object potentialDataRange=tupleBuffer[0]; if (potentialDataRange instanceof DataRange) addDataRange(variable,(DataRange)potentialDataRange); m_assertions1Retrieval.next(); m_interruptFlag.checkInterrupt(); } if (!m_extensionManager.containsClash()) normalize(variable); } return variable; } protected void addDataRange(DVariable variable,DataRange dataRange) { if (dataRange instanceof InternalDatatype) { // Internal datatypes are skipped, as they do not contribute to datatype checking. // These are used to encode rdfs:Literal and datatype definitions, and to rename complex data ranges. } else if (dataRange instanceof DatatypeRestriction) { DatatypeRestriction datatypeRestriction=(DatatypeRestriction)dataRange; if (!m_unknownDatatypeRestrictionsPermanent.contains(datatypeRestriction) && (m_unknownDatatypeRestrictionsAdditional==null || !m_unknownDatatypeRestrictionsAdditional.contains(datatypeRestriction))) { variable.m_positiveDatatypeRestrictions.add(datatypeRestriction); if (variable.m_mostSpecificRestriction==null) variable.m_mostSpecificRestriction=datatypeRestriction; else if (DatatypeRegistry.isDisjointWith(variable.m_mostSpecificRestriction.getDatatypeURI(),datatypeRestriction.getDatatypeURI())) { m_unionDependencySet.clearConstituents(); m_unionDependencySet.addConstituent(m_extensionManager.getAssertionDependencySet(variable.m_mostSpecificRestriction,variable.m_node)); m_unionDependencySet.addConstituent(m_extensionManager.getAssertionDependencySet(datatypeRestriction,variable.m_node)); Object[] tuple1; Object[] tuple2; if (m_tableauMonitor!=null) { tuple1=new Object[] { variable.m_mostSpecificRestriction,variable.m_node }; tuple2=new Object[] { datatypeRestriction,variable.m_node }; m_tableauMonitor.clashDetectionStarted(tuple1,tuple2); } m_extensionManager.setClash(m_unionDependencySet); if (m_tableauMonitor!=null) { tuple1=new Object[] { variable.m_mostSpecificRestriction,variable.m_node }; tuple2=new Object[] { datatypeRestriction,variable.m_node }; m_tableauMonitor.clashDetectionFinished(tuple1,tuple2); } } else if (DatatypeRegistry.isSubsetOf(datatypeRestriction.getDatatypeURI(),variable.m_mostSpecificRestriction.getDatatypeURI())) variable.m_mostSpecificRestriction=datatypeRestriction; } } else if (dataRange instanceof ConstantEnumeration) variable.m_positiveConstantEnumerations.add((ConstantEnumeration)dataRange); else if (dataRange instanceof AtomicNegationDataRange) { DataRange negatedDataRange=((AtomicNegationDataRange)dataRange).getNegatedDataRange(); if (negatedDataRange instanceof InternalDatatype) { // Skip for the same reasons as above. } else if (negatedDataRange instanceof DatatypeRestriction) { DatatypeRestriction datatypeRestriction=(DatatypeRestriction)negatedDataRange; if (!m_unknownDatatypeRestrictionsPermanent.contains(datatypeRestriction) && (m_unknownDatatypeRestrictionsAdditional==null || !m_unknownDatatypeRestrictionsAdditional.contains(datatypeRestriction))) variable.m_negativeDatatypeRestrictions.add(datatypeRestriction); } else if (negatedDataRange instanceof ConstantEnumeration) { ConstantEnumeration negatedConstantEnumeration=(ConstantEnumeration)negatedDataRange; variable.m_negativeConstantEnumerations.add(negatedConstantEnumeration); for (int index=negatedConstantEnumeration.getNumberOfConstants()-1;index>=0;--index) variable.addForbiddenDataValue(negatedConstantEnumeration.getConstant(index).getDataValue()); } else throw new IllegalStateException("Internal error: invalid data range."); } else throw new IllegalStateException("Internal error: invalid data range."); } protected void checkConjunctionSatisfiability() { if (!m_extensionManager.containsClash() && !m_conjunction.m_activeVariables.isEmpty()) { if (m_tableauMonitor!=null) m_tableauMonitor.datatypeConjunctionCheckingStarted(m_conjunction); if (m_conjunction.isSymmetricClique()) { DVariable representative=m_conjunction.m_activeVariables.get(0); if (!m_extensionManager.containsClash() && !representative.hasCardinalityAtLeast(m_conjunction.m_activeVariables.size())) setClashFor(m_conjunction.m_activeVariables); } else if (!m_extensionManager.containsClash()) { eliminateTrivialInequalities(); eliminateTriviallySatisfiableNodes(); enumerateValueSpaceSubsets(); if (!m_extensionManager.containsClash()) { eliminateTriviallySatisfiableNodes(); checkAssignments(); } } if (m_tableauMonitor!=null) m_tableauMonitor.datatypeConjunctionCheckingFinished(m_conjunction,!m_extensionManager.containsClash()); } } protected void normalize(DVariable variable) { if (!variable.m_positiveConstantEnumerations.isEmpty()) normalizeAsEnumeration(variable); else if (!variable.m_positiveDatatypeRestrictions.isEmpty()) normalizeAsValueSpaceSubset(variable); } protected void normalizeAsEnumeration(DVariable variable) { variable.m_hasExplicitDataValues=true; List<Object> explicitDataValues=variable.m_explicitDataValues; List<ConstantEnumeration> positiveConstantEnumerations=variable.m_positiveConstantEnumerations; ConstantEnumeration firstDataValueEnumeration=positiveConstantEnumerations.get(0); nextValue: for (int index=firstDataValueEnumeration.getNumberOfConstants()-1;index>=0;--index) { Object dataValue=firstDataValueEnumeration.getConstant(index).getDataValue(); if (!explicitDataValues.contains(dataValue) && !variable.m_forbiddenDataValues.contains(dataValue)) { for (int enumerationIndex=positiveConstantEnumerations.size()-1;enumerationIndex>=1;--enumerationIndex) if (!containsDataValue(positiveConstantEnumerations.get(enumerationIndex),dataValue)) continue nextValue; explicitDataValues.add(dataValue); } } variable.m_forbiddenDataValues.clear(); List<DatatypeRestriction> positiveDatatypeRestrictions=variable.m_positiveDatatypeRestrictions; for (int index=positiveDatatypeRestrictions.size()-1;!explicitDataValues.isEmpty() && index>=0;--index) { DatatypeRestriction positiveDatatypeRestriction=positiveDatatypeRestrictions.get(index); ValueSpaceSubset valueSpaceSubset=DatatypeRegistry.createValueSpaceSubset(positiveDatatypeRestriction); eliminateDataValuesUsingValueSpaceSubset(valueSpaceSubset,explicitDataValues,false); } List<DatatypeRestriction> negativeDatatypeRestrictions=variable.m_negativeDatatypeRestrictions; for (int index=negativeDatatypeRestrictions.size()-1;!explicitDataValues.isEmpty() && index>=0;--index) { DatatypeRestriction negativeDatatypeRestriction=negativeDatatypeRestrictions.get(index); ValueSpaceSubset valueSpaceSubset=DatatypeRegistry.createValueSpaceSubset(negativeDatatypeRestriction); eliminateDataValuesUsingValueSpaceSubset(valueSpaceSubset,explicitDataValues,true); } if (explicitDataValues.isEmpty()) setClashFor(variable); } protected boolean containsDataValue(ConstantEnumeration constantEnumeration,Object dataValue) { for (int index=constantEnumeration.getNumberOfConstants()-1;index>=0;--index) if (constantEnumeration.getConstant(index).getDataValue().equals(dataValue)) return true; return false; } protected void eliminateDataValuesUsingValueSpaceSubset(ValueSpaceSubset valueSpaceSubset,List<Object> explicitDataValues,boolean eliminateWhenValue) { for (int valueIndex=explicitDataValues.size()-1;valueIndex>=0;--valueIndex) { Object dataValue=explicitDataValues.get(valueIndex); if (valueSpaceSubset.containsDataValue(dataValue)==eliminateWhenValue) explicitDataValues.remove(valueIndex); } } protected void normalizeAsValueSpaceSubset(DVariable variable) { String mostSpecificDatatypeURI=variable.m_mostSpecificRestriction.getDatatypeURI(); variable.m_valueSpaceSubset=DatatypeRegistry.createValueSpaceSubset(variable.m_mostSpecificRestriction); List<DatatypeRestriction> positiveDatatypeRestrictions=variable.m_positiveDatatypeRestrictions; for (int index=positiveDatatypeRestrictions.size()-1;index>=0;--index) { DatatypeRestriction datatypeRestriction=positiveDatatypeRestrictions.get(index); if (datatypeRestriction!=variable.m_mostSpecificRestriction) variable.m_valueSpaceSubset=DatatypeRegistry.conjoinWithDR(variable.m_valueSpaceSubset,datatypeRestriction); } List<DatatypeRestriction> negativeDatatypeRestrictions=variable.m_negativeDatatypeRestrictions; for (int index=negativeDatatypeRestrictions.size()-1;index>=0;--index) { DatatypeRestriction datatypeRestriction=negativeDatatypeRestrictions.get(index); String datatypeRestrictionDatatypeURI=datatypeRestriction.getDatatypeURI(); if (!DatatypeRegistry.isDisjointWith(mostSpecificDatatypeURI,datatypeRestrictionDatatypeURI)) variable.m_valueSpaceSubset=DatatypeRegistry.conjoinWithDRNegation(variable.m_valueSpaceSubset,datatypeRestriction); } if (!variable.m_valueSpaceSubset.hasCardinalityAtLeast(1)) { variable.m_forbiddenDataValues.clear(); setClashFor(variable); } else { for (int valueIndex=variable.m_forbiddenDataValues.size()-1;valueIndex>=0;--valueIndex) { Object forbiddenValue=variable.m_forbiddenDataValues.get(valueIndex); if (!variable.m_valueSpaceSubset.containsDataValue(forbiddenValue)) variable.m_forbiddenDataValues.remove(valueIndex); } } } protected void eliminateTrivialInequalities() { for (int index1=m_conjunction.m_activeVariables.size()-1;index1>=0;--index1) { DVariable variable1=m_conjunction.m_activeVariables.get(index1); if (variable1.m_mostSpecificRestriction!=null) { String datatypeURI1=variable1.m_mostSpecificRestriction.getDatatypeURI(); for (int index2=variable1.m_unequalToDirect.size()-1;index2>=0;--index2) { DVariable variable2=variable1.m_unequalToDirect.get(index2); if (variable2.m_mostSpecificRestriction!=null && DatatypeRegistry.isDisjointWith(datatypeURI1,variable2.m_mostSpecificRestriction.getDatatypeURI())) { variable1.m_unequalTo.remove(variable2); variable1.m_unequalToDirect.remove(variable2); variable2.m_unequalTo.remove(variable1); variable2.m_unequalToDirect.remove(variable1); } } } } } protected void eliminateTriviallySatisfiableNodes() { m_auxiliaryVariableList.clear(); for (int index=m_conjunction.m_activeVariables.size()-1;index>=0;--index) m_auxiliaryVariableList.add(m_conjunction.m_activeVariables.get(index)); while (!m_auxiliaryVariableList.isEmpty()) { DVariable variable=m_auxiliaryVariableList.remove(m_auxiliaryVariableList.size()-1); if (variable.hasCardinalityAtLeast(variable.m_unequalTo.size()+1)) { for (int index=variable.m_unequalTo.size()-1;index>=0;--index) { DVariable neighborVariable=variable.m_unequalTo.get(index); neighborVariable.m_unequalTo.remove(variable); neighborVariable.m_unequalToDirect.remove(variable); if (!m_auxiliaryVariableList.contains(neighborVariable)) m_auxiliaryVariableList.add(neighborVariable); } variable.clearEqualities(); m_conjunction.m_activeVariables.remove(variable); } } } protected void enumerateValueSpaceSubsets() { for (int index=m_conjunction.m_activeVariables.size()-1;!m_extensionManager.containsClash() && index>=0;--index) { DVariable variable=m_conjunction.m_activeVariables.get(index); if (variable.m_valueSpaceSubset!=null) { variable.m_hasExplicitDataValues=true; variable.m_valueSpaceSubset.enumerateDataValues(variable.m_explicitDataValues); if (!variable.m_forbiddenDataValues.isEmpty()) { for (int valueIndex=variable.m_explicitDataValues.size()-1;valueIndex>=0;--valueIndex) { Object dataValue=variable.m_explicitDataValues.get(valueIndex); if (variable.m_forbiddenDataValues.contains(dataValue)) variable.m_explicitDataValues.remove(valueIndex); } } variable.m_valueSpaceSubset=null; variable.m_forbiddenDataValues.clear(); if (variable.m_explicitDataValues.isEmpty()) setClashFor(variable); } } } protected void checkAssignments() { // This method could be further optimized to check each clique of inequalities separately. // It is not expected that this is an important optimization, so we don't to it for the moment. // The nodes are sorted so that we get a kind of 'join order' optimization. Collections.sort(m_conjunction.m_activeVariables,SmallestEnumerationFirst.INSTANCE); if (!findAssignment(0)) setClashFor(m_conjunction.m_activeVariables); } protected boolean findAssignment(int nodeIndex) { if (nodeIndex==m_conjunction.m_activeVariables.size()) return true; else { DVariable variable=m_conjunction.m_activeVariables.get(nodeIndex); for (int valueIndex=variable.m_explicitDataValues.size()-1;valueIndex>=0;--valueIndex) { Object dataValue=variable.m_explicitDataValues.get(valueIndex); if (satisfiesNeighbors(variable,dataValue)) { variable.m_dataValue=dataValue; if (findAssignment(nodeIndex+1)) return true; } m_interruptFlag.checkInterrupt(); } variable.m_dataValue=null; return false; } } protected boolean satisfiesNeighbors(DVariable variable,Object dataValue) { for (int neighborIndex=variable.m_unequalTo.size()-1;neighborIndex>=0;--neighborIndex) { Object neighborDataValue=variable.m_unequalTo.get(neighborIndex).m_dataValue; if (neighborDataValue!=null && neighborDataValue.equals(dataValue)) return false; } return true; } protected void setClashFor(DVariable variable) { m_unionDependencySet.clearConstituents(); loadAssertionDependencySets(variable); m_extensionManager.setClash(m_unionDependencySet); } protected void setClashFor(List<DVariable> variables) { m_unionDependencySet.clearConstituents(); for (int nodeIndex=variables.size()-1;nodeIndex>=0;--nodeIndex) { DVariable variable=variables.get(nodeIndex); loadAssertionDependencySets(variable); for (int neighborIndex=variable.m_unequalToDirect.size()-1;neighborIndex>=0;--neighborIndex) { DVariable neighborVariable=variable.m_unequalToDirect.get(neighborIndex); DependencySet dependencySet=m_extensionManager.getAssertionDependencySet(Inequality.INSTANCE,variable.m_node,neighborVariable.m_node); m_unionDependencySet.addConstituent(dependencySet); } } m_extensionManager.setClash(m_unionDependencySet); } protected void loadAssertionDependencySets(DVariable variable) { Node node=variable.m_node; for (int index=variable.m_positiveDatatypeRestrictions.size()-1;index>=0;--index) { AtomicDataRange dataRange=variable.m_positiveDatatypeRestrictions.get(index); DependencySet dependencySet=m_extensionManager.getAssertionDependencySet(dataRange,node); m_unionDependencySet.addConstituent(dependencySet); } for (int index=variable.m_negativeDatatypeRestrictions.size()-1;index>=0;--index) { LiteralDataRange dataRange=variable.m_negativeDatatypeRestrictions.get(index).getNegation(); DependencySet dependencySet=m_extensionManager.getAssertionDependencySet(dataRange,node); m_unionDependencySet.addConstituent(dependencySet); } for (int index=variable.m_positiveConstantEnumerations.size()-1;index>=0;--index) { AtomicDataRange dataRange=variable.m_positiveConstantEnumerations.get(index); DependencySet dependencySet=m_extensionManager.getAssertionDependencySet(dataRange,node); m_unionDependencySet.addConstituent(dependencySet); } for (int index=variable.m_negativeConstantEnumerations.size()-1;index>=0;--index) { LiteralDataRange dataRange=variable.m_negativeConstantEnumerations.get(index).getNegation(); DependencySet dependencySet=m_extensionManager.getAssertionDependencySet(dataRange,node); m_unionDependencySet.addConstituent(dependencySet); } } public static class DConjunction implements Serializable { private static final long serialVersionUID = 3597740301361593691L; protected final List<DVariable> m_unusedVariables; protected final List<DVariable> m_usedVariables; protected final List<DVariable> m_activeVariables; protected DVariable[] m_buckets; protected int m_numberOfEntries; protected int m_resizeThreshold; public DConjunction() { m_unusedVariables=new ArrayList<DVariable>(); m_usedVariables=new ArrayList<DVariable>(); m_activeVariables=new ArrayList<DVariable>(); m_buckets=new DVariable[16]; m_resizeThreshold=(int)(m_buckets.length*0.75); m_numberOfEntries=0; } protected void clear() { for (int index=m_usedVariables.size()-1;index>=0;--index) { DVariable variable=m_usedVariables.get(index); variable.dispose(); m_unusedVariables.add(variable); } m_usedVariables.clear(); m_activeVariables.clear(); Arrays.fill(m_buckets,null); m_numberOfEntries=0; } protected void clearActiveVariables() { for (int index=m_activeVariables.size()-1;index>=0;--index) m_activeVariables.get(index).clearEqualities(); m_activeVariables.clear(); } public List<DVariable> getActiveVariables() { return Collections.unmodifiableList(m_activeVariables); } public DVariable getVariableFor(Node node) { int index=getIndexFor(node.hashCode(),m_buckets.length); DVariable entry=m_buckets[index]; while (entry!=null) { if (entry.m_node==node) return entry; entry=entry.m_nextEntry; } return null; } protected DVariable getVariableForEx(Node node,boolean[] newVariableAdded) { int index=getIndexFor(node.hashCode(),m_buckets.length); DVariable entry=m_buckets[index]; while (entry!=null) { if (entry.m_node==node) { newVariableAdded[0]=false; return entry; } entry=entry.m_nextEntry; } DVariable newVariable; if (m_unusedVariables.isEmpty()) newVariable=new DVariable(); else newVariable=m_unusedVariables.remove(m_unusedVariables.size()-1); newVariable.m_node=node; newVariable.m_nextEntry=m_buckets[index]; m_buckets[index]=newVariable; m_numberOfEntries++; if (m_numberOfEntries>=m_resizeThreshold) resize(m_buckets.length*2); newVariableAdded[0]=true; m_usedVariables.add(newVariable); return newVariable; } protected void resize(int newCapacity) { DVariable[] newBuckets=new DVariable[newCapacity]; for (int i=0;i<m_buckets.length;i++) { DVariable entry=m_buckets[i]; while (entry!=null) { DVariable nextEntry=entry.m_nextEntry; int newIndex=getIndexFor(entry.m_node.hashCode(),newCapacity); entry.m_nextEntry=newBuckets[newIndex]; newBuckets[newIndex]=entry; entry=nextEntry; } } m_buckets=newBuckets; m_resizeThreshold=(int)(newCapacity*0.75); } protected void addInequality(DVariable node1,DVariable node2) { // Inequalities between nodes in the tableau are detected by the ExtensionManager. // Consequently, the DConjunction should not contain inequalities between DVariables. assert node1!=node2; if (!node1.m_unequalTo.contains(node2)) { node1.m_unequalTo.add(node2); node2.m_unequalTo.add(node1); node1.m_unequalToDirect.add(node2); } } public boolean isSymmetricClique() { // This method depends on the fact that there are no self-links. int numberOfVariables=m_activeVariables.size(); if (numberOfVariables>0) { DVariable first=m_activeVariables.get(0); for (int variableIndex=numberOfVariables-1;variableIndex>=0;--variableIndex) { DVariable variable=m_activeVariables.get(variableIndex); if (variable.m_unequalTo.size()+1!=numberOfVariables || !first.hasSameRestrictions(variable)) return false; } } return true; } public String toString() { return toString(Prefixes.STANDARD_PREFIXES); } public String toString(Prefixes prefixes) { StringBuffer buffer=new StringBuffer(); boolean first=true; for (int variableIndex=0;variableIndex<m_activeVariables.size();variableIndex++) { if (first) first=false; else buffer.append(" & "); DVariable variable=m_activeVariables.get(variableIndex); buffer.append(variable.toString(prefixes)); buffer.append('('); buffer.append(variableIndex); buffer.append(')'); for (int neighborIndex=0;neighborIndex<variable.m_unequalToDirect.size();neighborIndex++) { buffer.append(" & "); buffer.append(variableIndex); buffer.append(" != "); buffer.append(m_activeVariables.indexOf(variable.m_unequalToDirect.get(neighborIndex))); } } return buffer.toString(); } } public static class DVariable implements Serializable { private static final long serialVersionUID = -2490195841140286089L; protected final List<ConstantEnumeration> m_positiveConstantEnumerations; protected final List<ConstantEnumeration> m_negativeConstantEnumerations; protected final List<DatatypeRestriction> m_positiveDatatypeRestrictions; protected final List<DatatypeRestriction> m_negativeDatatypeRestrictions; protected final List<DVariable> m_unequalTo; protected final List<DVariable> m_unequalToDirect; protected final List<Object> m_forbiddenDataValues; protected final List<Object> m_explicitDataValues; protected boolean m_hasExplicitDataValues; protected DatatypeRestriction m_mostSpecificRestriction; protected Node m_node; protected DVariable m_nextEntry; protected ValueSpaceSubset m_valueSpaceSubset; protected Object m_dataValue; protected DVariable() { m_positiveConstantEnumerations=new ArrayList<ConstantEnumeration>(); m_negativeConstantEnumerations=new ArrayList<ConstantEnumeration>(); m_positiveDatatypeRestrictions=new ArrayList<DatatypeRestriction>(); m_negativeDatatypeRestrictions=new ArrayList<DatatypeRestriction>(); m_unequalTo=new ArrayList<DVariable>(); m_unequalToDirect=new ArrayList<DVariable>(); m_forbiddenDataValues=new ArrayList<Object>(); m_explicitDataValues=new ArrayList<Object>(); } protected void dispose() { m_positiveConstantEnumerations.clear(); m_negativeConstantEnumerations.clear(); m_positiveDatatypeRestrictions.clear(); m_negativeDatatypeRestrictions.clear(); m_unequalTo.clear(); m_unequalToDirect.clear(); m_forbiddenDataValues.clear(); m_explicitDataValues.clear(); m_hasExplicitDataValues=false; m_mostSpecificRestriction=null; m_node=null; m_nextEntry=null; m_valueSpaceSubset=null; m_dataValue=null; } protected void clearEqualities() { m_unequalTo.clear(); m_unequalToDirect.clear(); } protected void addForbiddenDataValue(Object forbiddenDataValue) { if (!m_forbiddenDataValues.contains(forbiddenDataValue)) m_forbiddenDataValues.add(forbiddenDataValue); } public boolean hasCardinalityAtLeast(int number) { if (m_hasExplicitDataValues) return m_explicitDataValues.size()>=number; else if (m_valueSpaceSubset!=null) return m_valueSpaceSubset.hasCardinalityAtLeast(number+m_forbiddenDataValues.size()); else return true; } public Node getNode() { return m_node; } public List<ConstantEnumeration> getPositiveDataValueEnumerations() { return Collections.unmodifiableList(m_positiveConstantEnumerations); } public List<ConstantEnumeration> getNegativeDataValueEnumerations() { return Collections.unmodifiableList(m_negativeConstantEnumerations); } public List<DatatypeRestriction> getPositiveDatatypeRestrictions() { return Collections.unmodifiableList(m_positiveDatatypeRestrictions); } public List<DatatypeRestriction> getNegativeDatatypeRestrictions() { return Collections.unmodifiableList(m_negativeDatatypeRestrictions); } public List<DVariable> getUnequalToDirect() { return Collections.unmodifiableList(m_unequalToDirect); } public boolean hasSameRestrictions(DVariable that) { return this==that || ( equals(m_positiveConstantEnumerations,that.m_positiveConstantEnumerations) && equals(m_negativeConstantEnumerations,that.m_negativeConstantEnumerations) && equals(m_positiveDatatypeRestrictions,that.m_positiveDatatypeRestrictions) && equals(m_negativeDatatypeRestrictions,that.m_negativeDatatypeRestrictions) ); } protected static <T> boolean equals(List<T> first,List<T> second) { if (first.size()!=second.size()) return false; for (int index=first.size()-1;index>=0;--index) { T object=first.get(index); if (!second.contains(object)) return false; } return true; } public String toString() { return toString(Prefixes.STANDARD_PREFIXES); } public String toString(Prefixes prefixes) { StringBuffer buffer=new StringBuffer(); boolean first=true; buffer.append('['); for (int index=0;index<m_positiveConstantEnumerations.size();index++) { if (first) first=false; else buffer.append(", "); buffer.append(m_positiveConstantEnumerations.get(index).toString(prefixes)); } for (int index=0;index<m_negativeConstantEnumerations.size();index++) { if (first) first=false; else buffer.append(", "); buffer.append(m_negativeConstantEnumerations.get(index).getNegation().toString(prefixes)); } for (int index=0;index<m_positiveDatatypeRestrictions.size();index++) { if (first) first=false; else buffer.append(", "); buffer.append(m_positiveDatatypeRestrictions.get(index).toString(prefixes)); } for (int index=0;index<m_negativeDatatypeRestrictions.size();index++) { if (first) first=false; else buffer.append(", "); buffer.append(m_negativeDatatypeRestrictions.get(index).getNegation().toString(prefixes)); } buffer.append(']'); return buffer.toString(); } } protected static int getIndexFor(int hashCode,int tableLength) { hashCode+=~(hashCode << 9); hashCode^=(hashCode >>> 14); hashCode+=(hashCode << 4); hashCode^=(hashCode >>> 10); return hashCode & (tableLength-1); } protected static class SmallestEnumerationFirst implements Comparator<DVariable>, Serializable { private static final long serialVersionUID = 8838838641444833249L; public static final Comparator<DVariable> INSTANCE=new SmallestEnumerationFirst(); public int compare(DVariable o1,DVariable o2) { return o1.m_explicitDataValues.size()-o2.m_explicitDataValues.size(); } } }