All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.clarkparsia.pellet.sparqldl.engine.CombinedQueryEngine Maven / Gradle / Ivy

// Copyright (c) 2006 - 2008, Clark & Parsia, LLC. 
// This source code is available under the terms of the Affero General Public
// License v3.
//
// Please see LICENSE.txt for full license terms, including the availability of
// proprietary exceptions.
// Questions, comments, or requests for clarification: [email protected]

package com.clarkparsia.pellet.sparqldl.engine;

import static com.clarkparsia.pellet.utils.TermFactory.hasValue;
import static com.clarkparsia.pellet.utils.TermFactory.inv;
import static com.clarkparsia.pellet.utils.TermFactory.not;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.mindswap.pellet.KnowledgeBase;
import org.mindswap.pellet.PelletOptions;
import org.mindswap.pellet.exceptions.InternalReasonerException;
import org.mindswap.pellet.exceptions.UnsupportedQueryException;
import org.mindswap.pellet.taxonomy.Taxonomy;
import org.mindswap.pellet.taxonomy.TaxonomyNode;
import org.mindswap.pellet.utils.ATermUtils;
import org.mindswap.pellet.utils.CandidateSet;
import org.mindswap.pellet.utils.DisjointSet;
import org.mindswap.pellet.utils.Timer;

import aterm.ATermAppl;

import com.clarkparsia.pellet.sparqldl.model.CoreNewImpl;
import com.clarkparsia.pellet.sparqldl.model.NotKnownQueryAtom;
import com.clarkparsia.pellet.sparqldl.model.Query;
import com.clarkparsia.pellet.sparqldl.model.QueryAtom;
import com.clarkparsia.pellet.sparqldl.model.QueryAtomFactory;
import com.clarkparsia.pellet.sparqldl.model.QueryImpl;
import com.clarkparsia.pellet.sparqldl.model.QueryPredicate;
import com.clarkparsia.pellet.sparqldl.model.QueryResult;
import com.clarkparsia.pellet.sparqldl.model.QueryResultImpl;
import com.clarkparsia.pellet.sparqldl.model.ResultBinding;
import com.clarkparsia.pellet.sparqldl.model.ResultBindingImpl;
import com.clarkparsia.pellet.sparqldl.model.UnionQueryAtom;
import com.clarkparsia.pellet.sparqldl.model.Query.VarType;

/**
 * 

* Title: Engine for queries with only distinguished variables. *

*

* Description: All variable name spaces are disjoint. *

*

* Copyright: Copyright (c) 2007 *

*

* Company: Clark & Parsia, LLC. *

* * @author Petr Kremen */ public class CombinedQueryEngine implements QueryExec { public static final Logger log = Logger.getLogger( CombinedQueryEngine.class .getName() ); public static final QueryOptimizer optimizer = new QueryOptimizer(); private KnowledgeBase kb; protected QueryPlan plan; protected Query oldQuery; protected Query query; private QueryResult result; private Set downMonotonic; private void prepare(Query query) { if( log.isLoggable( Level.FINE ) ) { log.fine( "Preparing plan ..." ); } this.kb = query.getKB(); if( kb == null ) { throw new RuntimeException( "No input data set is given for query!" ); } this.result = new QueryResultImpl( query ); this.oldQuery = query; this.query = setupCores( query ); if( log.isLoggable( Level.FINE ) ) { log.fine( "After setting-up cores : " + this.query ); } this.plan = optimizer.getExecutionPlan( this.query ); this.plan.reset(); // warm up the reasoner by computing the satisfiability of classes // used in the query so that cached models can be used for instance // checking - TODO also non-named classes if( (PelletOptions.USE_CACHING) && !kb.isClassified() ) { for( final QueryAtom a : oldQuery.getAtoms() ) { for( final ATermAppl arg : a.getArguments() ) { if( kb.isClass( arg ) ) { kb.isSatisfiable( arg ); kb.isSatisfiable( ATermUtils.makeNot( arg ) ); } } } } if( PelletOptions.OPTIMIZE_DOWN_MONOTONIC ) { // TODO use down monotonic variables for implementation of // DirectType atom downMonotonic = new HashSet(); setupDownMonotonicVariables( this.query ); if( log.isLoggable( Level.FINE ) ) { log.fine( "Variables to be optimized : " + downMonotonic ); } } } // computes cores of undistinguished variables private Query setupCores(final Query query) { final Iterator undistVarIterator = query.getUndistVars().iterator(); if( !undistVarIterator.hasNext() ) { return query; } final DisjointSet coreVertices = new DisjointSet(); final List toRemove = new ArrayList(); while( undistVarIterator.hasNext() ) { final ATermAppl a = undistVarIterator.next(); coreVertices.add( a ); for( final QueryAtom atom : query.findAtoms( QueryPredicate.PropertyValue, a, null, null ) ) { coreVertices.add( atom ); coreVertices.union( a, atom ); final ATermAppl a2 = atom.getArguments().get( 2 ); if( query.getUndistVars().contains( a2 ) ) { coreVertices.add( a2 ); coreVertices.union( a, a2 ); } toRemove.add( atom ); } for( final QueryAtom atom : query.findAtoms( QueryPredicate.PropertyValue, null, null, a ) ) { coreVertices.add( atom ); coreVertices.union( a, atom ); final ATermAppl a2 = atom.getArguments().get( 0 ); if( query.getUndistVars().contains( a2 ) ) { coreVertices.add( a2 ); coreVertices.union( a, a2 ); } toRemove.add( atom ); } for( final QueryAtom atom : query.findAtoms( QueryPredicate.Type, a, null ) ) { coreVertices.add( atom ); coreVertices.union( a, atom ); toRemove.add( atom ); } } final Query transformedQuery = query.apply( new ResultBindingImpl() ); for( final Set set : coreVertices.getEquivalanceSets() ) { final Collection atoms = new ArrayList(); for( final Object a : set ) { if( a instanceof QueryAtom ) { atoms.add( (QueryAtom) a ); } } final CoreNewImpl c = (CoreNewImpl) QueryAtomFactory.Core( atoms, query.getUndistVars(), kb ); transformedQuery.add( c ); if( log.isLoggable( Level.FINE ) ) { log.fine( c.getUndistVars() + " : " + c.getDistVars() + " : " + c.getQuery().getAtoms() ); } } for( final QueryAtom atom : toRemove ) { transformedQuery.remove( atom ); } return transformedQuery; } // down-monotonic variables = Class variables in Type atoms and Property // variables in PropertyValue atoms private void setupDownMonotonicVariables(final Query query) { for( final QueryAtom atom : query.getAtoms() ) { ATermAppl arg; switch ( atom.getPredicate() ) { case PropertyValue: case Type: arg = atom.getArguments().get( 1 ); if( ATermUtils.isVar( arg ) ) { downMonotonic.add( arg ); } break; default: arg = null; } } } /** * {@inheritDoc} */ public boolean supports(Query q) { // TODO cycles in undist vars and fully undist.vars queries are not // supported !!! return true; } /** * {@inheritDoc} */ public QueryResult exec(Query query) { if( log.isLoggable( Level.FINE ) ) { log.fine( "Executing query " + query ); } Timer timer = new Timer( "CombinedQueryEngine" ); timer.start(); prepare( query ); branches = 0; exec( new ResultBindingImpl() ); timer.stop(); if( log.isLoggable( Level.FINE ) ) { log.log( Level.FINE, "#B=" + branches + ", time=" + timer.getLast() + " ms." ); } return result; } private long branches; private void exec(ResultBinding binding) { if( log.isLoggable( Level.FINE ) ) { branches++; } if( !plan.hasNext() ) { // TODO if result vars are not same as dist vars. if( !binding.isEmpty() || result.isEmpty() ) { if( log.isLoggable( Level.FINE ) ) { log.fine( "Found binding: " + binding ); } // Filter filter = query.getFilter(); if( !result.getResultVars().containsAll( binding.getAllVariables() ) ) { ResultBinding newBinding = new ResultBindingImpl(); for( ATermAppl var : result.getResultVars() ) { ATermAppl value = binding.getValue( var ); newBinding.setValue( var, value ); } binding = newBinding; } result.add( binding ); } if( log.isLoggable( Level.FINER ) ) { log.finer( "Returning ... binding=" + binding ); } return; } final QueryAtom current = plan.next( binding ); if( log.isLoggable( Level.FINER ) ) { log.finer( "Evaluating " + current ); } if( current.isGround() && !current.getPredicate().equals( QueryPredicate.UndistVarCore ) ) { if( QueryEngine.checkGround( current, kb ) ) { exec( binding ); } } else { exec( current, binding ); } if( log.isLoggable( Level.FINER ) ) { log.finer( "Returning ... " + binding ); } plan.back(); } private void exec(QueryAtom current, ResultBinding binding) { final List arguments = current.getArguments(); boolean direct = false; boolean strict = false; switch ( current.getPredicate() ) { case DirectType: direct = true; case Type: // TODO implementation of downMonotonic vars final ATermAppl tI = arguments.get( 0 ); final ATermAppl tC = arguments.get( 1 ); Set instanceCandidates = null; if( tI.equals( tC ) ) { instanceCandidates = kb.getIndividuals().size() < kb.getClasses().size() ? kb.getIndividuals() : kb.getClasses(); for( final ATermAppl ic : instanceCandidates ) { if( direct ? kb.getInstances( ic, direct ).contains( ic ) : kb.isType( ic, ic ) ) { final ResultBinding candidateBinding = binding.duplicate(); if( ATermUtils.isVar( tI ) ) { candidateBinding.setValue( tI, ic ); } exec( candidateBinding ); } } } else { final Set classCandidates; if( !ATermUtils.isVar( tC ) ) { classCandidates = Collections.singleton( tC ); instanceCandidates = kb.getInstances( tC, direct ); } else if( !ATermUtils.isVar( tI ) ) { // classCandidates = flatten(TaxonomyUtils.getTypes(kb // .getTaxonomy(), tI, direct)); // TODO classCandidates = flatten( kb.getTypes( tI, direct ) ); // TODO instanceCandidates = Collections.singleton( tI ); } else { classCandidates = kb.getAllClasses(); } // explore all possible bindings boolean loadInstances = (instanceCandidates == null); for( final ATermAppl cls : classCandidates ) { if( loadInstances ) { instanceCandidates = kb.getInstances( cls, direct ); } for( final ATermAppl inst : instanceCandidates ) { runNext( binding, arguments, inst, cls ); } } // finish explore bindings } break; case PropertyValue: // TODO implementation of downMonotonic vars final ATermAppl pvI = arguments.get( 0 ); final ATermAppl pvP = arguments.get( 1 ); final ATermAppl pvIL = arguments.get( 2 ); Collection propertyCandidates = null; Collection subjectCandidates = null; Collection objectCandidates = null; boolean loadProperty = false; boolean loadSubjects = false; boolean loadObjects = false; if( !ATermUtils.isVar( pvP ) ) { propertyCandidates = Collections.singleton( pvP ); if( !ATermUtils.isVar( pvI ) ) { subjectCandidates = Collections.singleton( pvI ); objectCandidates = kb.getPropertyValues( pvP, pvI ); } else if( !ATermUtils.isVar( pvIL ) ) { objectCandidates = Collections.singleton( pvIL ); subjectCandidates = kb.getIndividualsWithProperty( pvP, pvIL ); } loadProperty = false; } else { if( !ATermUtils.isVar( pvI ) ) { subjectCandidates = Collections.singleton( pvI ); } if( !ATermUtils.isVar( pvIL ) ) { objectCandidates = Collections.singleton( pvIL ); } else if( !plan.getQuery().getDistVarsForType( VarType.LITERAL ).contains( pvIL ) ) { propertyCandidates = kb.getObjectProperties(); } if( propertyCandidates == null ) { propertyCandidates = kb.getProperties(); } loadProperty = true; } loadSubjects = (subjectCandidates == null); loadObjects = (objectCandidates == null); for( final ATermAppl property : propertyCandidates ) { // TODO replace this nasty if-cascade with some map for // var // bindings. if( loadObjects && loadSubjects ) { if( pvI.equals( pvIL ) ) { if( pvI.equals( pvP ) ) { if( !kb.hasPropertyValue( property, property, property ) ) { continue; } runNext( binding, arguments, property, property, property ); } else { for( final ATermAppl i : kb.getIndividuals() ) { if( !kb.hasPropertyValue( i, property, i ) ) { continue; } runNext( binding, arguments, i, property, i ); } } } else { if( pvI.equals( pvP ) ) { for( final ATermAppl i : kb.getIndividuals() ) { if( !kb.hasPropertyValue( property, property, i ) ) { continue; } runNext( binding, arguments, property, property, i ); } } else if( pvIL.equals( pvP ) ) { for( final ATermAppl i : kb.getIndividuals() ) { if( !kb.hasPropertyValue( i, property, property ) ) { continue; } runNext( binding, arguments, i, property, property ); } } else { for( final ATermAppl subject : kb.getIndividuals() ) { for( final ATermAppl object : kb.getPropertyValues( property, subject ) ) { runNext( binding, arguments, subject, property, object ); } } } } } else if( loadObjects ) { // subject is known. if( pvP.equals( pvIL ) ) { if( !kb.hasPropertyValue( subjectCandidates.iterator().next(), property, property ) ) { // terminate subjectCandidates = Collections.emptySet(); } } for( final ATermAppl subject : subjectCandidates ) { for( final ATermAppl object : kb.getPropertyValues( property, subject ) ) { runNext( binding, arguments, subject, property, object ); } } } else { // object is known. for( final ATermAppl object : objectCandidates ) { if( loadSubjects ) { if( pvI.equals( pvP ) ) { if( kb.hasPropertyValue( property, property, object ) ) { subjectCandidates = Collections.singleton( property ); } else { // terminate subjectCandidates = Collections.emptySet(); } } else { subjectCandidates = new HashSet( kb .getIndividualsWithProperty( property, object ) ); } } for( final ATermAppl subject : subjectCandidates ) { if( loadProperty && !kb.hasPropertyValue( subject, property, object ) ) { continue; } runNext( binding, arguments, subject, property, object ); } } } } // finish visiting non-ground triple. break; case SameAs: // optimize - merge nodes final ATermAppl saI1 = arguments.get( 0 ); final ATermAppl saI2 = arguments.get( 1 ); for( final ATermAppl known : getSymmetricCandidates( VarType.INDIVIDUAL, saI1, saI2 ) ) { final Set dependents; if( saI1.equals( saI2 ) ) { dependents = Collections.singleton( known ); } else { dependents = kb.getAllSames( known ); } for( final ATermAppl dependent : dependents ) { runSymetricCheck( current, saI1, known, saI2, dependent, binding ); } } break; case DifferentFrom: // optimize - different from map final ATermAppl dfI1 = arguments.get( 0 ); final ATermAppl dfI2 = arguments.get( 1 ); if( !dfI1.equals( dfI2 ) ) { for( final ATermAppl known : getSymmetricCandidates( VarType.INDIVIDUAL, dfI1, dfI2 ) ) { for( final ATermAppl dependent : kb.getDifferents( known ) ) { runSymetricCheck( current, dfI1, known, dfI2, dependent, binding ); } } } else { if( log.isLoggable( Level.FINER ) ) { log.finer( "Atom " + current + "cannot be satisfied in any consistent ontology." ); } } // TODO What about undist vars ? // Query : PropertyValue(?x,p,_:x), Type(_:x, C), // DifferentFrom( _:x, x) . // Data : p(a,x) . p(b,y) . C(x) . C(y) . // Result: {b} // // Data : p(a,x) . (exists p (C and {y}))(b) . C(x) . // Result: {y} // // rolling-up to ?x : (exists p (C and not {x}))(?x) . // // More complex problems : // Query : PropertyValue(?x,p,_:x), Type(_:x, C), // DifferentFrom( _:x, _:y) . Type(_:y, T) . // Data : p(a,x) . C(x) . // Result: {a} // // Query : PropertyValue(?x,p,_:x), Type(_:x, C), // DifferentFrom( _:x, _:y) . Type(_:y, T) . // Data : p(x,x) . C(x) . // Result: {} // // Query : PropertyValue(?x,p,_:x), Type(_:x, C), // DifferentFrom( _:x, _:y) . Type(_:y, D) . // Data : p(a,x) . C(x) . D(a) . // Result: {a} // // rolling-up to ?x : (exists p (C and (not D)))(?x) . // // rolling-up to _:x of DifferentFrom(_:x,_:y) : // roll-up(_:x) and (not roll-up(_:y)). // but it is not complete if the rolling-up to _:y is not // complete, but just a preprocessing (for example _:y is in // a cycle). break; case Annotation: final ATermAppl aI = arguments.get( 0 ); final ATermAppl aP = arguments.get( 1 ); final ATermAppl aIL = arguments.get( 2 ); subjectCandidates = null; objectCandidates = null; propertyCandidates = null; //if aI is a variable, get all the annotation subjects if( ATermUtils.isVar( aI ) ) { subjectCandidates = kb.getAnnotationSubjects(); } //else, we only have one subject candidate else { subjectCandidates = Collections.singleton( aI ); } //if aP is a variable, get all the annotation properties if( ATermUtils.isVar( aP ) ) { propertyCandidates = kb.getAnnotationProperties(); } //else, we only have one property candidate else { propertyCandidates = Collections.singleton( aP ); } //if aIL is a variable, get all the annotation objects for the subject and the property candidates if( ATermUtils.isVar( aIL ) ) { for( final ATermAppl subject : subjectCandidates ) { for( final ATermAppl property : propertyCandidates ) { for( final ATermAppl object : kb.getAnnotations( subject, property ) ) { runNext( binding, arguments, subject, property, object ); } } } } //else, we only have one object candidate else { for( final ATermAppl subject : subjectCandidates ) { for( final ATermAppl property : propertyCandidates ) { if( kb.isAnnotation(subject, property, aIL) ) { runNext( binding, arguments, subject, property, aIL ); } } } } break; // throw new IllegalArgumentException("The annotation atom " // + current + " should be ground, but is not."); // TBOX ATOMS case DirectSubClassOf: direct = true; case StrictSubClassOf: strict = true; case SubClassOf: final ATermAppl scLHS = arguments.get( 0 ); final ATermAppl scRHS = arguments.get( 1 ); if( scLHS.equals( scRHS ) ) { // TODO optimization for downMonotonic variables for( final ATermAppl ic : kb.getClasses() ) { runNext( binding, arguments, ic, ic ); } } else { final boolean lhsDM = isDownMonotonic( scLHS ); final boolean rhsDM = isDownMonotonic( scRHS ); if( lhsDM || rhsDM ) { downMonotonic( kb.getTaxonomy(), kb.getClasses(), lhsDM, scLHS, scRHS, binding, direct, strict ); } else { final Set lhsCandidates; Set rhsCandidates = null; if( !ATermUtils.isVar( scLHS ) ) { lhsCandidates = Collections.singleton( scLHS ); rhsCandidates = flatten( kb.getSuperClasses( scLHS, direct ) ); rhsCandidates.addAll( kb.getEquivalentClasses( scLHS ) ); if( strict ) { rhsCandidates.removeAll( kb.getEquivalentClasses( scLHS ) ); } else if( !ATermUtils.isComplexClass( scLHS ) ) { rhsCandidates.add( scLHS ); } } else if( !ATermUtils.isVar( scRHS ) ) { rhsCandidates = Collections.singleton( scRHS ); if( scRHS.equals( ATermUtils.TOP ) ) { lhsCandidates = new HashSet( kb.getAllClasses() ); } else { lhsCandidates = flatten( kb.getSubClasses( scRHS, direct ) ); lhsCandidates.addAll( kb.getAllEquivalentClasses( scRHS ) ); } if( strict ) { lhsCandidates.removeAll( kb.getAllEquivalentClasses( scRHS ) ); } } else { lhsCandidates = kb.getClasses(); } boolean reload = (rhsCandidates == null); for( final ATermAppl subject : lhsCandidates ) { if( reload ) { rhsCandidates = flatten( kb.getSuperClasses( subject, direct ) ); if( strict ) { rhsCandidates.removeAll( kb.getEquivalentClasses( subject ) ); } else if( !ATermUtils.isComplexClass( subject ) ) { rhsCandidates.add( subject ); } } for( final ATermAppl object : rhsCandidates ) { runNext( binding, arguments, subject, object ); } } } } break; case EquivalentClass: // TODO implementation of downMonotonic vars final ATermAppl eqcLHS = arguments.get( 0 ); final ATermAppl eqcRHS = arguments.get( 1 ); for( final ATermAppl known : getSymmetricCandidates( VarType.CLASS, eqcLHS, eqcRHS ) ) { // TODO optimize - try just one - if success then take // all // found bindings and extend them for other equivalent // classes as well. // meanwhile just a simple check below final Set dependents; if( eqcLHS.equals( eqcRHS ) ) { dependents = Collections.singleton( known ); } else { dependents = kb.getEquivalentClasses( known ); } for( final ATermAppl dependent : dependents ) { int size = result.size(); runSymetricCheck( current, eqcLHS, known, eqcRHS, dependent, binding ); if( result.size() == size ) { // no binding found, so that there is no need to // explore other equivalent classes - they fail // as // well. break; } } } break; case DisjointWith: // TODO implementation of downMonotonic vars final ATermAppl dwLHS = arguments.get( 0 ); final ATermAppl dwRHS = arguments.get( 1 ); if( !dwLHS.equals( dwRHS ) ) { // TODO optimizeTBox for( final ATermAppl known : getSymmetricCandidates( VarType.CLASS, dwLHS, dwRHS ) ) { for( final Set dependents : kb.getDisjointClasses( known ) ) { for( final ATermAppl dependent : dependents ) { runSymetricCheck( current, dwLHS, known, dwRHS, dependent, binding ); } } } } else { log.finer( "Atom " + current + "cannot be satisfied in any consistent ontology." ); } break; case ComplementOf: // TODO implementation of downMonotonic vars final ATermAppl coLHS = arguments.get( 0 ); final ATermAppl coRHS = arguments.get( 1 ); if( !coLHS.equals( coRHS ) ) { // TODO optimizeTBox for( final ATermAppl known : getSymmetricCandidates( VarType.CLASS, coLHS, coRHS ) ) { for( final ATermAppl dependent : kb.getComplements( known ) ) { runSymetricCheck( current, coLHS, known, coRHS, dependent, binding ); } } } else { log.finer( "Atom " + current + "cannot be satisfied in any consistent ontology." ); } break; // RBOX ATOMS case DirectSubPropertyOf: direct = true; case StrictSubPropertyOf: strict = true; case SubPropertyOf: final ATermAppl spLHS = arguments.get( 0 ); final ATermAppl spRHS = arguments.get( 1 ); if( spLHS.equals( spRHS ) ) { // TODO optimization for downMonotonic variables for( final ATermAppl ic : kb.getProperties() ) { runNext( binding, arguments, ic, ic ); } } else { final boolean lhsDM = isDownMonotonic( spLHS ); final boolean rhsDM = isDownMonotonic( spRHS ); if( lhsDM || rhsDM ) { downMonotonic( kb.getRoleTaxonomy(true), kb.getProperties(), lhsDM, spLHS, spRHS, binding, direct, strict ); } else { final Set spLhsCandidates; Set spRhsCandidates = null; if( !ATermUtils.isVar( spLHS ) ) { spLhsCandidates = Collections.singleton( spLHS ); spRhsCandidates = flatten( kb.getSuperProperties( spLHS, direct ) ); if( strict ) { spRhsCandidates.removeAll( kb.getEquivalentProperties( spLHS ) ); } else { spRhsCandidates.add( spLHS ); } } else if( !ATermUtils.isVar( spRHS ) ) { spRhsCandidates = Collections.singleton( spRHS ); spLhsCandidates = flatten( kb.getSubProperties( spRHS, direct ) ); if( strict ) { spLhsCandidates.removeAll( kb.getEquivalentProperties( spRHS ) ); } else { spLhsCandidates.add( spRHS ); } } else { spLhsCandidates = kb.getProperties(); } boolean reload = (spRhsCandidates == null); for( final ATermAppl subject : spLhsCandidates ) { if( reload ) { spRhsCandidates = flatten( kb.getSuperProperties( subject, direct ) ); if( strict ) { spRhsCandidates .removeAll( kb.getEquivalentProperties( subject ) ); } else { spRhsCandidates.add( subject ); } } for( final ATermAppl object : spRhsCandidates ) { runNext( binding, arguments, subject, object ); } } } } break; case EquivalentProperty: // TODO implementation of downMonotonic // vars final ATermAppl eqpLHS = arguments.get( 0 ); final ATermAppl eqpRHS = arguments.get( 1 ); // TODO optimize - try just one - if success then take all // found // bindings and extend them for other equivalent classes as // well. // meanwhile just a simple check below for( final ATermAppl known : getSymmetricCandidates( VarType.PROPERTY, eqpLHS, eqpRHS ) ) { final Set dependents; if( eqpLHS.equals( eqpRHS ) ) { dependents = Collections.singleton( known ); } else { dependents = kb.getEquivalentProperties( known ); } for( final ATermAppl dependent : dependents ) { int size = result.size(); runSymetricCheck( current, eqpLHS, known, eqpRHS, dependent, binding ); if( result.size() == size ) { // no binding found, so that there is no need to // explore other equivalent classes - they fail // as // well. break; } } } break; case Domain: final ATermAppl domLHS = arguments.get( 0 ); final ATermAppl domRHS = arguments.get( 1 ); Collection domLhsCandidates; Collection domRhsCandidates; if ( !ATermUtils.isVar( domLHS ) ) { domLhsCandidates = Collections.singleton( domLHS ); } else { domLhsCandidates = kb.getProperties(); } if ( !ATermUtils.isVar( domRHS ) ) { domRhsCandidates = Collections.singleton( domRHS ); } else { domRhsCandidates = kb.getAllClasses(); } for ( ATermAppl prop : domLhsCandidates) { for ( ATermAppl cls : domRhsCandidates ) { //System.out.println("Checking dom(" + prop + ", " + cls + ")"); if ( (kb.isDatatypeProperty( prop ) || kb.isObjectProperty( prop )) && kb.hasDomain( prop, cls ) ) { runNext( binding, arguments, prop, cls ); } } } break; case Range: final ATermAppl rangeLHS = arguments.get( 0 ); final ATermAppl rangeRHS = arguments.get( 1 ); Collection rangeLhsCandidates; Collection rangeRhsClassCandidates; Collection rangeRhsDTypeCandidates; if ( !ATermUtils.isVar( rangeLHS ) ) { rangeLhsCandidates = Collections.singleton( rangeLHS ); } else { rangeLhsCandidates = kb.getProperties(); } if ( !ATermUtils.isVar( rangeRHS ) ) { //System.out.println( "Bound range: " + rangeRHS ); if ( kb.isDatatype( rangeRHS ) ) { rangeRhsClassCandidates = Collections.emptySet(); rangeRhsDTypeCandidates = Collections.singleton( rangeRHS ); } else { rangeRhsClassCandidates = Collections.singleton( rangeRHS ); rangeRhsDTypeCandidates = Collections.emptySet(); } } else { rangeRhsClassCandidates = kb.getAllClasses(); // TODO : change the datatype reasoner to keep track of associated aterms. rangeRhsDTypeCandidates = new HashSet(); for (ATermAppl dtype : kb.getDatatypeReasoner().listDataRanges() ) { rangeRhsDTypeCandidates.add( dtype ); } } for ( ATermAppl prop : rangeLhsCandidates) { if( kb.isObjectProperty( prop ) ) { for( ATermAppl cls : rangeRhsClassCandidates ) { //System.out.println("Checking range(" + prop + ", " + cls + ")"); if( kb.hasRange( prop, cls ) ) { runNext( binding, arguments, prop, cls ); } } } else if ( kb.isDatatypeProperty( prop ) ) { for( ATermAppl dtype : rangeRhsDTypeCandidates ) { //System.out.println("Checking range(" + prop + ", " + dtype + ")"); if ( kb.hasRange( prop, dtype ) ) { runNext( binding, arguments, prop, dtype ); } } } } break; case InverseOf: // TODO implementation of downMonotonic vars final ATermAppl ioLHS = arguments.get( 0 ); final ATermAppl ioRHS = arguments.get( 1 ); if( ioLHS.equals( ioRHS ) ) { runAllPropertyChecks( current, arguments.get( 0 ), kb.getSymmetricProperties(), binding ); } else { for( final ATermAppl known : getSymmetricCandidates( VarType.PROPERTY, ioLHS, ioRHS ) ) { // meanwhile workaround for( final ATermAppl dependent : kb.getInverses( known ) ) { runSymetricCheck( current, ioLHS, known, ioRHS, dependent, binding ); } } } break; case Symmetric: runAllPropertyChecks( current, arguments.get( 0 ), kb.getSymmetricProperties(), binding ); break; case Asymmetric: runAllPropertyChecks( current, arguments.get( 0 ), kb.getAsymmetricProperties(), binding ); break; case Reflexive: runAllPropertyChecks( current, arguments.get( 0 ), kb.getReflexiveProperties(), binding ); break; case Irreflexive: runAllPropertyChecks( current, arguments.get( 0 ), kb.getIrreflexiveProperties(), binding ); break; case ObjectProperty: runAllPropertyChecks( current, arguments.get( 0 ), kb.getObjectProperties(), binding ); break; case DatatypeProperty: runAllPropertyChecks( current, arguments.get( 0 ), kb.getDataProperties(), binding ); break; case Functional: runAllPropertyChecks( current, arguments.get( 0 ), kb.getFunctionalProperties(), binding ); break; case InverseFunctional: runAllPropertyChecks( current, arguments.get( 0 ), kb .getInverseFunctionalProperties(), binding ); break; case Transitive: runAllPropertyChecks( current, arguments.get( 0 ), kb.getTransitiveProperties(), binding ); break; case UndistVarCore: // TODO Core IF final CoreNewImpl core = (CoreNewImpl) current.apply( binding ); final Collection distVars = core.getDistVars(); if( distVars.isEmpty() ) { final Collection constants = core.getConstants(); if( constants.isEmpty() ) { if( QueryEngine.execBooleanABoxQuery( core.getQuery() ) ) { result.add( binding ); // throw new RuntimeException( // "The query contains neither dist vars, nor constants, // yet evaluated by the CombinedQueryEngine !!! "); } } else { final ATermAppl c = constants.iterator().next(); final ATermAppl clazz = core.getQuery().rollUpTo( c, Collections.emptySet(), STOP_ROLLING_ON_CONSTANTS ); if( kb.isType( c, clazz ) ) { exec( binding ); } } } else if( distVars.size() == 1 ) { final ATermAppl var = distVars.iterator().next(); final ATermAppl c = core.getQuery().rollUpTo( var, Collections.emptySet(), STOP_ROLLING_ON_CONSTANTS ); final Collection instances = kb.getInstances( c ); for( final ATermAppl a : instances ) { final ResultBinding candidateBinding = binding.duplicate(); candidateBinding.setValue( var, a ); exec( candidateBinding ); } } else { // TODO // if (distVars.size() == 2 // && core.getUndistVars().size() == 1 // && !kb.getExpressivity().hasNominal() // && !kb.getExpressivity().hasTransitivity()) { // // TODO 1. undist. var. in distinguished manner // // TODO 2. identify both DV's // } final CoreStrategy s = QueryEngine.getStrategy( current ); switch ( s ) { case SIMPLE: execSimpleCore( oldQuery, binding, distVars ); break; case ALLFAST: execAllFastCore( oldQuery, binding, distVars, core.getUndistVars() ); break; default: throw new InternalReasonerException( "Unknown core strategy." ); } } break; case NegativePropertyValue: { final ATermAppl s = arguments.get( 0 ); final ATermAppl p = arguments.get( 1 ); final ATermAppl o = arguments.get( 2 ); if( ATermUtils.isVar( p ) ) { throw new UnsupportedQueryException( "NegativePropertyValue atom with a variable property not supported" ); } if( ATermUtils.isVar( o ) && kb.isDatatypeProperty( p ) ) { throw new UnsupportedQueryException( "NegativePropertyValue atom with a datatype property and variable object not supported" ); } if( ATermUtils.isVar( s ) ) { Set oValues = ATermUtils.isVar( o ) ? kb.getIndividuals() : Collections.singleton( o ); for( ATermAppl oValue : oValues ) { Set sValues = kb.getInstances( not( hasValue( p, oValue ) ) ); for( ATermAppl sValue : sValues ) { runNext( binding, arguments, sValue, p, oValue ); } } } else if( ATermUtils.isVar( o ) ) { Set oValues = kb.getInstances( not( hasValue( inv( p ), o ) ) ); for( ATermAppl oValue : oValues ) { runNext( binding, arguments, s, p, oValue ); } } else { if( kb.isType( s, hasValue( p, o ) ) ) { exec( binding ); } } break; } case NotKnown: { Query newQuery = new QueryImpl( kb, true ); for( QueryAtom atom : ((NotKnownQueryAtom) current).getAtoms() ) { newQuery.add( atom.apply( binding ) ); } for( ATermAppl var : newQuery.getUndistVars() ) { newQuery.addDistVar( var, VarType.INDIVIDUAL ); } QueryExec newEngine = new CombinedQueryEngine(); boolean isNegationTrue = newEngine.exec( newQuery ).isEmpty(); if( isNegationTrue ) { exec( binding ); } break; } case Union: { for( List atoms : ((UnionQueryAtom) current).getUnion() ) { Query newQuery = new QueryImpl( kb, true ); for( QueryAtom atom : atoms ) { newQuery.add( atom.apply( binding ) ); } for( ATermAppl var : newQuery.getUndistVars() ) { newQuery.addDistVar( var, VarType.INDIVIDUAL ); newQuery.addResultVar( var ); } QueryExec newEngine = new CombinedQueryEngine(); QueryResult newResult = newEngine.exec( newQuery ); for( ResultBinding newBinding : newResult ) { newBinding.setValues( binding ); exec( newBinding ); } } break; } case Datatype: throw new UnsupportedQueryException( "Datatype atom not ground: " + current ); case propertyDisjointWith: final ATermAppl dwLHSp = arguments.get( 0 ); final ATermAppl dwRHSp = arguments.get( 1 ); if( !dwLHSp.equals( dwRHSp ) ) { // TODO optimizeTBox for( final ATermAppl known : getSymmetricCandidates( VarType.PROPERTY, dwLHSp, dwRHSp ) ) { for( final Set dependents : kb.getDisjointProperties( known ) ) { for( final ATermAppl dependent : dependents ) { runSymetricCheck( current, dwLHSp, known, dwRHSp, dependent, binding ); } } } } else { log.finer( "Atom " + current + "cannot be satisfied in any consistent ontology." ); } break; default: throw new UnsupportedQueryException( "Unknown atom type '" + current.getPredicate() + "'." ); } } private boolean STOP_ROLLING_ON_CONSTANTS = false; private void execSimpleCore(final Query q, final ResultBinding binding, final Collection distVars) { final Map> varBindings = new HashMap>(); final KnowledgeBase kb = q.getKB(); for( final ATermAppl currVar : distVars ) { ATermAppl rolledUpClass = q.rollUpTo( currVar, Collections.emptySet(), STOP_ROLLING_ON_CONSTANTS ); if( log.isLoggable( Level.FINER ) ) { log.finer( currVar + " rolled to " + rolledUpClass ); } Set inst = kb.getInstances( rolledUpClass ); varBindings.put( currVar, inst ); } if( log.isLoggable( Level.FINER ) ) { log.finer( "Var bindings: " + varBindings ); } final Set literalVars = q.getDistVarsForType( VarType.LITERAL ); final Set individualVars = q.getDistVarsForType( VarType.INDIVIDUAL ); boolean hasLiterals = !individualVars.containsAll( literalVars ); for( final Iterator i = new BindingIterator( varBindings ); i.hasNext(); ) { final ResultBinding candidate = i.next().duplicate(); candidate.setValues( binding ); if( hasLiterals ) { for( final Iterator l = new LiteralIterator( q, candidate ); l .hasNext(); ) { final ResultBinding mappy = binding.duplicate(); mappy.setValues( l.next() ); if( QueryEngine.execBooleanABoxQuery( q.apply( mappy ) ) ) { exec( mappy ); } } } else { if( QueryEngine.execBooleanABoxQuery( q.apply( candidate ) ) ) { exec( candidate ); } } } } private Map fastPrune(final Query q, final ATermAppl var) { // final Collection instances = new HashSet(kb // .getIndividuals()); // // final KnowledgeBase kb = q.getKB(); // // // // for (final QueryAtom atom : q.findAtoms(QueryPredicate.PropertyValue, // node, null, null)) { // instances.retainAll(kb.retrieveIndividualsWithProperty(atom // .getArguments().get(1))); // } // for (final QueryAtom atom : q.findAtoms(QueryPredicate.PropertyValue, // null, null, node)) { // instances.retainAll(kb.retrieveIndividualsWithProperty(ATermUtils // .makeInv(atom.getArguments().get(1)))); // } // return instances; // final ATermAppl c = q.rollUpTo(var, Collections.EMPTY_SET, false); // // CandidateSet set = kb.getABox().getObviousInstances(c); // log.fine(c + " : " + set.getKnowns().size() + " : " // + set.getUnknowns().size()); // // if (set.getUnknowns().isEmpty()) { // return set.getKnowns(); // } else { // return kb.getInstances(q // .rollUpTo(var, Collections.EMPTY_SET, false)); // } // return kb.getIndividuals(); final ATermAppl c = q.rollUpTo( var, Collections.emptySet(), STOP_ROLLING_ON_CONSTANTS ); if( log.isLoggable( Level.FINER ) ) { log.finer( var + " rolled to " + c ); } CandidateSet set = kb.getABox().getObviousInstances( c ); final Map map = new HashMap(); for( final Object o : set.getKnowns() ) { map.put( (ATermAppl) o, true ); } for( final Object o : set.getUnknowns() ) { map.put( (ATermAppl) o, false ); } return map; } private void execAllFastCore(final Query q, final ResultBinding binding, final Collection distVars, final Collection undistVars) { if( distVars.isEmpty() ) { exec( binding ); } else { final ATermAppl var = distVars.iterator().next(); distVars.remove( var ); final Map instances = fastPrune( q, var ); for( final Entry entry : instances.entrySet() ) { ATermAppl b = entry.getKey(); final ResultBinding newBinding = binding.duplicate(); newBinding.setValue( var, b ); final Query q2 = q.apply( newBinding ); if( entry.getValue() || QueryEngine.execBooleanABoxQuery( q2 ) ) { execAllFastCore( q2, newBinding, distVars, undistVars ); } } distVars.add( var ); } } // private void execIteratedCore(final Query q, final ResultBinding binding, // final Collection distVars, CoreStrategy strategy) { // if (distVars.isEmpty()) { // exec(binding); // } else { // final ATermAppl var = distVars.iterator().next(); // distVars.remove(var); // // boolean loadAll = (distVars.isEmpty() && !strategy // .equals(CoreStrategy.ALLFAST)) // || strategy.equals(CoreStrategy.OPTIMIZED); // // final Collection instances; // // final KnowledgeBase kb = q.getKB(); // // if (loadAll) { // final ATermAppl clazz = q.rollUpTo(var, Collections.EMPTY_SET, // false); // // if (log.isLoggable( Level.FINE )) { // log // .debug("Rolling up " + var + " to " + clazz // + " in " + q); // } // // instances = kb.getInstances(clazz); // } else { // instances = new HashSet(kb.getIndividuals()); // for (final QueryAtom atom : q.findAtoms( // QueryPredicate.PropertyValue, var, null, null)) { // instances.retainAll(kb.retrieveIndividualsWithProperty(atom // .getArguments().get(1))); // } // for (final QueryAtom atom : q.findAtoms( // QueryPredicate.PropertyValue, null, null, var)) { // instances.retainAll(kb // .retrieveIndividualsWithProperty(ATermUtils // .makeInv(atom.getArguments().get(1)))); // } // // } // // if (strategy.equals(CoreStrategy.FIRSTFAST)) { // strategy = CoreStrategy.OPTIMIZED; // } // // for (final ATermAppl b : instances) { // if (log.isLoggable( Level.FINE )) { // log.fine("trying " + var + " --> " + b); // } // final ResultBinding newBinding = binding.clone(); // // newBinding.setValue(var, b); // final Query q2 = q.apply(newBinding); // // if (!loadAll || QueryEngine.execBooleanABoxQuery(q2)) { // execIteratedCore(q2, newBinding, distVars, strategy); // } // } // // distVars.add(var); // } // } private void downMonotonic(final Taxonomy taxonomy, final Collection all, final boolean lhsDM, final ATermAppl lhs, final ATermAppl rhs, final ResultBinding binding, boolean direct, boolean strict) { final ATermAppl downMonotonic = lhsDM ? lhs : rhs; final ATermAppl theOther = lhsDM ? rhs : lhs; Collection candidates; if( ATermUtils.isVar( theOther ) ) { candidates = all; // TODO more refined evaluation in case that both // variables are down-monotonic } else { final ATermAppl top = lhsDM ? rhs : taxonomy.getTop().getName(); if( ATermUtils.isComplexClass( top ) ) { candidates = kb.getEquivalentClasses( top ); if( !strict && candidates.isEmpty() ) { candidates = flatten( kb.getSubClasses( top, true ) ); } } else { candidates = Collections.singleton( top ); } } for( final ATermAppl candidate : candidates ) { final ResultBinding newBinding = binding.duplicate(); if( ATermUtils.isVar( theOther ) ) { newBinding.setValue( theOther, candidate ); } // final Set toDo = lhsDM ? taxonomy.getFlattenedSubs( // ATermUtils.normalize(candidate), direct) : // taxonomy.getFlattenedSupers(ATermUtils.normalize(candidate), // direct); final Set toDo = lhsDM ? flatten( taxonomy.getSubs( candidate, direct ) ) : flatten( taxonomy.getSupers( candidate, direct ) ); if( strict ) { toDo.removeAll( taxonomy.getEquivalents( candidate ) ); } else { toDo.add( candidate ); } runRecursively( taxonomy, downMonotonic, candidate, newBinding, new HashSet( toDo ), direct, strict ); } } private boolean isDownMonotonic(final ATermAppl scLHS) { // TODO more refined condition to allow optimization for other atoms as // well - Type and // PropertyValue as well. return PelletOptions.OPTIMIZE_DOWN_MONOTONIC && downMonotonic.contains( scLHS ); } private void runNext(final ResultBinding binding, final List arguments, final ATermAppl... values) { final ResultBinding candidateBinding = binding.duplicate(); for( int i = 0; i < arguments.size(); i++ ) { if( ATermUtils.isVar( arguments.get( i ) ) ) { candidateBinding.setValue( arguments.get( i ), values[i] ); } } exec( candidateBinding ); } private Set getSymmetricCandidates(VarType forType, ATermAppl cA, ATermAppl cB) { final Set candidates; if( !ATermUtils.isVar( cA ) ) { candidates = Collections.singleton( cA ); } else if( !ATermUtils.isVar( cB ) ) { candidates = Collections.singleton( cB ); } else { switch ( forType ) { case CLASS: candidates = kb.getClasses(); break; case PROPERTY: candidates = kb.getProperties(); break; case INDIVIDUAL: candidates = kb.getIndividuals(); break; default: throw new RuntimeException( "Uknown variable type : " + forType ); } } return candidates; } private void runRecursively(final Taxonomy t, final ATermAppl downMonotonic, final ATermAppl rootCandidate, final ResultBinding binding, final Set toDo, final boolean direct, final boolean strict) { int size = result.size(); if( log.isLoggable( Level.FINE ) ) { log.fine( "Trying : " + rootCandidate + ", done=" + toDo ); } if( !strict ) { toDo.remove( rootCandidate ); runNext( binding, Collections.singletonList( downMonotonic ), rootCandidate ); } if( strict || result.size() > size ) { // final Set subs = t.getSFlattenedSubs(rootCandidate, // direct); final Set subs = flatten( t.getSubs( rootCandidate, direct ) ); for( final ATermAppl subject : subs ) { if( !toDo.contains( subject ) ) { continue; } runRecursively( t, downMonotonic, subject, binding, toDo, false, false ); } } else { if( log.isLoggable( Level.FINE ) ) { log.fine( "Skipping subs of " + rootCandidate ); } // toDo.removeAll(t.getFlattenedSubs(rootCandidate, false)); toDo.removeAll( flatten( t.getSubs( rootCandidate, false ) ) ); } } private void runSymetricCheck(@SuppressWarnings("unused") QueryAtom current, ATermAppl cA, ATermAppl known, ATermAppl cB, ATermAppl dependent, ResultBinding binding) { final ResultBinding candidateBinding = binding.duplicate(); if( !ATermUtils.isVar( cA ) ) { candidateBinding.setValue( cB, dependent ); } else if( !ATermUtils.isVar( cB ) ) { candidateBinding.setValue( cA, dependent ); } else { candidateBinding.setValue( cA, known ); candidateBinding.setValue( cB, dependent ); } exec( candidateBinding ); } private void runAllPropertyChecks(@SuppressWarnings("unused") QueryAtom current, final ATermAppl var, final Set candidates, ResultBinding binding) { if( isDownMonotonic( var ) ) { for( final TaxonomyNode topNode : kb.getRoleTaxonomy(true).getTop().getSubs() ) { final ATermAppl top = topNode.getName(); if( candidates.contains( top ) ) { runRecursively( kb.getRoleTaxonomy(true), var, topNode.getName(), binding, new HashSet( candidates ), false, false ); } } } else { for( final ATermAppl candidate : candidates ) { final ResultBinding candidateBinding = binding.duplicate(); candidateBinding.setValue( var, candidate ); exec( candidateBinding ); } } } private Set flatten(final Set> set) { final Set result = new HashSet(); for( final Set set2 : set ) { for( final ATermAppl a : set2 ) { result.add( a ); } } return result; } }