org.dllearner.refinementoperators.RhoDRDown.java Source code

Java tutorial

Introduction

Here is the source code for org.dllearner.refinementoperators.RhoDRDown.java

Source

/**
 * Copyright (C) 2007 - 2016, Jens Lehmann
 *
 * This file is part of DL-Learner.
 *
 * DL-Learner is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * DL-Learner 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.dllearner.refinementoperators;

import com.google.common.base.Functions;
import com.google.common.base.Joiner;
import com.google.common.collect.*;
import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.ResultSet;
import org.dllearner.core.*;
import org.dllearner.core.annotations.NoConfigOption;
import org.dllearner.core.config.ConfigOption;
import org.dllearner.core.options.CommonConfigOptions;
import org.dllearner.core.owl.*;
import org.dllearner.reasoning.SPARQLReasoner;
import org.dllearner.utilities.OWLAPIUtils;
import org.dllearner.utilities.OWLCLassExpressionToOWLClassTransformer;
import org.dllearner.utilities.ToIRIFunction;
import org.dllearner.utilities.owl.ConceptTransformation;
import org.dllearner.utilities.owl.OWLClassExpressionLengthMetric;
import org.dllearner.utilities.owl.OWLClassExpressionToSPARQLConverter;
import org.dllearner.utilities.owl.OWLClassExpressionUtils;
import org.dllearner.utilities.split.DefaultDateTimeValuesSplitter;
import org.dllearner.utilities.split.DefaultNumericValuesSplitter;
import org.dllearner.utilities.split.ValuesSplitter;
import org.semanticweb.owlapi.model.*;
import org.semanticweb.owlapi.vocab.OWLFacet;
import org.semanticweb.owlapi.vocab.OWLRDFVocabulary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.helpers.BasicMarkerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import uk.ac.manchester.cs.owl.owlapi.OWLClassImpl;
import uk.ac.manchester.cs.owl.owlapi.OWLDataFactoryImpl;

import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import static com.google.common.primitives.Ints.max;

/**
 * A downward refinement operator, which makes use of domains
 * and ranges of properties. The operator is currently under
 * development. Its aim is to span a much "cleaner" and smaller search
 * tree compared to RhoDown by omitting many class descriptions,
 * which are obviously too weak, because they violate
 * domain/range restrictions. Furthermore, it makes use of disjoint
 * classes in the knowledge base.
 *
 * Note: Some of the code has moved to {@link Utility} in a modified
 * form to make it accessible for implementations of other refinement
 * operators. These utility methods may be completed and carefully
 * integrated back later.
 *
 * @author Jens Lehmann
 *
 */
@ComponentAnn(name = "rho refinement operator", shortName = "rho", version = 0.8)
public class RhoDRDown extends RefinementOperatorAdapter implements Component, CustomHierarchyRefinementOperator,
        CustomStartRefinementOperator, ReasoningBasedRefinementOperator {

    private static final ToIRIFunction TO_IRI_FUNCTION = new ToIRIFunction(true);
    private static final OWLCLassExpressionToOWLClassTransformer OWLCLASS_TRANSFORM_FUNCTION = new OWLCLassExpressionToOWLClassTransformer();

    private static Logger logger = LoggerFactory.getLogger(RhoDRDown.class);
    private final static Marker sparql_debug = new BasicMarkerFactory().getMarker("SD");

    private static final OWLClass OWL_THING = new OWLClassImpl(OWLRDFVocabulary.OWL_THING.getIRI());

    @ConfigOption(description = "the reasoner to use")
    private AbstractReasonerComponent reasoner;

    //@ConfigOption(description = "the learning algorithm")
    //private

    // hierarchies
    @NoConfigOption
    private ClassHierarchy classHierarchy;
    @NoConfigOption
    private ObjectPropertyHierarchy objectPropertyHierarchy;
    @NoConfigOption
    private DatatypePropertyHierarchy dataPropertyHierarchy;

    // domains and ranges
    private Map<OWLObjectProperty, OWLClassExpression> opDomains = new TreeMap<>();
    private Map<OWLDataProperty, OWLClassExpression> dpDomains = new TreeMap<>();
    private Map<OWLObjectProperty, OWLClassExpression> opRanges = new TreeMap<>();

    // maximum number of fillers for each role
    private Map<OWLObjectPropertyExpression, Integer> maxNrOfFillers = new TreeMap<>();
    // limit for cardinality restrictions (this makes sense if we e.g. have compounds with up to
    // more than 200 atoms but we are only interested in atoms with certain characteristics and do
    // not want something like e.g. >= 204 hasAtom.NOT Carbon-87; which blows up the search space
    @ConfigOption(defaultValue = "5", description = "limit for cardinality restrictions (this makes sense if we e.g. have compounds with too many atoms)")
    private int cardinalityLimit = 5;

    // start concept (can be used to start from an arbitrary concept, needs
    // to be Thing or NamedClass), note that when you use e.g. Compound as
    // start class, then the algorithm should start the search with class
    // Compound (and not with Thing), because otherwise concepts like
    // NOT Carbon-87 will be returned which itself is not a subclass of Compound
    @ConfigOption(defaultValue = "owl:Thing", description = "You can specify a start class for the algorithm")
    private OWLClassExpression startClass = OWL_THING;

    // the length of concepts of top refinements, the first values is
    // for refinements of \rho_\top(\top), the second one for \rho_A(\top)
    private int topRefinementsLength = 0;
    private Map<OWLClassExpression, Integer> topARefinementsLength = new TreeMap<>();
    // M is finite and this value is the maximum length of any value in M
    private int mMaxLength = 4;

    // the sets M_\top and M_A
    private Map<Integer, SortedSet<OWLClassExpression>> m = new TreeMap<>();
    private Map<OWLClassExpression, Map<Integer, SortedSet<OWLClassExpression>>> mA = new TreeMap<>();

    // @see MathOperations.getCombos
    private Map<Integer, List<List<Integer>>> combos = new HashMap<>();

    // refinements of the top concept ordered by length
    private Map<Integer, SortedSet<OWLClassExpression>> topRefinements = new TreeMap<>();
    private Map<OWLClassExpression, Map<Integer, SortedSet<OWLClassExpression>>> topARefinements = new TreeMap<>();

    // cumulated refinements of top (all from length one to the specified length)
    private Map<Integer, TreeSet<OWLClassExpression>> topRefinementsCumulative = new HashMap<>();
    private Map<OWLClassExpression, Map<Integer, TreeSet<OWLClassExpression>>> topARefinementsCumulative = new TreeMap<>();

    // app_A set of applicable properties for a given class (separate for
    // object properties, boolean datatypes, and double datatypes)
    private Map<OWLClassExpression, Set<OWLObjectProperty>> appOP = new TreeMap<>();
    private Map<OWLClassExpression, Set<OWLDataProperty>> appBD = new TreeMap<>();
    private Map<OWLClassExpression, Set<OWLDataProperty>> appNumeric = new TreeMap<>();
    private Map<OWLClassExpression, Set<OWLDataProperty>> appSD = new TreeMap<>();

    // most general applicable properties
    private Map<OWLClassExpression, Set<OWLObjectProperty>> mgr = new TreeMap<>();
    private Map<OWLClassExpression, Set<OWLDataProperty>> mgbd = new TreeMap<>();
    private Map<OWLClassExpression, Set<OWLDataProperty>> mgNumeric = new TreeMap<>();
    private Map<OWLClassExpression, Set<OWLDataProperty>> mgDT = new TreeMap<>();
    private Map<OWLClassExpression, Set<OWLDataProperty>> mgsd = new TreeMap<>();

    // splits for double datatype properties in ascending order
    private Map<OWLDataProperty, List<OWLLiteral>> splits = new TreeMap<>();

    @ConfigOption(description = "the number of generated split intervals for numeric types", defaultValue = "12")
    private int maxNrOfSplits = 12;

    // data structure for a simple frequent pattern matching preprocessing phase
    @ConfigOption(defaultValue = "3", description = "minimum number an individual or literal has to be seen in the "
            + "knowledge base before considering it for inclusion in concepts")
    private int frequencyThreshold = CommonConfigOptions.valueFrequencyThresholdDefault;
    private Map<OWLObjectPropertyExpression, Map<OWLIndividual, Integer>> valueFrequency = new HashMap<>();
    // data structure with identified frequent values
    private Map<OWLObjectPropertyExpression, Set<OWLIndividual>> frequentValues = new HashMap<>();
    // frequent data values
    private Map<OWLDataProperty, Set<OWLLiteral>> frequentDataValues = new HashMap<>();
    private Map<OWLDataProperty, Map<OWLLiteral, Integer>> dataValueFrequency = new HashMap<>();
    @ConfigOption(description = "whether to use hasValue on frequently occuring strings", defaultValue = "false")
    private boolean useDataHasValueConstructor = false;

    // statistics
    public long mComputationTimeNs = 0;
    public long topComputationTimeNs = 0;

    @ConfigOption(defaultValue = "true")
    private boolean applyAllFilter = true;

    @ConfigOption(defaultValue = "true", description = "throwing out all refinements with "
            + "duplicate \u2203 r for any r")
    private boolean applyExistsFilter = true;

    @ConfigOption(description = "support of universal restrictions (owl:allValuesFrom), e.g. \u2200 r.C ", defaultValue = "true")
    private boolean useAllConstructor = true;

    @ConfigOption(description = "support of existential restrictions (owl:someValuesFrom), e.g. \u2203 r.C ", defaultValue = "true")
    private boolean useExistsConstructor = true;

    @ConfigOption(description = "support of has value constructor (owl:hasValue), e.g. \u2203 r.{a} ", defaultValue = "false")
    private boolean useHasValueConstructor = false;

    @ConfigOption(description = "support of qualified cardinality restrictions (owl:minCardinality), e.g. \u2265 3 r.C ", defaultValue = "true")
    private boolean useCardinalityRestrictions = true;

    @ConfigOption(description = "support of negation (owl:complementOf), e.g. \u00AC C ", defaultValue = "true")
    private boolean useNegation = true;

    @ConfigOption(description = "support of inverse object properties (owl:inverseOf), e.g. r\u207B.C ", defaultValue = "false")
    private boolean useInverse = false;

    @ConfigOption(description = "support of boolean datatypes (xsd:boolean), e.g. \u2203 r.{true} ", defaultValue = "true")
    private boolean useBooleanDatatypes = true;

    @ConfigOption(description = "support of numeric datatypes (xsd:int, xsd:double, ...), e.g. \u2203 r.{true} ", defaultValue = "true")
    private boolean useNumericDatatypes = true;

    @ConfigOption(defaultValue = "true")
    private boolean useTimeDatatypes = true;

    @ConfigOption(description = "support of string datatypes (xsd:string), e.g. \u2203 r.{\"SOME_STRING\"} ", defaultValue = "false")
    private boolean useStringDatatypes = false;

    @ConfigOption(defaultValue = "true", description = "skip combination of intersection between disjoint classes")
    private boolean disjointChecks = true;

    @ConfigOption(defaultValue = "true")
    private boolean instanceBasedDisjoints = true;

    @ConfigOption(defaultValue = "false")
    private boolean dropDisjuncts = false;

    @ConfigOption(description = "universal restrictions on a property r are only used when there is already a cardinality and/or existential restriction on r", defaultValue = "true")
    private boolean useSomeOnly = true;

    // caches for reasoner queries
    private Map<OWLClassExpression, Map<OWLClassExpression, Boolean>> cachedDisjoints = new TreeMap<>();

    //   private Map<OWLClass,Map<OWLClass,Boolean>> abDisjoint = new TreeMap<OWLClass,Map<OWLClass,Boolean>>();
    //   private Map<OWLClass,Map<OWLClass,Boolean>> notABDisjoint = new TreeMap<OWLClass,Map<OWLClass,Boolean>>();
    //   private Map<OWLClass,Map<OWLClass,Boolean>> notABMeaningful = new TreeMap<OWLClass,Map<OWLClass,Boolean>>();

    @ConfigOption(description = "whether to generate object complement while refining", defaultValue = "false")
    private boolean useObjectValueNegation = false;

    @ConfigOption(description = "class expression length metric (should match learning algorithm usage)", defaultValue = "default cel_metric")
    private OWLClassExpressionLengthMetric lengthMetric = OWLClassExpressionLengthMetric.getDefaultMetric();
    private OWLDataFactory df = new OWLDataFactoryImpl();

    public RhoDRDown() {
    }

    public RhoDRDown(RhoDRDown op) {
        setApplyAllFilter(op.applyAllFilter);
        setCardinalityLimit(op.cardinalityLimit);
        setClassHierarchy(op.classHierarchy);
        setDataPropertyHierarchy(op.dataPropertyHierarchy);
        setDropDisjuncts(op.dropDisjuncts);
        setFrequencyThreshold(op.frequencyThreshold);
        setInstanceBasedDisjoints(op.instanceBasedDisjoints);
        setObjectPropertyHierarchy(op.objectPropertyHierarchy);
        setReasoner(op.reasoner);
        setStartClass(op.startClass);
        setSubHierarchy(op.classHierarchy);
        setUseAllConstructor(op.useAllConstructor);
        setUseBooleanDatatypes(op.useBooleanDatatypes);
        setUseCardinalityRestrictions(op.useCardinalityRestrictions);
        setUseDataHasValueConstructor(op.useDataHasValueConstructor);
        setUseExistsConstructor(op.useExistsConstructor);
        setUseHasValueConstructor(op.useHasValueConstructor);
        setUseNegation(op.useNegation);
        setUseObjectValueNegation(op.useObjectValueNegation);
        setUseStringDatatypes(op.useStringDatatypes);
        setUseNumericDatatypes(op.useNumericDatatypes);
        initialized = false;
    }

    @Override
    public void init() throws ComponentInitException {
        /*
        if(initialized) {
           throw new ComponentInitException("Refinement operator cannot be initialised twice.");
        }
        */

        if (classHierarchy == null)
            classHierarchy = reasoner.getClassHierarchy();
        if (dataPropertyHierarchy == null)
            dataPropertyHierarchy = reasoner.getDatatypePropertyHierarchy();
        if (objectPropertyHierarchy == null)
            objectPropertyHierarchy = reasoner.getObjectPropertyHierarchy();

        //      System.out.println("classHierarchy: " + classHierarchy);
        //      System.out.println("object properties: " + reasoner.getObjectProperties());

        // query reasoner for domains and ranges
        // (because they are used often in the operator)
        opDomains = reasoner.getObjectPropertyDomains();
        opRanges = reasoner.getObjectPropertyRanges();
        dpDomains = reasoner.getDataPropertyDomains();

        if (useHasValueConstructor) {
            for (OWLObjectProperty op : objectPropertyHierarchy.getEntities()) {
                // init
                Map<OWLIndividual, Integer> opMap = new TreeMap<>();
                valueFrequency.put(op, opMap);

                // sets ordered by corresponding individual (which we ignore)
                Map<OWLIndividual, SortedSet<OWLIndividual>> propertyMembers = reasoner.getPropertyMembers(op);

                Collection<SortedSet<OWLIndividual>> fillerSets = propertyMembers.values();
                for (SortedSet<OWLIndividual> fillerSet : fillerSets) {
                    for (OWLIndividual i : fillerSet) {
                        Integer value = opMap.get(i);

                        if (value != null) {
                            opMap.put(i, value + 1);
                        } else {
                            opMap.put(i, 1);
                        }
                    }
                }

                // keep only frequent patterns
                Set<OWLIndividual> frequentInds = new TreeSet<>();
                for (OWLIndividual i : opMap.keySet()) {
                    if (opMap.get(i) >= frequencyThreshold) {
                        frequentInds.add(i);
                        //                  break;
                    }
                }
                frequentValues.put(op, frequentInds);

                if (useInverse) {
                    opMap = new TreeMap<>();
                    valueFrequency.put(op.getInverseProperty(), opMap);

                    frequentInds = new TreeSet<>();

                    for (Entry<OWLIndividual, SortedSet<OWLIndividual>> entry : propertyMembers.entrySet()) {
                        OWLIndividual subject = entry.getKey();
                        SortedSet<OWLIndividual> values = entry.getValue();

                        opMap.put(subject, values.size());

                        if (values.size() >= frequencyThreshold) {
                            frequentInds.add(subject);
                        }
                    }
                    frequentValues.put(op.getInverseProperty(), frequentInds);
                }
            }
        }

        if (useDataHasValueConstructor) {
            for (OWLDataProperty dp : dataPropertyHierarchy.getEntities()) {
                Map<OWLLiteral, Integer> dpMap = new TreeMap<>();
                dataValueFrequency.put(dp, dpMap);

                //            long s1 = System.currentTimeMillis();
                //            ConcurrentMap<OWLLiteral, Integer> lit2frequency = reasoner.getDatatypeMembers(dp).values()
                //                  .parallelStream()
                //                  .map(set -> set.stream().collect(Collectors.toList()))
                //                  .flatMap(list -> list.stream())
                //                  .collect(Collectors.toConcurrentMap(
                //                        Function.identity(), lit -> 1, Integer::sum));
                //            long s2 = System.currentTimeMillis();
                //            System.out.println(s2 - s1);

                // sets ordered by corresponding individual (which we ignore)
                //            s1 = System.currentTimeMillis();
                Collection<SortedSet<OWLLiteral>> fillerSets = reasoner.getDatatypeMembers(dp).values();
                for (SortedSet<OWLLiteral> fillerSet : fillerSets) {
                    for (OWLLiteral lit : fillerSet) {
                        Integer frequency = dpMap.get(lit);

                        if (frequency != null) {
                            dpMap.put(lit, frequency + 1);
                        } else {
                            dpMap.put(lit, 1);
                        }
                    }
                }
                //            s2 = System.currentTimeMillis();
                //            System.out.println(s2 - s1);

                // keep only frequent patterns
                Set<OWLLiteral> frequentInds = new TreeSet<>();
                for (OWLLiteral i : dpMap.keySet()) {
                    if (dpMap.get(i) >= frequencyThreshold) {
                        logger.trace("adding value " + i + ", because " + dpMap.get(i) + ">=" + frequencyThreshold);
                        frequentInds.add(i);
                    }
                }
                frequentDataValues.put(dp, frequentInds);
            }
        }

        // we do not need the temporary set anymore and let the
        // garbage collector take care of it
        valueFrequency = null;
        dataValueFrequency.clear();// = null;

        // compute splits for numeric data properties
        if (useNumericDatatypes) {
            if (reasoner instanceof SPARQLReasoner && !((SPARQLReasoner) reasoner).isUseGenericSplitsCode()) {
                // TODO SPARQL support for splits
                logger.warn("Numeric Facet restrictions are not (yet) implemented for "
                        + AnnComponentManager.getName(reasoner) + ", option ignored");
            } else {
                ValuesSplitter splitter = new DefaultNumericValuesSplitter(reasoner, df, maxNrOfSplits);
                splits.putAll(splitter.computeSplits());
                if (logger.isDebugEnabled()) {
                    logger.debug(sparql_debug, "Numeric Splits: {}", splits);
                }
            }
        }

        // compute splits for time data properties
        if (useTimeDatatypes) {
            if (reasoner instanceof SPARQLReasoner && !((SPARQLReasoner) reasoner).isUseGenericSplitsCode()) {
                // TODO SPARQL support for splits
                logger.warn("Time based Facet restrictions are not (yet) implemented for "
                        + AnnComponentManager.getName(reasoner) + ", option ignored");
            } else {
                ValuesSplitter splitter = new DefaultDateTimeValuesSplitter(reasoner, df, maxNrOfSplits);
                splits.putAll(splitter.computeSplits());
            }
        }

        // determine the maximum number of fillers for each role
        // (up to a specified cardinality maximum)
        if (useCardinalityRestrictions) {
            if (reasoner instanceof SPARQLReasoner) {
                logger.warn("Cardinality restrictions in Sparql not fully implemented, defaulting to 10.");
            }
            for (OWLObjectProperty op : objectPropertyHierarchy.getEntities()) {
                if (reasoner instanceof SPARQLReasoner) {
                    // TODO SPARQL support for cardinalities
                    maxNrOfFillers.put(op, 10);
                } else {
                    int maxFillers = 0;
                    Map<OWLIndividual, SortedSet<OWLIndividual>> opMembers = reasoner.getPropertyMembers(op);
                    for (SortedSet<OWLIndividual> inds : opMembers.values()) {
                        if (inds.size() > maxFillers)
                            maxFillers = inds.size();
                        if (maxFillers >= cardinalityLimit) {
                            maxFillers = cardinalityLimit;
                            break;
                        }
                    }
                    maxNrOfFillers.put(op, maxFillers);

                    // handle inverse properties
                    if (useInverse) {
                        maxFillers = 0;

                        Multimap<OWLIndividual, OWLIndividual> map = HashMultimap.create();

                        for (Entry<OWLIndividual, SortedSet<OWLIndividual>> entry : opMembers.entrySet()) {
                            OWLIndividual subject = entry.getKey();
                            SortedSet<OWLIndividual> objects = entry.getValue();

                            for (OWLIndividual obj : objects) {
                                map.put(obj, subject);
                            }
                        }

                        for (Entry<OWLIndividual, Collection<OWLIndividual>> entry : map.asMap().entrySet()) {
                            Collection<OWLIndividual> inds = entry.getValue();
                            if (inds.size() > maxFillers)
                                maxFillers = inds.size();
                            if (maxFillers >= cardinalityLimit) {
                                maxFillers = cardinalityLimit;
                                break;
                            }
                        }
                        maxNrOfFillers.put(op.getInverseProperty(), maxFillers);
                    }
                }
            }
        }

        startClass = OWLAPIUtils.classExpressionPropertyExpanderChecked(startClass, reasoner, df, logger);

        if (classHierarchy == null) {
            classHierarchy = reasoner.getClassHierarchy();
        }
        if (objectPropertyHierarchy == null) {
            objectPropertyHierarchy = reasoner.getObjectPropertyHierarchy();
        }
        if (dataPropertyHierarchy == null) {
            dataPropertyHierarchy = reasoner.getDatatypePropertyHierarchy();
        }

        initialized = true;
    }

    protected void isFinal() {
        if (initialized)
            throw new IllegalStateException(this.getClass() + " already initialised in "
                    + Thread.currentThread().getStackTrace()[2].getMethodName());
    }

    /* (non-Javadoc)
     * @see org.dllearner.algorithms.refinement.RefinementOperator#refine(org.dllearner.core.owl.Description)
     */
    @Override
    public Set<OWLClassExpression> refine(OWLClassExpression concept) {
        throw new RuntimeException();
    }

    @Override
    public Set<OWLClassExpression> refine(OWLClassExpression description, int maxLength) {
        // check that maxLength is valid
        if (maxLength < OWLClassExpressionUtils.getLength(description, lengthMetric)) {
            throw new Error("length has to be at least class expression length (class expression: " + description
                    + " with length " + OWLClassExpressionUtils.getLength(description, lengthMetric)
                    + ", and max length: " + maxLength + ")");
        }
        return refine(description, maxLength, null, startClass);
    }

    /* (non-Javadoc)
     * @see org.dllearner.algorithms.refinement.RefinementOperator#refine(org.dllearner.core.owl.Description, int, java.util.List)
     */
    @Override
    public Set<OWLClassExpression> refine(OWLClassExpression description, int maxLength,
            List<OWLClassExpression> knownRefinements) {
        return refine(description, maxLength, knownRefinements, startClass);
    }

    @SuppressWarnings({ "unchecked" })
    public Set<OWLClassExpression> refine(OWLClassExpression description, int maxLength,
            List<OWLClassExpression> knownRefinements, OWLClassExpression currDomain) {

        //      System.out.println("|- " + description + " " + currDomain + " " + maxLength);

        // actions needing to be performed if this is the first time the
        // current domain is used
        if (!currDomain.isOWLThing() && !topARefinementsLength.containsKey(currDomain)) {
            topARefinementsLength.put(currDomain, 0);
        }

        // check whether using list or set makes more sense
        // here; and whether HashSet or TreeSet should be used
        // => TreeSet because duplicates are possible
        Set<OWLClassExpression> refinements = new TreeSet<>();

        // used as temporary variable
        Set<OWLClassExpression> tmp;

        if (description.isOWLThing()) {
            // extends top refinements if necessary
            if (currDomain.isOWLThing()) {
                if (maxLength > topRefinementsLength)
                    computeTopRefinements(maxLength);
                refinements = (TreeSet<OWLClassExpression>) topRefinementsCumulative.get(maxLength).clone();
            } else {
                if (maxLength > topARefinementsLength.get(currDomain)) {
                    computeTopRefinements(maxLength, currDomain);
                }
                refinements = (TreeSet<OWLClassExpression>) topARefinementsCumulative.get(currDomain).get(maxLength)
                        .clone();
            }
            //         refinements.addAll(classHierarchy.getMoreSpecialConcepts(description));
        } else if (description.isOWLNothing()) {
            // cannot be further refined
        } else if (!description.isAnonymous()) {
            refinements.addAll(classHierarchy.getSubClasses(description, true));
            refinements.remove(df.getOWLNothing());
        } else if (description instanceof OWLObjectComplementOf) {
            OWLClassExpression operand = ((OWLObjectComplementOf) description).getOperand();
            if (!operand.isAnonymous()) {
                tmp = classHierarchy.getSuperClasses(operand, true);

                for (OWLClassExpression c : tmp) {
                    if (!c.isOWLThing()) {
                        refinements.add(df.getOWLObjectComplementOf(c));
                    }
                }
            }
        } else if (description instanceof OWLObjectIntersectionOf) {
            List<OWLClassExpression> operands = ((OWLObjectIntersectionOf) description).getOperandsAsList();
            // refine one of the elements
            for (OWLClassExpression child : operands) {
                // refine the child; the new max length is the current max length minus
                // the currently considered concept plus the length of the child
                // TODO: add better explanation
                int length = OWLClassExpressionUtils.getLength(description, lengthMetric);
                int childLength = OWLClassExpressionUtils.getLength(child, lengthMetric);
                tmp = refine(child, maxLength - length + childLength, null, currDomain);

                // create new intersection
                for (OWLClassExpression c : tmp) {
                    if (!useSomeOnly || isCombinable(description, c)) {
                        List<OWLClassExpression> newChildren = new ArrayList<>(operands);
                        newChildren.add(c);
                        newChildren.remove(child);
                        Collections.sort(newChildren);
                        OWLClassExpression mc = new OWLObjectIntersectionOfImplExt(newChildren);

                        // clean concept and transform it to ordered negation normal form
                        // (non-recursive variant because only depth 1 was modified)
                        mc = ConceptTransformation.cleanConceptNonRecursive(mc);
                        mc = ConceptTransformation.nnf(mc);

                        // check whether the intersection is OK (sanity checks), then add it
                        if (checkIntersection((OWLObjectIntersectionOf) mc))
                            refinements.add(mc);
                    }
                }

            }

        } else if (description instanceof OWLObjectUnionOf) {
            // refine one of the elements
            List<OWLClassExpression> operands = ((OWLObjectUnionOf) description).getOperandsAsList();
            for (OWLClassExpression child : operands) {
                //            System.out.println("union child: " + child + " " + maxLength + " " + description.getLength() + " " + child.getLength());

                // refine child
                int length = OWLClassExpressionUtils.getLength(description, lengthMetric);
                int childLength = OWLClassExpressionUtils.getLength(child, lengthMetric);
                tmp = refine(child, maxLength - length + childLength, null, currDomain);

                // construct union (see above)
                for (OWLClassExpression c : tmp) {
                    List<OWLClassExpression> newChildren = new ArrayList<>(operands);
                    newChildren.remove(child);
                    newChildren.add(c);
                    Collections.sort(newChildren);
                    OWLClassExpression md = new OWLObjectUnionOfImplExt(newChildren);

                    // transform to ordered negation normal form
                    md = ConceptTransformation.nnf(md);
                    // note that we do not have to call clean here because a disjunction will
                    // never be nested in another disjunction in this operator

                    refinements.add(md);
                }
            }

            // if enabled, we can remove elements of the disjunction
            if (dropDisjuncts) {
                // A1 OR A2 => {A1,A2}
                if (operands.size() == 2) {
                    refinements.add(operands.get(0));
                    refinements.add(operands.get(1));
                } else {
                    // copy children list and remove a different element in each turn
                    for (int i = 0; i < operands.size(); i++) {
                        List<OWLClassExpression> newChildren = new LinkedList<>(operands);
                        newChildren.remove(i);
                        OWLObjectUnionOf md = new OWLObjectUnionOfImplExt(newChildren);
                        refinements.add(md);
                    }
                }
            }

        } else if (description instanceof OWLObjectSomeValuesFrom) {
            OWLObjectPropertyExpression role = ((OWLObjectSomeValuesFrom) description).getProperty();
            OWLClassExpression filler = ((OWLObjectSomeValuesFrom) description).getFiller();

            OWLClassExpression domain = role.isAnonymous() ? opDomains.get(role.getNamedProperty())
                    : opRanges.get(role);

            // rule 1: EXISTS r.D => EXISTS r.E
            tmp = refine(filler, maxLength - lengthMetric.objectSomeValuesLength - lengthMetric.objectProperyLength,
                    null, domain);

            for (OWLClassExpression c : tmp) {
                refinements.add(df.getOWLObjectSomeValuesFrom(role, c));
            }

            // rule 2: EXISTS r.D => EXISTS s.D or EXISTS r^-1.D => EXISTS s^-1.D
            Set<OWLObjectProperty> moreSpecialRoles = objectPropertyHierarchy
                    .getMoreSpecialRoles(role.getNamedProperty());

            for (OWLObjectProperty moreSpecialRole : moreSpecialRoles) {
                refinements.add(df.getOWLObjectSomeValuesFrom(moreSpecialRole, filler));
            }

            // rule 3: EXISTS r.D => >= 2 r.D
            // (length increases by 1 so we have to check whether max length is sufficient)
            if (useCardinalityRestrictions) {// && !role.isAnonymous()) {
                if (maxLength > OWLClassExpressionUtils.getLength(description, lengthMetric)
                        && maxNrOfFillers.get(role) > 1) {
                    OWLObjectMinCardinality min = df.getOWLObjectMinCardinality(2, role, filler);
                    refinements.add(min);
                }
            }

            // rule 4: EXISTS r.TOP => EXISTS r.{value}
            if (useHasValueConstructor && filler.isOWLThing()) { // && !role.isAnonymous()) {
                // watch out for frequent patterns
                Set<OWLIndividual> frequentInds = frequentValues.get(role);
                if (frequentInds != null) {
                    for (OWLIndividual ind : frequentInds) {
                        OWLObjectHasValue ovr = df.getOWLObjectHasValue(role, ind);
                        refinements.add(ovr);
                        if (useObjectValueNegation) {
                            refinements.add(df.getOWLObjectComplementOf(ovr));
                        }

                    }
                }
            }

        } else if (description instanceof OWLObjectAllValuesFrom) {
            refinements.addAll(refine((OWLObjectAllValuesFrom) description, maxLength));
        } else if (description instanceof OWLObjectCardinalityRestriction) {
            OWLObjectPropertyExpression role = ((OWLObjectCardinalityRestriction) description).getProperty();
            OWLClassExpression filler = ((OWLObjectCardinalityRestriction) description).getFiller();
            OWLClassExpression range = role.isAnonymous() ? opDomains.get(role.getNamedProperty())
                    : opRanges.get(role);
            int cardinality = ((OWLObjectCardinalityRestriction) description).getCardinality();
            if (description instanceof OWLObjectMaxCardinality) {
                // rule 1: <= x r.C =>  <= x r.D
                if (useNegation || cardinality > 0) {
                    tmp = refine(filler,
                            maxLength - lengthMetric.objectCardinalityLength - lengthMetric.objectProperyLength,
                            null, range);

                    for (OWLClassExpression d : tmp) {
                        refinements.add(df.getOWLObjectMaxCardinality(cardinality, role, d));
                    }
                }

                // rule 2: <= x r.C  =>  <= (x-1) r.C
                //            int number = max.getNumber();
                if ((useNegation && cardinality > 1) || (!useNegation && cardinality > 2)) {
                    refinements.add(df.getOWLObjectMaxCardinality(cardinality - 1, role, filler));
                }

            } else if (description instanceof OWLObjectMinCardinality) {
                tmp = refine(filler,
                        maxLength - lengthMetric.objectCardinalityLength - lengthMetric.objectProperyLength, null,
                        range);

                for (OWLClassExpression d : tmp) {
                    refinements.add(df.getOWLObjectMinCardinality(cardinality, role, d));
                }

                // >= x r.C  =>  >= (x+1) r.C
                //            int number = min.getNumber();
                if (cardinality < maxNrOfFillers.get(role)) {
                    refinements.add(df.getOWLObjectMinCardinality(cardinality + 1, role, filler));
                }
            }
        } else if (description instanceof OWLDataSomeValuesFrom) {
            OWLDataProperty dp = ((OWLDataSomeValuesFrom) description).getProperty().asOWLDataProperty();
            OWLDataRange dr = ((OWLDataSomeValuesFrom) description).getFiller();
            if (dr instanceof OWLDatatypeRestriction) {
                OWLDatatype datatype = ((OWLDatatypeRestriction) dr).getDatatype();
                Set<OWLFacetRestriction> facetRestrictions = ((OWLDatatypeRestriction) dr).getFacetRestrictions();

                OWLDatatypeRestriction newDatatypeRestriction = null;
                if (OWLAPIUtils.isNumericDatatype(datatype) || OWLAPIUtils.dtDatatypes.contains(datatype)) {
                    for (OWLFacetRestriction facetRestriction : facetRestrictions) {
                        OWLFacet facet = facetRestriction.getFacet();

                        OWLLiteral value = facetRestriction.getFacetValue();

                        if (facet == OWLFacet.MAX_INCLUSIVE) {
                            // find out which split value was used
                            int splitIndex = splits.get(dp).lastIndexOf(value);
                            if (splitIndex == -1)
                                throw new Error("split error");
                            int newSplitIndex = splitIndex - 1;
                            if (newSplitIndex >= 0) {
                                OWLLiteral newValue = splits.get(dp).get(newSplitIndex);
                                newDatatypeRestriction = asDatatypeRestriction(dp, newValue, facet);
                            }
                        } else if (facet == OWLFacet.MIN_INCLUSIVE) {
                            // find out which split value was used
                            int splitIndex = splits.get(dp).lastIndexOf(value);
                            if (splitIndex == -1)
                                throw new Error("split error");
                            int newSplitIndex = splitIndex + 1;
                            if (newSplitIndex < splits.get(dp).size()) {
                                OWLLiteral newValue = splits.get(dp).get(newSplitIndex);
                                newDatatypeRestriction = asDatatypeRestriction(dp, newValue, facet);
                            }
                        }
                    }
                }
                if (newDatatypeRestriction != null) {
                    refinements.add(df.getOWLDataSomeValuesFrom(dp, newDatatypeRestriction));
                }
            }

        } else if (description instanceof OWLDataHasValue) {
            OWLDataPropertyExpression dp = ((OWLDataHasValue) description).getProperty();
            OWLLiteral value = ((OWLDataHasValue) description).getFiller();

            if (!dp.isAnonymous()) {
                Set<OWLDataProperty> subDPs = dataPropertyHierarchy.getMoreSpecialRoles(dp.asOWLDataProperty());
                for (OWLDataProperty subDP : subDPs) {
                    refinements.add(df.getOWLDataHasValue(subDP, value));
                }
            }
        }

        // if a refinement is not Bottom, Top, ALL r.Bottom a refinement of top can be appended
        if (!description.isOWLThing() && !description.isOWLNothing()
                && !(description instanceof OWLObjectAllValuesFrom
                        && ((OWLObjectAllValuesFrom) description).getFiller().isOWLNothing())) {
            // -1 because of the AND symbol which is appended
            int topRefLength = maxLength - OWLClassExpressionUtils.getLength(description, lengthMetric) - 1;

            // maybe we have to compute new top refinements here
            if (currDomain.isOWLThing()) {
                if (topRefLength > topRefinementsLength)
                    computeTopRefinements(topRefLength);
            } else if (topRefLength > topARefinementsLength.get(currDomain))
                computeTopRefinements(topRefLength, currDomain);

            if (topRefLength > 0) {
                Set<OWLClassExpression> topRefs;
                if (currDomain.isOWLThing())
                    topRefs = topRefinementsCumulative.get(topRefLength);
                else
                    topRefs = topARefinementsCumulative.get(currDomain).get(topRefLength);

                for (OWLClassExpression c : topRefs) {
                    // true if refinement should be skipped due to filters,
                    // false otherwise
                    boolean skip = false;

                    // if a refinement of of the form ALL r, we check whether ALL r
                    // does not occur already
                    if (applyAllFilter) {
                        if (c instanceof OWLObjectAllValuesFrom) {
                            if (description instanceof OWLNaryBooleanClassExpression) {
                                for (OWLClassExpression child : ((OWLNaryBooleanClassExpression) description)
                                        .getOperands()) {
                                    if (child instanceof OWLObjectAllValuesFrom) {
                                        OWLObjectPropertyExpression r1 = ((OWLObjectAllValuesFrom) c).getProperty();
                                        OWLObjectPropertyExpression r2 = ((OWLObjectAllValuesFrom) child)
                                                .getProperty();
                                        if (r1.equals(r2)) {
                                            skip = true;
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }

                    // we only add \forall r.C to an intersection if there is
                    // already some existential restriction \exists r.C
                    if (useSomeOnly) {
                        skip = !isCombinable(description, c);
                    }

                    // check for double datatype properties
                    /*
                    if(c instanceof DatatypeSomeRestriction &&
                          description instanceof DatatypeSomeRestriction) {
                       DataRange dr = ((DatatypeSomeRestriction)c).getDataRange();
                       DataRange dr2 = ((DatatypeSomeRestriction)description).getDataRange();
                       // it does not make sense to have statements like height >= 1.8 AND height >= 1.7
                       if((dr instanceof DoubleMaxValue && dr2 instanceof DoubleMaxValue)
                          ||(dr instanceof DoubleMinValue && dr2 instanceof DoubleMinValue))
                          skip = true;
                    }*/

                    // perform a disjointness check when named classes are added;
                    // this can avoid a lot of superfluous computation in the algorithm e.g.
                    // when A1 looks good, so many refinements of the form (A1 OR (A2 AND A3))
                    // are generated which are all equal to A1 due to disjointness of A2 and A3
                    if (disjointChecks && !c.isAnonymous() && !description.isAnonymous()
                            && isDisjoint(description, c)) {
                        skip = true;
                        //                  System.out.println(c + " ignored when refining " + description);
                    }

                    if (!skip) {
                        List<OWLClassExpression> operands = Lists.newArrayList(description, c);
                        Collections.sort(operands);
                        OWLObjectIntersectionOf mc = new OWLObjectIntersectionOfImplExt(operands);

                        // clean and transform to ordered negation normal form
                        mc = (OWLObjectIntersectionOf) ConceptTransformation.cleanConceptNonRecursive(mc);
                        mc = (OWLObjectIntersectionOf) ConceptTransformation.nnf(mc);

                        // last check before intersection is added
                        if (checkIntersection(mc))
                            refinements.add(mc);
                    }
                }
            }
        }

        //      for(OWLClassExpression refinement : refinements) {
        //         if((refinement instanceof Intersection || refinement instanceof Union) && refinement.getChildren().size()<2) {
        //            System.out.println(OWLClassExpression + " " + refinement + " " + currDomain + " " + maxLength);
        //            System.exit(0);
        //         }
        //      }
        //      System.out.println("++++++++\nREFINING: " + description + "   maxLength:" + maxLength);
        //      System.out.println(refinements);
        return refinements;
    }

    private boolean isCombinable(OWLClassExpression ce, OWLClassExpression child) {
        boolean combinable = true;

        if (child instanceof OWLObjectAllValuesFrom) {
            boolean tmp = false;
            OWLObjectPropertyExpression r1 = ((OWLObjectAllValuesFrom) child).getProperty();
            if (ce instanceof OWLObjectIntersectionOf) {
                for (OWLClassExpression operand : ((OWLObjectIntersectionOf) ce).getOperands()) {
                    if (child instanceof OWLObjectSomeValuesFrom) {
                        OWLObjectPropertyExpression r2 = ((OWLObjectSomeValuesFrom) operand).getProperty();
                        if (r1.equals(r2)) {
                            tmp = true;
                            break;
                        }
                    }
                }
            } else if (ce instanceof OWLObjectSomeValuesFrom) {
                OWLObjectPropertyExpression r2 = ((OWLObjectSomeValuesFrom) ce).getProperty();
                if (r1.equals(r2)) {
                    tmp = true;
                }
            }
            combinable = tmp;
        }
        return combinable;
    }

    private Set<OWLClassExpression> refine(OWLObjectAllValuesFrom ce, int maxLength) {
        Set<OWLClassExpression> refinements = new HashSet<>();

        OWLObjectPropertyExpression role = ce.getProperty();
        OWLClassExpression filler = ce.getFiller();
        OWLClassExpression range = role.isAnonymous() ? opDomains.get(role.getNamedProperty()) : opRanges.get(role);

        // rule 1: ALL r.D => ALL r.E
        Set<OWLClassExpression> tmp = refine(filler,
                maxLength - lengthMetric.objectAllValuesLength - lengthMetric.objectProperyLength, null, range);

        for (OWLClassExpression c : tmp) {
            refinements.add(df.getOWLObjectAllValuesFrom(role, c));
        }

        // rule 2: ALL r.D => ALL r.BOTTOM if D is a most specific atomic concept
        if (!filler.isOWLNothing() && !filler.isAnonymous() && tmp.size() == 0) {
            refinements.add(df.getOWLObjectAllValuesFrom(role, df.getOWLNothing()));
        }

        // rule 3: ALL r.D => ALL s.D or ALL r^-1.D => ALL s^-1.D
        Set<OWLObjectProperty> subProperties = objectPropertyHierarchy.getMoreSpecialRoles(role.getNamedProperty());

        for (OWLObjectProperty subProperty : subProperties) {
            refinements.add(df.getOWLObjectAllValuesFrom(subProperty, filler));
        }

        // rule 4: ALL r.D => <= (maxFillers-1) r.D
        // (length increases by 1 so we have to check whether max length is sufficient)
        // => commented out because this is actually not a downward refinement
        //      if(useCardinalityRestrictions) {
        //         if(maxLength > ce.getLength() && maxNrOfFillers.get(ar)>1) {
        //            ObjectMaxCardinalityRestriction max = new ObjectMaxCardinalityRestriction(maxNrOfFillers.get(ar)-1,role,description.getChild(0));
        //            refinements.add(max);
        //         }
        //      }

        return refinements;
    }

    // when a child of an intersection is refined and reintegrated into the
    // intersection, we can perform some sanity checks;
    // method returns true if everything is OK and false otherwise
    // TODO: can be implemented more efficiently if the newly added child
    // is given as parameter
    public static boolean checkIntersection(OWLObjectIntersectionOf intersection) {
        // rule 1: max. restrictions at most once
        boolean maxDoubleOccurence = false;
        // rule 2: min restrictions at most once
        boolean minDoubleOccurence = false;
        // rule 3: no double occurences of boolean datatypes
        TreeSet<OWLDataProperty> occuredDP = new TreeSet<>();
        // rule 4: no double occurences of hasValue restrictions
        TreeSet<OWLObjectPropertyExpression> occuredVR = new TreeSet<>();
        // rule 5: max. restrictions at most once
        boolean maxIntOccurence = false;
        // rule 6: min restrictions at most once
        boolean minIntOccurence = false;

        for (OWLClassExpression child : intersection.getOperands()) {
            if (child instanceof OWLDataSomeValuesFrom) {
                OWLDataRange dr = ((OWLDataSomeValuesFrom) child).getFiller();
                if (dr instanceof OWLDatatypeRestriction) {
                    OWLDatatype datatype = ((OWLDatatypeRestriction) dr).getDatatype();
                    for (OWLFacetRestriction facetRestriction : ((OWLDatatypeRestriction) dr)
                            .getFacetRestrictions()) {
                        OWLFacet facet = facetRestriction.getFacet();
                        if (facet == OWLFacet.MIN_INCLUSIVE) {
                            if (datatype.isDouble()) {
                                if (minDoubleOccurence) {
                                    return false;
                                } else {
                                    minDoubleOccurence = true;
                                }
                            } else if (datatype.isInteger()) {
                                if (minIntOccurence) {
                                    return false;
                                } else {
                                    minIntOccurence = true;
                                }
                            }
                        } else if (facet == OWLFacet.MAX_INCLUSIVE) {
                            if (datatype.isDouble()) {
                                if (maxDoubleOccurence) {
                                    return false;
                                } else {
                                    maxDoubleOccurence = true;
                                }
                            } else if (datatype.isInteger()) {
                                if (maxIntOccurence) {
                                    return false;
                                } else {
                                    maxIntOccurence = true;
                                }
                            }
                        }
                    }
                }
            } else if (child instanceof OWLDataHasValue) {
                OWLDataProperty dp = ((OWLDataHasValue) child).getProperty().asOWLDataProperty();
                //            System.out.println("dp: " + dp);
                // return false if the boolean property exists already
                if (!occuredDP.add(dp))
                    return false;
            } else if (child instanceof OWLObjectHasValue) {
                OWLObjectPropertyExpression op = ((OWLObjectHasValue) child).getProperty();
                if (!occuredVR.add(op))
                    return false;
            }
            //         System.out.println(child.getClass());
        }
        return true;
    }

    /**
     * By default, the operator does not specialize e.g. (A or B) to A, because
     * it only guarantees weak completeness. Under certain circumstances, e.g.
     * refinement of a fixed given concept, it can be useful to allow such
     * refinements, which can be done by passing the parameter true to this method.
     * @param dropDisjuncts Whether to remove disjuncts in refinement process.
     */
    public void setDropDisjuncts(boolean dropDisjuncts) {
        this.dropDisjuncts = dropDisjuncts;
    }

    private void computeTopRefinements(int maxLength) {
        computeTopRefinements(maxLength, null);
    }

    private void computeTopRefinements(int maxLength, OWLClassExpression domain) {
        long topComputationTimeStartNs = System.nanoTime();
        //      System.out.println("computing top refinements for " + domain + " up to length " + maxLength);

        if (domain == null && m.size() == 0)
            computeM();

        if (domain != null && !mA.containsKey(domain))
            computeM(domain);

        int refinementsLength;

        if (domain == null) {
            refinementsLength = topRefinementsLength;
        } else {
            if (!topARefinementsLength.containsKey(domain))
                topARefinementsLength.put(domain, 0);

            refinementsLength = topARefinementsLength.get(domain);
        }

        // compute all possible combinations of the disjunction
        for (int i = refinementsLength + 1; i <= maxLength; i++) {
            combos.put(i, MathOperations.getCombos(i, mMaxLength));

            // initialise the refinements with empty sets
            if (domain == null) {
                topRefinements.put(i, new TreeSet<>());
            } else {
                if (!topARefinements.containsKey(domain))
                    topARefinements.put(domain, new TreeMap<>());
                topARefinements.get(domain).put(i, new TreeSet<>());
            }

            for (List<Integer> combo : combos.get(i)) {

                // combination is a single number => try to use M
                if (combo.size() == 1) {
                    // note we cannot use "put" instead of "addAll" because there
                    // can be several combos for one length
                    if (domain == null)
                        topRefinements.get(i).addAll(m.get(i));
                    else
                        topARefinements.get(domain).get(i).addAll(mA.get(domain).get(i));
                    // combinations has several numbers => generate disjunct
                } else {

                    // check whether the combination makes sense, i.e. whether
                    // all lengths mentioned in it have corresponding elements
                    // e.g. when negation is deactivated there won't be elements of
                    // length 2 in M
                    boolean validCombo = true;
                    for (Integer j : combo) {
                        if ((domain == null && m.get(j).size() == 0)
                                || (domain != null && mA.get(domain).get(j).size() == 0))
                            validCombo = false;
                    }

                    if (validCombo) {

                        SortedSet<OWLObjectUnionOf> baseSet = new TreeSet<>();
                        for (Integer j : combo) {
                            if (domain == null)
                                baseSet = MathOperations.incCrossProduct(baseSet, m.get(j));
                            else
                                baseSet = MathOperations.incCrossProduct(baseSet, mA.get(domain).get(j));
                        }

                        // convert all concepts in ordered negation normal form
                        Set<OWLObjectUnionOf> tmp = new HashSet<>();
                        for (OWLClassExpression concept : baseSet) {
                            tmp.add((OWLObjectUnionOf) ConceptTransformation.nnf(concept));
                        }
                        baseSet = new TreeSet<>(tmp);

                        // apply the exists filter (throwing out all refinements with
                        // double \exists r for any r)
                        // TODO: similar filtering can be done for boolean datatype
                        // properties
                        if (applyExistsFilter) {
                            Iterator<OWLObjectUnionOf> it = baseSet.iterator();
                            while (it.hasNext()) {
                                if (MathOperations.containsDoubleObjectSomeRestriction(it.next()))
                                    it.remove();
                            }
                        }

                        // add computed refinements
                        if (domain == null) {
                            topRefinements.get(i).addAll(baseSet);
                        } else {
                            topARefinements.get(domain).get(i).addAll(baseSet);
                        }
                    }
                }
            }

            // create cumulative versions of refinements such that they can
            // be accessed easily
            TreeSet<OWLClassExpression> cumulativeRefinements = new TreeSet<>();
            for (int j = 1; j <= i; j++) {
                if (domain == null) {
                    cumulativeRefinements.addAll(topRefinements.get(j));
                } else {
                    cumulativeRefinements.addAll(topARefinements.get(domain).get(j));
                }
            }

            if (domain == null) {
                topRefinementsCumulative.put(i, cumulativeRefinements);
            } else {
                if (!topARefinementsCumulative.containsKey(domain))
                    topARefinementsCumulative.put(domain, new TreeMap<>());
                topARefinementsCumulative.get(domain).put(i, cumulativeRefinements);
            }
        }

        // register new top refinements length
        if (domain == null)
            topRefinementsLength = maxLength;
        else
            topARefinementsLength.put(domain, maxLength);

        topComputationTimeNs += System.nanoTime() - topComputationTimeStartNs;

        //      if(domain == null) {
        //         System.out.println("computed top refinements up to length " + topRefinementsLength + ": " + topRefinementsCumulative.get(maxLength));
        //      } else {
        //         System.out.println("computed top refinements up to length " + topARefinementsLength + ": (domain: "+domain+"): " + topARefinementsCumulative.get(domain).get(maxLength));
        //      }
    }

    // compute M_\top
    private void computeM() {
        long mComputationTimeStartNs = System.nanoTime();
        logger.debug(sparql_debug, "computeM");
        // initialise all possible lengths (1 to mMaxLength)
        for (int i = 1; i <= mMaxLength; i++) {
            m.put(i, new TreeSet<>());
        }

        SortedSet<OWLClassExpression> m1 = classHierarchy.getSubClasses(df.getOWLThing(), true);
        m.get(lengthMetric.classLength).addAll(m1);

        if (useNegation) {
            int lc = lengthMetric.objectComplementLength + lengthMetric.classLength;
            Set<OWLClassExpression> m2tmp = classHierarchy.getSuperClasses(df.getOWLNothing(), true);
            for (OWLClassExpression c : m2tmp) {
                if (!c.isOWLThing()) {
                    m.get(lc).add(df.getOWLObjectComplementOf(c));
                }
            }
        }

        if (useExistsConstructor) {
            int lc = lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength
                    + lengthMetric.classLength;
            int lc_i = lengthMetric.objectSomeValuesLength + lengthMetric.objectInverseLength
                    + lengthMetric.classLength;
            for (OWLObjectProperty r : objectPropertyHierarchy.getMostGeneralRoles()) {
                m.get(lc).add(df.getOWLObjectSomeValuesFrom(r, df.getOWLThing()));

                if (useInverse) {
                    m.get(lc_i).add(df.getOWLObjectSomeValuesFrom(r.getInverseProperty(), df.getOWLThing()));
                }
            }
        }

        if (useAllConstructor) {
            // we allow \forall r.\top here because otherwise the operator
            // becomes too difficult to manage due to dependencies between
            // M_A and M_A' where A'=ran(r)
            int lc = lengthMetric.objectAllValuesLength + lengthMetric.objectProperyLength
                    + lengthMetric.classLength;
            int lc_i = lengthMetric.objectAllValuesLength + lengthMetric.objectInverseLength
                    + lengthMetric.classLength;
            for (OWLObjectProperty r : objectPropertyHierarchy.getMostGeneralRoles()) {
                m.get(lc).add(df.getOWLObjectAllValuesFrom(r, df.getOWLThing()));

                if (useInverse) {
                    m.get(lc_i).add(df.getOWLObjectAllValuesFrom(r.getInverseProperty(), df.getOWLThing()));
                }
            }
        }

        // boolean datatypes, e.g. testPositive = true
        if (useBooleanDatatypes) {
            Set<OWLDataProperty> booleanDPs = Sets.intersection(dataPropertyHierarchy.getEntities(),
                    reasoner.getBooleanDatatypeProperties());
            logger.debug(sparql_debug, "BOOL DPs:" + booleanDPs);
            int lc = lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength;
            for (OWLDataProperty dp : booleanDPs) {
                m.get(lc).add(df.getOWLDataHasValue(dp, df.getOWLLiteral(true)));
                m.get(lc).add(df.getOWLDataHasValue(dp, df.getOWLLiteral(false)));
            }
        }

        if (useNumericDatatypes) {
            Set<OWLDataProperty> numericDPs = Sets.intersection(dataPropertyHierarchy.getEntities(),
                    reasoner.getNumericDataProperties());
            logger.debug(sparql_debug, "Numeric DPs:" + numericDPs);
            int lc = lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1;
            for (OWLDataProperty dp : numericDPs) {
                addNumericFacetRestrictions(lc, dp);
            }
        }

        if (useTimeDatatypes) {
            Set<OWLDataProperty> dataProperties = dataPropertyHierarchy.getEntities().stream().filter(dp -> {
                OWLDatatype datatype = reasoner.getDatatype(dp);
                return (datatype != null && OWLAPIUtils.dtDatatypes.contains(datatype));
            }).collect(Collectors.toSet());
            int lc = lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1;
            for (OWLDataProperty dp : dataProperties) {
                addNumericFacetRestrictions(lc, dp);
            }
        }

        if (useDataHasValueConstructor) {
            Set<OWLDataProperty> stringDPs = Sets.intersection(dataPropertyHierarchy.getEntities(),
                    reasoner.getStringDatatypeProperties());
            logger.debug(sparql_debug, "STRING DPs:" + stringDPs);
            int lc = lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength;
            for (OWLDataProperty dp : stringDPs) {
                // loop over frequent values
                Set<OWLLiteral> freqValues = frequentDataValues.get(dp);
                for (OWLLiteral lit : freqValues) {
                    m.get(lc).add(df.getOWLDataHasValue(dp, lit));
                }
            }
        }

        if (useCardinalityRestrictions) {
            logger.debug(sparql_debug, "most general properties:");
            int lc = lengthMetric.objectCardinalityLength + lengthMetric.objectProperyLength
                    + lengthMetric.classLength;
            for (OWLObjectProperty r : objectPropertyHierarchy.getMostGeneralRoles()) {
                int maxFillers = maxNrOfFillers.get(r);
                logger.debug(sparql_debug, "`" + r + "=" + maxFillers);
                // zero fillers: <= -1 r.C does not make sense
                // one filler: <= 0 r.C is equivalent to NOT EXISTS r.C,
                // but we still keep it, because ALL r.NOT C may be difficult to reach
                if ((useNegation && maxFillers > 0) || (!useNegation && maxFillers > 1))
                    m.get(lc).add(df.getOWLObjectMaxCardinality(maxFillers - 1, r, df.getOWLThing()));
            }
        }

        logger.debug(sparql_debug, "m: " + m);

        mComputationTimeNs += System.nanoTime() - mComputationTimeStartNs;
    }

    private void addNumericFacetRestrictions(int lc, OWLDataProperty dp) {
        if (splits.get(dp) != null && splits.get(dp).size() > 0) {
            OWLLiteral min = splits.get(dp).get(0);
            OWLLiteral max = splits.get(dp).get(splits.get(dp).size() - 1);

            OWLDatatypeRestriction restriction = asDatatypeRestriction(dp, min, OWLFacet.MIN_INCLUSIVE);
            m.get(lc).add(df.getOWLDataSomeValuesFrom(dp, restriction));

            restriction = asDatatypeRestriction(dp, max, OWLFacet.MAX_INCLUSIVE);
            m.get(lc).add(df.getOWLDataSomeValuesFrom(dp, restriction));
        }
    }

    private OWLDatatypeRestriction asDatatypeRestriction(OWLDataProperty dp, OWLLiteral value, OWLFacet facet) {
        OWLDatatype datatype = reasoner.getDatatype(dp);

        OWLDatatypeRestriction restriction = df.getOWLDatatypeRestriction(datatype,
                Collections.singleton(df.getOWLFacetRestriction(facet, value)));
        return restriction;
    }

    // computation of the set M_A
    // a major difference compared to the ILP 2007 \rho operator is that
    // M is finite and contains elements of length (currently) at most 3
    private void computeM(OWLClassExpression nc) {
        long mComputationTimeStartNs = System.nanoTime();

        mA.put(nc, new TreeMap<>());
        // initialise all possible lengths (1 to mMaxLength)
        for (int i = 1; i <= mMaxLength; i++) {
            mA.get(nc).put(i, new TreeSet<>());
        }

        // most general classes, which are not disjoint with nc and provide real refinement
        SortedSet<OWLClassExpression> m1 = getClassCandidates(nc);
        mA.get(nc).get(lengthMetric.classLength).addAll(m1);

        // most specific negated classes, which are not disjoint with nc
        if (useNegation) {
            SortedSet<OWLClassExpression> m2;
            m2 = getNegClassCandidates(nc);
            mA.get(nc).get(lengthMetric.classLength + lengthMetric.objectComplementLength).addAll(m2);
        }

        // compute applicable properties
        computeMg(nc);

        // boolean datatypes, e.g. testPositive = true
        if (useBooleanDatatypes) {
            int lc = lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength;
            Set<OWLDataProperty> booleanDPs = mgbd.get(nc);
            for (OWLDataProperty dp : booleanDPs) {
                mA.get(nc).get(lc).add(df.getOWLDataHasValue(dp, df.getOWLLiteral(true)));
                mA.get(nc).get(lc).add(df.getOWLDataHasValue(dp, df.getOWLLiteral(false)));
            }
        }

        if (useExistsConstructor) {
            int lc = lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength
                    + lengthMetric.classLength;
            for (OWLObjectProperty r : mgr.get(nc)) {
                mA.get(nc).get(lc).add(df.getOWLObjectSomeValuesFrom(r, df.getOWLThing()));
            }
        }

        if (useAllConstructor) {
            // we allow \forall r.\top here because otherwise the operator
            // becomes too difficult to manage due to dependencies between
            // M_A and M_A' where A'=ran(r)
            int lc = lengthMetric.objectAllValuesLength + lengthMetric.objectProperyLength
                    + lengthMetric.classLength;
            for (OWLObjectProperty r : mgr.get(nc)) {
                mA.get(nc).get(lc).add(df.getOWLObjectAllValuesFrom(r, df.getOWLThing()));
            }
        }

        if (useNumericDatatypes) {
            Set<OWLDataProperty> numericDPs = mgNumeric.get(nc);
            int lc = lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1;

            for (OWLDataProperty dp : numericDPs) {
                List<OWLLiteral> splitLiterals = splits.get(dp);
                if (splitLiterals != null && splitLiterals.size() > 0) {
                    OWLLiteral min = splits.get(dp).get(0);
                    OWLLiteral max = splits.get(dp).get(splits.get(dp).size() - 1);
                    mA.get(nc).get(lc).add(df.getOWLDataSomeValuesFrom(dp,
                            asDatatypeRestriction(dp, min, OWLFacet.MIN_INCLUSIVE)));
                    mA.get(nc).get(lc).add(df.getOWLDataSomeValuesFrom(dp,
                            asDatatypeRestriction(dp, max, OWLFacet.MAX_INCLUSIVE)));
                }
            }
        }

        if (useTimeDatatypes) {
            Set<OWLDataProperty> dtDPs = mgDT.get(nc);
            int lc = lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1;

            for (OWLDataProperty dp : dtDPs) {
                if (splits.get(dp).size() > 0) {
                    OWLLiteral min = splits.get(dp).get(0);
                    OWLLiteral max = splits.get(dp).get(splits.get(dp).size() - 1);
                    mA.get(nc).get(lc).add(df.getOWLDataSomeValuesFrom(dp,
                            asDatatypeRestriction(dp, min, OWLFacet.MIN_INCLUSIVE)));
                    mA.get(nc).get(lc).add(df.getOWLDataSomeValuesFrom(dp,
                            asDatatypeRestriction(dp, max, OWLFacet.MAX_INCLUSIVE)));
                }
            }
        }

        if (useDataHasValueConstructor) {
            Set<OWLDataProperty> stringDPs = mgsd.get(nc);
            int lc = lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength;
            for (OWLDataProperty dp : stringDPs) {
                // loop over frequent values
                Set<OWLLiteral> freqValues = frequentDataValues.get(dp);
                for (OWLLiteral lit : freqValues) {
                    mA.get(nc).get(lc).add(df.getOWLDataHasValue(dp, lit));
                }
            }
        }

        if (useCardinalityRestrictions) {
            int lc = lengthMetric.objectCardinalityLength + lengthMetric.objectProperyLength
                    + lengthMetric.classLength;
            for (OWLObjectProperty r : mgr.get(nc)) {
                int maxFillers = maxNrOfFillers.get(r);
                // zero fillers: <= -1 r.C does not make sense
                // one filler: <= 0 r.C is equivalent to NOT EXISTS r.C,
                // but we still keep it, because ALL r.NOT C may be difficult to reach
                if ((useNegation && maxFillers > 0) || (!useNegation && maxFillers > 1))
                    mA.get(nc).get(lc).add(df.getOWLObjectMaxCardinality(maxFillers - 1, r, df.getOWLThing()));
            }
        }

        //      System.out.println("m for " + nc + ": " + mA.get(nc));

        mComputationTimeNs += System.nanoTime() - mComputationTimeStartNs;
    }

    // get candidates for a refinement of \top restricted to a class B
    public SortedSet<OWLClassExpression> getClassCandidates(OWLClassExpression index) {
        return getClassCandidatesRecursive(index, df.getOWLThing());
    }

    private SortedSet<OWLClassExpression> getClassCandidatesRecursive(OWLClassExpression index,
            OWLClassExpression upperClass) {
        SortedSet<OWLClassExpression> candidates = new TreeSet<>();

        SortedSet<OWLClassExpression> subClasses = classHierarchy.getSubClasses(upperClass, true);

        if (reasoner instanceof SPARQLReasoner) {
            OWLClassExpressionToSPARQLConverter conv = new OWLClassExpressionToSPARQLConverter();
            String query = "SELECT DISTINCT ?concept WHERE {";
            query += conv.convert("?ind", index);
            query += "?ind a ?concept . ";
            query += "VALUES ?concept {"
                    + Joiner.on(" ")
                            .join(FluentIterable.from(subClasses)
                                    .transform(Functions.compose(TO_IRI_FUNCTION, OWLCLASS_TRANSFORM_FUNCTION)))
                    + "}";
            query += "}";
            //         System.out.println(query);

            SortedSet<OWLClassExpression> meaningfulClasses = new TreeSet<>();
            QueryExecution qe = ((SPARQLReasoner) reasoner).getQueryExecutionFactory().createQueryExecution(query);
            ResultSet rs = qe.execSelect();
            while (rs.hasNext()) {
                QuerySolution qs = rs.next();
                meaningfulClasses.add(df.getOWLClass(IRI.create(qs.getResource("concept").getURI())));
            }
            qe.close();
            candidates.addAll(meaningfulClasses);
            // recursive call, i.e. go class hierarchy down for non-meaningful classes
            //         for (OWLClassExpression cls : Sets.difference(superClasses, meaningfulClasses)) {
            //            candidates.addAll(getNegClassCandidatesRecursive(index, cls));
            //         }
        } else {

            // we descend the subsumption hierarchy to ensure that we get
            // the most general concepts satisfying the criteria
            for (OWLClassExpression candidate : subClasses) {
                //               System.out.println("testing " + candidate + " ... ");
                //            NamedClass candidate = (OWLClass) d;
                // check disjointness with index (if not no further traversal downwards is necessary)
                if (!isDisjoint(candidate, index)) {
                    //                  System.out.println( " passed disjointness test ... ");
                    // check whether the class is meaningful, i.e. adds something to the index
                    // to do this, we need to make sure that the class is not a superclass of the
                    // index (otherwise we get nothing new) - for instance based disjoints, we
                    // make sure that there is at least one individual, which is not already in the
                    // upper class
                    boolean meaningful;
                    if (instanceBasedDisjoints) {
                        // bug: tests should be performed against the index, not the upper class
                        //                  SortedSet<OWLIndividual> tmp = rs.getIndividuals(upperClass);
                        SortedSet<OWLIndividual> tmp = reasoner.getIndividuals(index);
                        tmp.removeAll(reasoner.getIndividuals(candidate));
                        //                  System.out.println("  instances of " + index + " and not " + candidate + ": " + tmp.size());
                        meaningful = tmp.size() != 0;
                    } else {
                        meaningful = !isDisjoint(df.getOWLObjectComplementOf(candidate), index);
                    }

                    if (meaningful) {
                        // candidate went successfully through all checks
                        candidates.add(candidate);
                        //                  System.out.println(" real refinement");
                    } else {
                        // descend subsumption hierarchy to find candidates
                        //                  System.out.println(" enter recursion");
                        candidates.addAll(getClassCandidatesRecursive(index, candidate));
                    }
                }
                //            else {
                //               System.out.println(" ruled out, because it is disjoint");
                //            }
            }
        }
        //      System.out.println("cc method exit");
        return candidates;
    }

    // get candidates for a refinement of \top restricted to a class B
    public SortedSet<OWLClassExpression> getNegClassCandidates(OWLClassExpression index) {
        return getNegClassCandidatesRecursive(index, df.getOWLNothing(), null);
    }

    private SortedSet<OWLClassExpression> getNegClassCandidatesRecursive(OWLClassExpression index,
            OWLClassExpression lowerClass, Set<OWLClassExpression> seenClasses) {
        if (seenClasses == null) {
            seenClasses = new TreeSet<>();
        }
        SortedSet<OWLClassExpression> candidates = new TreeSet<>();
        //      System.out.println("index " + index + " lower class " + lowerClass);

        SortedSet<OWLClassExpression> superClasses = classHierarchy.getSuperClasses(lowerClass);

        if (reasoner instanceof SPARQLReasoner) {
            OWLClassExpressionToSPARQLConverter conv = new OWLClassExpressionToSPARQLConverter();
            String query = "SELECT DISTINCT ?concept WHERE {";
            query += conv.convert("?ind", index);
            query += "?ind a ?concept . ";
            query += "VALUES ?concept {"
                    + Joiner.on(" ")
                            .join(FluentIterable.from(superClasses)
                                    .transform(Functions.compose(TO_IRI_FUNCTION, OWLCLASS_TRANSFORM_FUNCTION)))
                    + "}";
            query += "}";
            //         System.out.println(query);
            SortedSet<OWLClassExpression> meaningfulClasses = new TreeSet<>();
            QueryExecution qe = ((SPARQLReasoner) reasoner).getQueryExecutionFactory().createQueryExecution(query);
            ResultSet rs = qe.execSelect();
            while (rs.hasNext()) {
                QuerySolution qs = rs.next();
                meaningfulClasses.add(df.getOWLClass(IRI.create(qs.getResource("concept").getURI())));
            }
            qe.close();
            candidates.addAll(meaningfulClasses);
            // recursive call, i.e. go class hierarchy up for non-meaningful classes
            //         for (OWLClassExpression cls : Sets.difference(superClasses, meaningfulClasses)) {
            //            candidates.addAll(getNegClassCandidatesRecursive(index, cls));
            //         }
        } else {
            for (OWLClassExpression candidate : superClasses) {
                if (!candidate.isOWLThing()) {
                    OWLObjectComplementOf negatedCandidate = df.getOWLObjectComplementOf(candidate);

                    // check disjointness with index/range (should not be disjoint otherwise not useful)
                    if (!isDisjoint(negatedCandidate, index)) {
                        boolean meaningful;

                        if (instanceBasedDisjoints) {
                            SortedSet<OWLIndividual> tmp = reasoner.getIndividuals(index);
                            tmp.removeAll(reasoner.getIndividuals(negatedCandidate));
                            meaningful = tmp.size() != 0;
                        } else {
                            meaningful = !isDisjoint(candidate, index);
                        }

                        if (meaningful) {
                            candidates.add(negatedCandidate);
                        } else if (!seenClasses.contains(candidate)) {
                            seenClasses.add(candidate);
                            candidates.addAll(getNegClassCandidatesRecursive(index, candidate, seenClasses));
                        }
                    }
                }
            }
        }

        return candidates;
    }

    private void computeMg(OWLClassExpression domain) {
        // compute the applicable properties if this has not been done yet
        if (appOP.get(domain) == null)
            computeApp(domain);

        // initialise mgr, mgbd, mgdd, mgsd
        mgr.put(domain, new TreeSet<>());
        mgbd.put(domain, new TreeSet<>());
        mgNumeric.put(domain, new TreeSet<>());
        mgsd.put(domain, new TreeSet<>());
        mgDT.put(domain, new TreeSet<>());

        SortedSet<OWLObjectProperty> mostGeneral = objectPropertyHierarchy.getMostGeneralRoles();
        computeMgrRecursive(domain, mostGeneral, mgr.get(domain));
        SortedSet<OWLDataProperty> mostGeneralDP = dataPropertyHierarchy.getMostGeneralRoles();
        // we make the (reasonable) assumption here that all sub and super
        // datatype properties have the same type (e.g. boolean, integer, double)
        Set<OWLDataProperty> mostGeneralBDP = Sets.intersection(mostGeneralDP,
                reasoner.getBooleanDatatypeProperties());
        Set<OWLDataProperty> mostGeneralNumericDPs = Sets.intersection(mostGeneralDP,
                reasoner.getNumericDataProperties());
        Set<OWLDataProperty> mostGeneralStringDPs = Sets.intersection(mostGeneralDP,
                reasoner.getStringDatatypeProperties());
        computeMgbdRecursive(domain, mostGeneralBDP, mgbd.get(domain));
        computeMostGeneralNumericDPRecursive(domain, mostGeneralNumericDPs, mgNumeric.get(domain));
        computeMostGeneralStringDPRecursive(domain, mostGeneralStringDPs, mgsd.get(domain));
    }

    private void computeMgrRecursive(OWLClassExpression domain, Set<OWLObjectProperty> currProperties,
            Set<OWLObjectProperty> mgrTmp) {
        for (OWLObjectProperty prop : currProperties) {
            if (appOP.get(domain).contains(prop))
                mgrTmp.add(prop);
            else
                computeMgrRecursive(domain, objectPropertyHierarchy.getMoreSpecialRoles(prop), mgrTmp);
        }
    }

    private void computeMgbdRecursive(OWLClassExpression domain, Set<OWLDataProperty> currProperties,
            Set<OWLDataProperty> mgbdTmp) {
        for (OWLDataProperty prop : currProperties) {
            if (appBD.get(domain).contains(prop))
                mgbdTmp.add(prop);
            else
                computeMgbdRecursive(domain, dataPropertyHierarchy.getMoreSpecialRoles(prop), mgbdTmp);
        }
    }

    private void computeMostGeneralNumericDPRecursive(OWLClassExpression domain,
            Set<OWLDataProperty> currProperties, Set<OWLDataProperty> mgddTmp) {
        for (OWLDataProperty prop : currProperties) {
            if (appNumeric.get(domain).contains(prop))
                mgddTmp.add(prop);
            else
                computeMostGeneralNumericDPRecursive(domain, dataPropertyHierarchy.getMoreSpecialRoles(prop),
                        mgddTmp);
        }
    }

    private void computeMostGeneralStringDPRecursive(OWLClassExpression domain, Set<OWLDataProperty> currProperties,
            Set<OWLDataProperty> mgddTmp) {
        for (OWLDataProperty prop : currProperties) {
            if (appSD.get(domain).contains(prop))
                mgddTmp.add(prop);
            else
                computeMostGeneralStringDPRecursive(domain, dataPropertyHierarchy.getMoreSpecialRoles(prop),
                        mgddTmp);
        }
    }

    // computes the set of applicable properties for a given class
    private void computeApp(OWLClassExpression domain) {
        SortedSet<OWLIndividual> individuals1 = reasoner.getIndividuals(domain);
        // object properties
        Set<OWLObjectProperty> mostGeneral = objectPropertyHierarchy.getMostGeneralRoles();
        Set<OWLObjectProperty> applicableRoles = new TreeSet<>();
        for (OWLObjectProperty role : mostGeneral) {
            // TODO: currently we just rely on named classes as roles,
            // instead of computing dom(r) and ran(r)
            OWLClassExpression d = opDomains.get(role);

            Set<OWLIndividual> individuals2 = new HashSet<>();
            for (Entry<OWLIndividual, SortedSet<OWLIndividual>> entry : reasoner.getPropertyMembers(role)
                    .entrySet()) {
                OWLIndividual ind = entry.getKey();
                if (!entry.getValue().isEmpty()) {
                    individuals2.add(ind);
                }
            }

            boolean disjoint = Sets.intersection(individuals1, individuals2).isEmpty();
            //         if(!isDisjoint(domain,d))
            if (!disjoint) {
                applicableRoles.add(role);
            }

        }

        appOP.put(domain, applicableRoles);

        // boolean datatype properties
        Set<OWLDataProperty> allBDPs = Sets.intersection(dataPropertyHierarchy.getEntities(),
                reasoner.getBooleanDatatypeProperties());
        Set<OWLDataProperty> applicableBDPs = new TreeSet<>();
        for (OWLDataProperty role : allBDPs) {
            //         Description d = (OWLClass) rs.getDomain(role);
            OWLClassExpression d = dpDomains.get(role);
            if (!isDisjoint(domain, d))
                applicableBDPs.add(role);
        }
        appBD.put(domain, applicableBDPs);

        // numeric data properties
        Set<OWLDataProperty> allNumericDPs = Sets.intersection(dataPropertyHierarchy.getEntities(),
                reasoner.getNumericDataProperties());
        Set<OWLDataProperty> applicableNumericDPs = new TreeSet<>();
        for (OWLDataProperty role : allNumericDPs) {
            // get domain of property
            OWLClassExpression d = dpDomains.get(role);
            // check if it's not disjoint with current class expression
            if (!isDisjoint(domain, d))
                applicableNumericDPs.add(role);
        }
        appNumeric.put(domain, applicableNumericDPs);

        // string datatype properties
        Set<OWLDataProperty> allSDPs = Sets.intersection(dataPropertyHierarchy.getEntities(),
                reasoner.getStringDatatypeProperties());
        Set<OWLDataProperty> applicableSDPs = new TreeSet<>();
        for (OWLDataProperty role : allSDPs) {
            //         Description d = (OWLClass) rs.getDomain(role);
            OWLClassExpression d = dpDomains.get(role);
            //         System.out.println("domain: " + d);
            if (!isDisjoint(domain, d))
                applicableSDPs.add(role);
        }
        appSD.put(domain, applicableSDPs);

    }

    // returns true if the intersection contains elements disjoint
    // to the given OWLClassExpression (if true adding the OWLClassExpression to
    // the intersection results in a OWLClassExpression equivalent to bottom)
    // e.g. OldPerson AND YoungPerson; Nitrogen-34 AND Tin-113
    // Note: currently we only check named classes in the intersection,
    // it would be interesting to see whether it makes sense to extend this
    // (advantage: less refinements, drawback: operator will need infinitely many
    // reasoner queries in the long run)
    @SuppressWarnings({ "unused" })
    private boolean containsDisjoints(OWLObjectIntersectionOf intersection, OWLClassExpression d) {
        List<OWLClassExpression> children = intersection.getOperandsAsList();
        for (OWLClassExpression child : children) {
            if (d.isOWLNothing())
                return true;
            else if (!child.isAnonymous()) {
                if (isDisjoint(child, d))
                    return true;
            }
        }
        return false;
    }

    private boolean isDisjoint(OWLClassExpression d1, OWLClassExpression d2) {
        if (d1.isOWLThing() || d2.isOWLThing()) {
            return false;
        }

        if (d1.isOWLNothing() || d2.isOWLNothing()) {
            return true;
        }

        // check whether we have cached this query
        Map<OWLClassExpression, Boolean> tmp = cachedDisjoints.get(d1);
        if (tmp != null && tmp.containsKey(d2)) {
            return tmp.get(d2);
        }

        // compute the disjointness
        Boolean result;
        if (instanceBasedDisjoints) {
            result = isDisjointInstanceBased(d1, d2);
        } else {
            OWLClassExpression d = df.getOWLObjectIntersectionOf(d1, d2);
            result = reasoner.isSuperClassOf(df.getOWLNothing(), d);
        }
        // add the result to the cache (we add it twice such that
        // the order of access does not matter)

        // create new entries if necessary
        if (tmp == null)
            cachedDisjoints.put(d1, new TreeMap<>());
        if (!cachedDisjoints.containsKey(d2))
            cachedDisjoints.put(d2, new TreeMap<>());

        // add result symmetrically in the OWLClassExpression matrix
        cachedDisjoints.get(d1).put(d2, result);
        cachedDisjoints.get(d2).put(d1, result);
        //         System.out.println("---");
        return result;
    }

    private boolean isDisjointInstanceBased(OWLClassExpression d1, OWLClassExpression d2) {
        if (reasoner instanceof SPARQLReasoner) {
            SortedSet<OWLIndividual> individuals = reasoner.getIndividuals(df.getOWLObjectIntersectionOf(d1, d2));
            return individuals.isEmpty();
        } else {
            SortedSet<OWLIndividual> d1Instances = reasoner.getIndividuals(d1);
            SortedSet<OWLIndividual> d2Instances = reasoner.getIndividuals(d2);

            for (OWLIndividual d1Instance : d1Instances) {
                if (d2Instances.contains(d1Instance))
                    return false;
            }
            return true;
        }
    }

    /*
    // computes whether two classes are disjoint; this should be computed
    // by the reasoner only ones and otherwise taken from a matrix
    private boolean isDisjoint(OWLClass a, OWLClassExpression d) {
       // we need to test whether A AND B is equivalent to BOTTOM
       Description d2 = new Intersection(a, d);
       return rs.subsumes(new Nothing(), d2);
    }*/

    // we need to test whether NOT A AND B is equivalent to BOTTOM
    @SuppressWarnings("unused")
    private boolean isNotADisjoint(OWLClass a, OWLClass b) {
        //      Map<OWLClass,Boolean> tmp = notABDisjoint.get(a);
        //      Boolean tmp2 = null;
        //      if(tmp != null)
        //         tmp2 = tmp.get(b);
        //
        //      if(tmp2==null) {
        OWLClassExpression notA = df.getOWLObjectComplementOf(a);
        OWLClassExpression d = df.getOWLObjectIntersectionOf(notA, b);
        Boolean result = reasoner.isSuperClassOf(df.getOWLNothing(), d);
        // ... add to cache ...
        return result;
        //      } else
        //         return tmp2;
    }

    // we need to test whether NOT A AND B = B
    // (if not then NOT A is not meaningful in the sense that it does
    // not semantically add anything to B)
    @SuppressWarnings("unused")
    private boolean isNotAMeaningful(OWLClass a, OWLClass b) {
        OWLClassExpression notA = df.getOWLObjectComplementOf(a);
        OWLClassExpression d = df.getOWLObjectIntersectionOf(notA, b);
        // check b subClassOf b AND NOT A (if yes then it is not meaningful)
        return !reasoner.isSuperClassOf(d, b);
    }

    public int getFrequencyThreshold() {
        return frequencyThreshold;
    }

    public void setFrequencyThreshold(int frequencyThreshold) {
        this.frequencyThreshold = frequencyThreshold;
    }

    public boolean isUseDataHasValueConstructor() {
        return useDataHasValueConstructor;
    }

    public void setUseDataHasValueConstructor(boolean useDataHasValueConstructor) {
        isFinal();
        this.useDataHasValueConstructor = useDataHasValueConstructor;
    }

    public boolean isApplyAllFilter() {
        return applyAllFilter;
    }

    public void setApplyAllFilter(boolean applyAllFilter) {
        this.applyAllFilter = applyAllFilter;
    }

    public boolean isApplyExistsFilter() {
        return applyExistsFilter;
    }

    public void setApplyExistsFilter(boolean applyExistsFilter) {
        this.applyExistsFilter = applyExistsFilter;
    }

    public boolean isUseAllConstructor() {
        return useAllConstructor;
    }

    public void setUseAllConstructor(boolean useAllConstructor) {
        this.useAllConstructor = useAllConstructor;
    }

    public boolean isUseExistsConstructor() {
        return useExistsConstructor;
    }

    public void setUseExistsConstructor(boolean useExistsConstructor) {
        this.useExistsConstructor = useExistsConstructor;
    }

    public boolean isUseHasValueConstructor() {
        return useHasValueConstructor;
    }

    public void setUseHasValueConstructor(boolean useHasValueConstructor) {
        isFinal();
        this.useHasValueConstructor = useHasValueConstructor;
    }

    public boolean isUseCardinalityRestrictions() {
        return useCardinalityRestrictions;
    }

    public void setUseCardinalityRestrictions(boolean useCardinalityRestrictions) {
        isFinal();
        this.useCardinalityRestrictions = useCardinalityRestrictions;
    }

    public boolean isUseNegation() {
        return useNegation;
    }

    public void setUseNegation(boolean useNegation) {
        this.useNegation = useNegation;
    }

    public boolean isUseBooleanDatatypes() {
        return useBooleanDatatypes;
    }

    public void setUseBooleanDatatypes(boolean useBooleanDatatypes) {
        this.useBooleanDatatypes = useBooleanDatatypes;
    }

    public boolean isUseStringDatatypes() {
        return useStringDatatypes;
    }

    public void setUseStringDatatypes(boolean useStringDatatypes) {
        this.useStringDatatypes = useStringDatatypes;
    }

    public boolean isInstanceBasedDisjoints() {
        return instanceBasedDisjoints;
    }

    public void setInstanceBasedDisjoints(boolean instanceBasedDisjoints) {
        this.instanceBasedDisjoints = instanceBasedDisjoints;
    }

    public AbstractReasonerComponent getReasoner() {
        return reasoner;
    }

    @Autowired
    public void setReasoner(AbstractReasonerComponent reasoner) {
        this.reasoner = reasoner;
    }

    public ClassHierarchy getSubHierarchy() {
        return classHierarchy;
    }

    public void setSubHierarchy(ClassHierarchy subHierarchy) {
        this.classHierarchy = subHierarchy;
    }

    public OWLClassExpression getStartClass() {
        return startClass;
    }

    @Override
    public void setStartClass(OWLClassExpression startClass) {
        this.startClass = startClass;
    }

    public int getCardinalityLimit() {
        return cardinalityLimit;
    }

    public void setCardinalityLimit(int cardinalityLimit) {
        this.cardinalityLimit = cardinalityLimit;
    }

    public ObjectPropertyHierarchy getObjectPropertyHierarchy() {
        return objectPropertyHierarchy;
    }

    @Override
    public void setObjectPropertyHierarchy(ObjectPropertyHierarchy objectPropertyHierarchy) {
        this.objectPropertyHierarchy = objectPropertyHierarchy;
    }

    public DatatypePropertyHierarchy getDataPropertyHierarchy() {
        return dataPropertyHierarchy;
    }

    @Override
    public void setDataPropertyHierarchy(DatatypePropertyHierarchy dataPropertyHierarchy) {
        this.dataPropertyHierarchy = dataPropertyHierarchy;
    }

    @Override
    public void setReasoner(Reasoner reasoner) {
        this.reasoner = (AbstractReasonerComponent) reasoner;
    }

    @Override
    @NoConfigOption
    public void setClassHierarchy(ClassHierarchy classHierarchy) {
        this.classHierarchy = classHierarchy;
    }

    /**
     * @param useObjectValueNegation the useObjectValueNegation to set
     */
    public void setUseObjectValueNegation(boolean useObjectValueNegation) {
        this.useObjectValueNegation = useObjectValueNegation;
    }

    public boolean isUseNumericDatatypes() {
        return useNumericDatatypes;
    }

    public void setUseNumericDatatypes(boolean useNumericDatatypes) {
        isFinal();
        this.useNumericDatatypes = useNumericDatatypes;
    }

    /**
     * @param useInverse whether to use inverse properties in property restrictions
     */
    public void setUseInverse(boolean useInverse) {
        isFinal();
        this.useInverse = useInverse;
    }

    /**
     * @param useSomeOnly whether to allow universal restrictions on a property r only if there exists already
     * a existential restriction on the same property in an intersection
     */
    public void setUseSomeOnly(boolean useSomeOnly) {
        this.useSomeOnly = useSomeOnly;
    }

    /**
     * @param useTimeDatatypes whether to use data/time literal restrictions
     */
    public void setUseTimeDatatypes(boolean useTimeDatatypes) {
        isFinal();
        this.useTimeDatatypes = useTimeDatatypes;
    }

    public int getMaxNrOfSplits() {
        return maxNrOfSplits;
    }

    public void setMaxNrOfSplits(int maxNrOfSplits) {
        this.maxNrOfSplits = maxNrOfSplits;
    }

    public boolean isDisjointChecks() {
        return disjointChecks;
    }

    public void setDisjointChecks(boolean disjointChecks) {
        this.disjointChecks = disjointChecks;
    }

    @Override
    public OWLClassExpressionLengthMetric getLengthMetric() {
        return lengthMetric;
    }

    @Override
    public void setLengthMetric(OWLClassExpressionLengthMetric lengthMetric) {
        this.lengthMetric = lengthMetric;

        mMaxLength = max(lengthMetric.classLength, lengthMetric.objectComplementLength + lengthMetric.classLength,
                lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength,
                lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength
                        + lengthMetric.objectInverseLength,
                lengthMetric.objectAllValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength,
                lengthMetric.objectAllValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength
                        + lengthMetric.objectInverseLength,
                lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength,
                lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1,
                lengthMetric.objectCardinalityLength + lengthMetric.objectProperyLength + lengthMetric.classLength);

        logger.debug("mMaxLength = " + mMaxLength);
    }
}