From c2bacb918c99a548aded67932be160b620060eac Mon Sep 17 00:00:00 2001 From: Tobias Hammerschmidt Date: Thu, 18 Aug 2016 11:29:19 +0200 Subject: [PATCH] special handling for comparison of numeric literals --- .../java/org/mindswap/pellet/Literal.java | 673 +++++++++--------- .../pellet/test/LiteralComparisonTest.java | 22 + 2 files changed, 365 insertions(+), 330 deletions(-) create mode 100644 test/src/test/java/com/clarkparsia/pellet/test/LiteralComparisonTest.java diff --git a/core/src/main/java/org/mindswap/pellet/Literal.java b/core/src/main/java/org/mindswap/pellet/Literal.java index 134d00cc5..5f0a66bfd 100644 --- a/core/src/main/java/org/mindswap/pellet/Literal.java +++ b/core/src/main/java/org/mindswap/pellet/Literal.java @@ -49,6 +49,7 @@ import aterm.ATermAppl; import com.clarkparsia.pellet.datatypes.DatatypeReasoner; +import com.clarkparsia.pellet.datatypes.OWLRealUtils; import com.clarkparsia.pellet.datatypes.exceptions.DatatypeReasonerException; import com.clarkparsia.pellet.datatypes.exceptions.InvalidLiteralException; import com.clarkparsia.pellet.datatypes.exceptions.UnrecognizedDatatypeException; @@ -57,415 +58,427 @@ * @author Evren Sirin */ public class Literal extends Node { - private ATermAppl atermValue; - - private Object value; - - // private Datatype datatype; - - private boolean hasValue; - - private NodeMerge merge; - - private boolean clashed = false; - - public Literal(ATermAppl name, ATermAppl term, ABox abox, DependencySet ds) { - - super( name, abox ); - - if( term != null ) { - hasValue = !term.getArgument( ATermUtils.LIT_URI_INDEX ) - .equals( ATermUtils.NO_DATATYPE ); - if( hasValue ) { - try { - value = abox.dtReasoner.getValue( term ); - } catch( InvalidLiteralException e ) { - final String msg = format( - "Attempt to create literal from invalid literal (%s): %s", term, e - .getMessage() ); - if( PelletOptions.INVALID_LITERAL_AS_INCONSISTENCY ) { - log.fine( msg ); - value = null; - } - else { - log.severe( msg ); - throw new InternalReasonerException( msg, e ); - } - } catch( UnrecognizedDatatypeException e ) { - final String msg = format( - "Attempt to create literal from with unrecognized datatype (%s): %s", - term, e.getMessage() ); - log.severe( msg ); - throw new InternalReasonerException( msg, e ); - } - if( value == null ) { - depends.put( name, ds ); - } - } - - atermValue = ATermUtils.makeValue( term ); - } + private ATermAppl atermValue; + + private Object value; + + // private Datatype datatype; + + private boolean hasValue; + + private NodeMerge merge; + + private boolean clashed = false; + + public Literal(ATermAppl name, ATermAppl term, ABox abox, DependencySet ds) { + + super( name, abox ); + + if( term != null ) { + hasValue = !term.getArgument( ATermUtils.LIT_URI_INDEX ) + .equals( ATermUtils.NO_DATATYPE ); + if( hasValue ) { + try { + value = abox.dtReasoner.getValue( term ); + } catch( InvalidLiteralException e ) { + final String msg = format( + "Attempt to create literal from invalid literal (%s): %s", term, e + .getMessage() ); + if( PelletOptions.INVALID_LITERAL_AS_INCONSISTENCY ) { + log.fine( msg ); + value = null; + } + else { + log.severe( msg ); + throw new InternalReasonerException( msg, e ); + } + } catch( UnrecognizedDatatypeException e ) { + final String msg = format( + "Attempt to create literal from with unrecognized datatype (%s): %s", + term, e.getMessage() ); + log.severe( msg ); + throw new InternalReasonerException( msg, e ); + } + if( value == null ) { + depends.put( name, ds ); + } + } + + atermValue = ATermUtils.makeValue( term ); + } else { - hasValue = false; + hasValue = false; } - } + } - public Literal(Literal literal, ABox abox) { - super( literal, abox ); + public Literal(Literal literal, ABox abox) { + super( literal, abox ); - atermValue = literal.atermValue; - value = literal.value; - hasValue = literal.hasValue; - } + atermValue = literal.atermValue; + value = literal.value; + hasValue = literal.hasValue; + } - @Override + @Override public DependencySet getNodeDepends() { - return getDepends( ATermUtils.TOP_LIT ); - } + return getDepends( ATermUtils.TOP_LIT ); + } - @Override + @Override public Node copyTo(ABox abox) { - return new Literal( this, abox ); - } + return new Literal( this, abox ); + } - @Override + @Override final public boolean isLeaf() { - return true; - } + return true; + } - @Override + @Override public int getNominalLevel() { - return isNominal() - ? NOMINAL - : BLOCKABLE; - } + return isNominal() + ? NOMINAL + : BLOCKABLE; + } - @Override + @Override public boolean isNominal() { - return (value != null); - } + return (value != null); + } - @Override + @Override public boolean isBlockable() { - return (value == null); - } + return (value == null); + } - @Override + @Override public boolean isLiteral() { - return true; - } + return true; + } - @Override + @Override public boolean isIndividual() { - return false; - } + return false; + } - @Override + @Override public boolean isDifferent(Node node) { - if( super.isDifferent( node ) ) { - return true; + if( super.isDifferent( node ) ) { + return true; + } + + Literal literal = (Literal) node; + if( hasValue && literal.hasValue ) { + final Class valueClass = value.getClass(); + final Class otherValueClass = literal.value.getClass(); + // XXX due to simplification of numeric types in pellet a special case is needed to compare values of numeric literals + if (isAcceptableNumber( valueClass ) && isAcceptableNumber( otherValueClass )) { + return OWLRealUtils.compare( (Number) value, (Number) literal.value ) != 0; + } else { + return valueClass.equals( otherValueClass ) + && !value.equals( literal.value ); + } } - Literal literal = (Literal) node; - if( hasValue && literal.hasValue ) { - return value.getClass().equals( literal.value.getClass() ) - && !value.equals( literal.value ); - } + return false; + } - return false; - } + @SuppressWarnings("unchecked") + private static boolean isAcceptableNumber(Class cls) { + return OWLRealUtils.acceptable( (Class) cls ); + } - @Override + @Override public boolean hasType(ATerm type) { - if( type instanceof ATermAppl ) { - final ATermAppl a = (ATermAppl) type; - if( ATermUtils.isNominal( a ) ) { - try { - final ATermAppl input = (ATermAppl) a.getArgument( 0 ); - final ATermAppl canonical = abox.getDatatypeReasoner() - .getCanonicalRepresentation( input ); - if( !canonical.equals( input ) ) { - type = ATermUtils.makeValue( canonical ); + if( type instanceof ATermAppl ) { + final ATermAppl a = (ATermAppl) type; + if( ATermUtils.isNominal( a ) ) { + try { + final ATermAppl input = (ATermAppl) a.getArgument( 0 ); + final ATermAppl canonical = abox.getDatatypeReasoner() + .getCanonicalRepresentation( input ); + if( !canonical.equals( input ) ) { + type = ATermUtils.makeValue( canonical ); } - } catch( InvalidLiteralException e ) { - log - .warning( format( - "hasType called with nominal using invalid literal ('%s'), returning false", - e.getMessage() ) ); - return false; - } catch( UnrecognizedDatatypeException e ) { - log - .warning( format( - "hasType called with nominal using literal with unrecognized datatype ('%s'), returning false", - e.getMessage() ) ); - return false; - } - } - } - - if( super.hasType( type ) ) { - return true; + } catch( InvalidLiteralException e ) { + log + .warning( format( + "hasType called with nominal using invalid literal ('%s'), returning false", + e.getMessage() ) ); + return false; + } catch( UnrecognizedDatatypeException e ) { + log + .warning( format( + "hasType called with nominal using literal with unrecognized datatype ('%s'), returning false", + e.getMessage() ) ); + return false; + } + } + } + + if( super.hasType( type ) ) { + return true; } else if( hasValue ) { - if( atermValue.equals( type ) ) { - return true; + if( atermValue.equals( type ) ) { + return true; } - // Datatype datatype = abox.getDatatypeReasoner().getDatatype( - // (ATermAppl) type ); - // if( datatype.contains( value ) ) - // return true; - } + // Datatype datatype = abox.getDatatypeReasoner().getDatatype( + // (ATermAppl) type ); + // if( datatype.contains( value ) ) + // return true; + } - return false; - } + return false; + } - @Override + @Override public DependencySet getDifferenceDependency(Node node) { - DependencySet ds = null; - if( isDifferent( node ) ) { - ds = differents.get( node ); - if( ds == null ) { - ds = DependencySet.INDEPENDENT; + DependencySet ds = null; + if( isDifferent( node ) ) { + ds = differents.get( node ); + if( ds == null ) { + ds = DependencySet.INDEPENDENT; } - } + } - return ds; - } + return ds; + } - @Override + @Override public void addType(ATermAppl c, DependencySet d) { - if( hasType( c ) ) { - return; + if( hasType( c ) ) { + return; } - /* - * A negated nominal is turned into a different - */ - if( ATermUtils.isNot( c ) ) { - final ATermAppl arg = (ATermAppl) c.getArgument( 0 ); - if( ATermUtils.isNominal( arg ) ) { - final ATermAppl v = (ATermAppl) arg.getArgument( 0 ); - Literal other = abox.getLiteral( v ); - if( other == null ) { - other = abox.addLiteral( v, d ); + /* + * A negated nominal is turned into a different + */ + if( ATermUtils.isNot( c ) ) { + final ATermAppl arg = (ATermAppl) c.getArgument( 0 ); + if( ATermUtils.isNominal( arg ) ) { + final ATermAppl v = (ATermAppl) arg.getArgument( 0 ); + Literal other = abox.getLiteral( v ); + if( other == null ) { + other = abox.addLiteral( v, d ); } - super.setDifferent( other, d ); - return; - } - } - super.addType( c, d ); - - // TODO when two literals are being merged this is not efficient - // if(abox.isInitialized()) - checkClash(); - } - - public void addAllTypes(Map types, DependencySet ds) { - for( Entry entry : types.entrySet() ) { - ATermAppl c = entry.getKey(); - - if( hasType( c ) ) { - continue; + super.setDifferent( other, d ); + return; + } + } + super.addType( c, d ); + + // TODO when two literals are being merged this is not efficient + // if(abox.isInitialized()) + checkClash(); + } + + public void addAllTypes(Map types, DependencySet ds) { + for( Entry entry : types.entrySet() ) { + ATermAppl c = entry.getKey(); + + if( hasType( c ) ) { + continue; } - DependencySet depends = entry.getValue(); + DependencySet depends = entry.getValue(); - super.addType( c, depends.union( ds, abox.doExplanation() ) ); - } + super.addType( c, depends.union( ds, abox.doExplanation() ) ); + } - checkClash(); - } + checkClash(); + } - @Override + @Override public boolean hasSuccessor(Node x) { - return false; - } + return false; + } - @Override + @Override public final Literal getSame() { - return (Literal) super.getSame(); - } + return (Literal) super.getSame(); + } - @Override + @Override public ATermAppl getTerm() { - return hasValue - ? (ATermAppl) atermValue.getArgument( 0 ) - : null; - } - - public String getLang() { - return hasValue - ? ((ATermAppl) ((ATermAppl) atermValue.getArgument( 0 )) - .getArgument( ATermUtils.LIT_LANG_INDEX )).getName() - : ""; - } - - public String getLexicalValue() { - if( hasValue ) { - return value.toString(); + return hasValue + ? (ATermAppl) atermValue.getArgument( 0 ) + : null; + } + + public String getLang() { + return hasValue + ? ((ATermAppl) ((ATermAppl) atermValue.getArgument( 0 )) + .getArgument( ATermUtils.LIT_LANG_INDEX )).getName() + : ""; + } + + public String getLexicalValue() { + if( hasValue ) { + return value.toString(); + } + + return null; + } + + void reportClash(Clash clash) { + clashed = true; + abox.setClash( clash ); + } + + private void checkClash() { + clashed = false; + + if( hasValue && value == null ) { + reportClash( Clash.invalidLiteral( this, getDepends( name ), getTerm() ) ); + return; } - return null; - } - - void reportClash(Clash clash) { - clashed = true; - abox.setClash( clash ); - } - - private void checkClash() { - clashed = false; - - if( hasValue && value == null ) { - reportClash( Clash.invalidLiteral( this, getDepends( name ), getTerm() ) ); - return; - } - - if( hasType( ATermUtils.BOTTOM_LIT ) ) { - reportClash( Clash.emptyDatatype( this, getDepends( ATermUtils.BOTTOM_LIT ) ) ); - if( abox.doExplanation() ) { - System.out.println( "1) Literal clash dependency = " + abox.getClash() ); + if( hasType( ATermUtils.BOTTOM_LIT ) ) { + reportClash( Clash.emptyDatatype( this, getDepends( ATermUtils.BOTTOM_LIT ) ) ); + if( abox.doExplanation() ) { + System.out.println( "1) Literal clash dependency = " + abox.getClash() ); } - return; - } + return; + } - final Set types = getTypes(); - final DatatypeReasoner dtReasoner = abox.getDatatypeReasoner(); + final Set types = getTypes(); + final DatatypeReasoner dtReasoner = abox.getDatatypeReasoner(); - try { - if( hasValue ) { + try { + if( hasValue ) { - if( !dtReasoner.isSatisfiable( types, value ) ) { - ArrayList primitives = new ArrayList(); - for( ATermAppl t : types ) { - if( ATermUtils.TOP_LIT.equals( t ) ) { - continue; + if( !dtReasoner.isSatisfiable( types, value ) ) { + ArrayList primitives = new ArrayList(); + for( ATermAppl t : types ) { + if( ATermUtils.TOP_LIT.equals( t ) ) { + continue; } else { - primitives.add( t ); + primitives.add( t ); } - } - - final ATermAppl dt[] = primitives - .toArray( new ATermAppl[primitives.size() - 1] ); - - DependencySet ds = DependencySet.EMPTY; - for( int i = 0; i < dt.length; i++ ) { - ds = ds.union( getDepends( dt[i] ), abox.doExplanation() ); - if (abox.doExplanation()) { - ATermAppl dtName = ATermUtils.isNot(dt[i]) ? (ATermAppl) dt[i].getArgument(0) : dt[i]; - ATermAppl definition = dtReasoner.getDefinition(dtName); - if (definition != null) { - ds = ds.union(Collections.singleton(ATermUtils.makeDatatypeDefinition(dtName, definition)), true); + } + + final ATermAppl dt[] = primitives + .toArray( new ATermAppl[primitives.size() - 1] ); + + DependencySet ds = DependencySet.EMPTY; + for( int i = 0; i < dt.length; i++ ) { + ds = ds.union( getDepends( dt[i] ), abox.doExplanation() ); + if (abox.doExplanation()) { + ATermAppl dtName = ATermUtils.isNot(dt[i]) ? (ATermAppl) dt[i].getArgument(0) : dt[i]; + ATermAppl definition = dtReasoner.getDefinition(dtName); + if (definition != null) { + ds = ds.union(Collections.singleton(ATermUtils.makeDatatypeDefinition(dtName, definition)), true); } - } - } - - reportClash( Clash.valueDatatype( this, ds, (ATermAppl) atermValue.getArgument(0), dt[0] ) ); - } - } - else { - if( dtReasoner.isSatisfiable( types ) ) { - if ( !dtReasoner.containsAtLeast( 2, types ) ) { - /* - * This literal is a variable, but given current ranges can only - * take on a single value. Merge with that value. - */ - final Object value = dtReasoner.valueIterator( types ).next(); - final ATermAppl valueTerm = dtReasoner.getLiteral( value ); - Literal valueLiteral = abox.getLiteral( valueTerm ); - if (valueLiteral == null) { - /* - * No dependency set is used here because omitting it prevents the - * constant literal from being removed during backtrack - */ - valueLiteral = abox.addLiteral( valueTerm ); - } - DependencySet mergeDs = DependencySet.INDEPENDENT; - for ( DependencySet ds : depends.values() ) { - mergeDs = mergeDs.union( ds, abox.doExplanation() ); } - merge = new NodeMerge( this, valueLiteral, mergeDs ); - } - } else { - ArrayList primitives = new ArrayList(); - for( ATermAppl t : types ) { - if( ATermUtils.TOP_LIT.equals( t ) ) { - continue; + } + + reportClash( Clash.valueDatatype( this, ds, (ATermAppl) atermValue.getArgument(0), dt[0] ) ); + } + } + else { + if( dtReasoner.isSatisfiable( types ) ) { + if ( !dtReasoner.containsAtLeast( 2, types ) ) { + /* + * This literal is a variable, but given current ranges can only + * take on a single value. Merge with that value. + */ + final Object value = dtReasoner.valueIterator( types ).next(); + final ATermAppl valueTerm = dtReasoner.getLiteral( value ); + Literal valueLiteral = abox.getLiteral( valueTerm ); + if (valueLiteral == null) { + /* + * No dependency set is used here because omitting it prevents the + * constant literal from being removed during backtrack + */ + valueLiteral = abox.addLiteral( valueTerm ); + } + DependencySet mergeDs = DependencySet.INDEPENDENT; + for ( DependencySet ds : depends.values() ) { + mergeDs = mergeDs.union( ds, abox.doExplanation() ); + } + merge = new NodeMerge( this, valueLiteral, mergeDs ); + } + } else { + ArrayList primitives = new ArrayList(); + for( ATermAppl t : types ) { + if( ATermUtils.TOP_LIT.equals( t ) ) { + continue; } else { - primitives.add( t ); + primitives.add( t ); } - } - - final ATermAppl dt[] = primitives - .toArray( new ATermAppl[primitives.size() - 1] ); - - DependencySet ds = DependencySet.EMPTY; - for( int i = 0; i < dt.length; i++ ) { - ds = ds.union( getDepends( dt[i] ), abox.doExplanation() ); - if (abox.doExplanation()) { - ATermAppl definition = dtReasoner.getDefinition(dt[i]); - if (definition != null) { - ds = ds.union(Collections.singleton(ATermUtils.makeDatatypeDefinition(dt[i], definition)), true); + } + + final ATermAppl dt[] = primitives + .toArray( new ATermAppl[primitives.size() - 1] ); + + DependencySet ds = DependencySet.EMPTY; + for( int i = 0; i < dt.length; i++ ) { + ds = ds.union( getDepends( dt[i] ), abox.doExplanation() ); + if (abox.doExplanation()) { + ATermAppl definition = dtReasoner.getDefinition(dt[i]); + if (definition != null) { + ds = ds.union(Collections.singleton(ATermUtils.makeDatatypeDefinition(dt[i], definition)), true); } - } + } } - reportClash( Clash.emptyDatatype( this, ds, dt ) ); - } - } - } catch( DatatypeReasonerException e ) { - final String msg = "Unexcepted datatype reasoner exception: " + e.getMessage(); - log.severe( msg ); - throw new InternalReasonerException( msg, e ); - } - } - - public Object getValue() { - return value; - } - - @Override + reportClash( Clash.emptyDatatype( this, ds, dt ) ); + } + } + } catch( DatatypeReasonerException e ) { + final String msg = "Unexcepted datatype reasoner exception: " + e.getMessage(); + log.severe( msg ); + throw new InternalReasonerException( msg, e ); + } + } + + public Object getValue() { + return value; + } + + @Override public boolean restore(int branch) { - Boolean restorePruned = restorePruned( branch ); - if( Boolean.FALSE.equals( restorePruned ) ) { - return restorePruned; + Boolean restorePruned = restorePruned( branch ); + if( Boolean.FALSE.equals( restorePruned ) ) { + return restorePruned; } - boolean restored = Boolean.TRUE.equals( restorePruned ); + boolean restored = Boolean.TRUE.equals( restorePruned ); + + restored |= super.restore( branch ); - restored |= super.restore( branch ); - - if (clashed) { - checkClash(); - } + if (clashed) { + checkClash(); + } - return restored; - } + return restored; + } - @Override + @Override final public void prune(DependencySet ds) { - pruned = ds; - } + pruned = ds; + } - @Override + @Override public void unprune(int branch) { - super.unprune( branch ); + super.unprune( branch ); - checkClash(); - } + checkClash(); + } - public String debugString() { - return name + " = " + getTypes().toString(); - } + public String debugString() { + return name + " = " + getTypes().toString(); + } - public NodeMerge getMergeToConstant() { - return merge; - } + public NodeMerge getMergeToConstant() { + return merge; + } - public void clearMergeToConstant() { - merge = null; - } -} \ No newline at end of file + public void clearMergeToConstant() { + merge = null; + } +} diff --git a/test/src/test/java/com/clarkparsia/pellet/test/LiteralComparisonTest.java b/test/src/test/java/com/clarkparsia/pellet/test/LiteralComparisonTest.java new file mode 100644 index 000000000..ad07e5b0d --- /dev/null +++ b/test/src/test/java/com/clarkparsia/pellet/test/LiteralComparisonTest.java @@ -0,0 +1,22 @@ +package com.clarkparsia.pellet.test; + +import org.junit.Assert; +import org.junit.Test; +import org.mindswap.pellet.ABox; +import org.mindswap.pellet.KnowledgeBase; +import org.mindswap.pellet.Literal; + +import com.clarkparsia.pellet.utils.TermFactory; + +public class LiteralComparisonTest { + + @Test + public void numericLiteralComparison() { + final KnowledgeBase kb = new KnowledgeBase(); + ABox abox = new ABox( kb ); + Literal byteLiteral = abox.addLiteral( TermFactory.literal( (byte) 0 ) ); + Literal shortLiteral = abox.addLiteral( TermFactory.literal( (short ) 200) ); + Assert.assertTrue( "numeric literals should be different", byteLiteral.isDifferent( shortLiteral ) ); + } + +}