001/**
002 * Copyright (C) 2007 - 2016, Jens Lehmann
003 *
004 * This file is part of DL-Learner.
005 *
006 * DL-Learner is free software; you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation; either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * DL-Learner is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
018 */
019package org.dllearner.refinementoperators;
020
021import com.google.common.collect.*;
022import org.dllearner.core.*;
023import org.dllearner.core.annotations.NoConfigOption;
024import org.dllearner.core.config.ConfigOption;
025import org.dllearner.core.options.CommonConfigOptions;
026import org.dllearner.core.owl.*;
027import org.dllearner.reasoning.SPARQLReasoner;
028import org.dllearner.utilities.OWLAPIUtils;
029import org.dllearner.utilities.owl.ConceptTransformation;
030import org.dllearner.utilities.owl.OWLClassExpressionLengthMetric;
031import org.dllearner.utilities.owl.OWLClassExpressionUtils;
032import org.dllearner.utilities.split.DefaultDateTimeValuesSplitter;
033import org.dllearner.utilities.split.DefaultNumericValuesSplitter;
034import org.dllearner.utilities.split.ValuesSplitter;
035import org.semanticweb.owlapi.model.*;
036import org.semanticweb.owlapi.vocab.OWLFacet;
037import org.semanticweb.owlapi.vocab.OWLRDFVocabulary;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040import org.slf4j.Marker;
041import org.slf4j.helpers.BasicMarkerFactory;
042import org.springframework.beans.factory.annotation.Autowired;
043import uk.ac.manchester.cs.owl.owlapi.OWLClassImpl;
044import uk.ac.manchester.cs.owl.owlapi.OWLDataFactoryImpl;
045
046import java.util.*;
047import java.util.Map.Entry;
048import java.util.function.Function;
049import java.util.stream.Collectors;
050
051import static com.google.common.primitives.Ints.max;
052import static java.util.stream.Collectors.summingInt;
053
054/**
055 * A downward refinement operator, which makes use of domains
056 * and ranges of properties. The operator is currently under
057 * development. Its aim is to span a much "cleaner" and smaller search
058 * tree compared to RhoDown by omitting many class descriptions,
059 * which are obviously too weak, because they violate
060 * domain/range restrictions. Furthermore, it makes use of disjoint
061 * classes in the knowledge base.
062 *
063 * Note: Some of the code has moved to {@link Utility} in a modified
064 * form to make it accessible for implementations of other refinement
065 * operators. These utility methods may be completed and carefully
066 * integrated back later.
067 *
068 * @author Jens Lehmann
069 *
070 */
071@ComponentAnn(name = "rho refinement operator", shortName = "rho", version = 0.8)
072public class RhoDRDown extends RefinementOperatorAdapter implements Component, CustomHierarchyRefinementOperator, CustomStartRefinementOperator, ReasoningBasedRefinementOperator {
073
074        private static Logger logger = LoggerFactory.getLogger(RhoDRDown.class);
075        private final static Marker sparql_debug = new BasicMarkerFactory().getMarker("SD");
076
077        private static final OWLClass OWL_THING = new OWLClassImpl(
078            OWLRDFVocabulary.OWL_THING.getIRI());
079
080        @ConfigOption(description = "the reasoner to use")
081        private AbstractReasonerComponent reasoner;
082
083        // hierarchies
084        @NoConfigOption
085        private ClassHierarchy classHierarchy;
086        @NoConfigOption
087        private ObjectPropertyHierarchy objectPropertyHierarchy;
088        @NoConfigOption
089        private DatatypePropertyHierarchy dataPropertyHierarchy;
090
091        // domains and ranges
092        private Map<OWLObjectProperty,OWLClassExpression> opDomains = new TreeMap<>();
093        private Map<OWLDataProperty,OWLClassExpression> dpDomains = new TreeMap<>();
094        private Map<OWLObjectProperty,OWLClassExpression> opRanges = new TreeMap<>();
095
096        // maximum number of fillers for each role
097        private Map<OWLObjectPropertyExpression, Integer> maxNrOfFillers = new TreeMap<>();
098        // limit for cardinality restrictions (this makes sense if we e.g. have compounds with up to
099        // more than 200 atoms but we are only interested in atoms with certain characteristics and do
100        // not want something like e.g. >= 204 hasAtom.NOT Carbon-87; which blows up the search space
101        @ConfigOption(defaultValue = "5", description = "limit for cardinality restrictions (this makes sense if we e.g. have compounds with too many atoms)")
102        private int cardinalityLimit = 5;
103
104        // start concept (can be used to start from an arbitrary concept, needs
105        // to be Thing or NamedClass), note that when you use e.g. Compound as
106        // start class, then the algorithm should start the search with class
107        // Compound (and not with Thing), because otherwise concepts like
108        // NOT Carbon-87 will be returned which itself is not a subclass of Compound
109        @ConfigOption(
110                        defaultValue = "owl:Thing",
111                        description = "You can specify a start class for the algorithm")
112        private OWLClassExpression startClass = OWL_THING;
113
114        // the length of concepts of top refinements, the first values is
115        // for refinements of \rho_\top(\top), the second one for \rho_A(\top)
116        private int topRefinementsLength = 0;
117        private Map<OWLClassExpression, Integer> topARefinementsLength = new TreeMap<>();
118        // M is finite and this value is the maximum length of any value in M
119        private int mMaxLength = 4;
120
121        // the sets M_\top and M_A
122        private Map<Integer,SortedSet<OWLClassExpression>> m = new TreeMap<>();
123        private Map<OWLClassExpression,Map<Integer,SortedSet<OWLClassExpression>>> mA = new TreeMap<>();
124
125        // @see MathOperations.getCombos
126        private Map<Integer, List<List<Integer>>> combos = new HashMap<>();
127
128        // refinements of the top concept ordered by length
129        private Map<Integer, SortedSet<OWLClassExpression>> topRefinements = new TreeMap<>();
130        private Map<OWLClassExpression,Map<Integer, SortedSet<OWLClassExpression>>> topARefinements = new TreeMap<>();
131
132        // cumulated refinements of top (all from length one to the specified length)
133        private Map<Integer, TreeSet<OWLClassExpression>> topRefinementsCumulative = new HashMap<>();
134        private Map<OWLClassExpression,Map<Integer, TreeSet<OWLClassExpression>>> topARefinementsCumulative = new TreeMap<>();
135
136        // app_A set of applicable properties for a given class (separate for
137        // object properties, boolean datatypes, and double datatypes)
138        private Map<OWLClassExpression, Set<OWLObjectProperty>> appOP = new TreeMap<>();
139        private Map<OWLClassExpression, Set<OWLDataProperty>> appBD = new TreeMap<>();
140        private Map<OWLClassExpression, Set<OWLDataProperty>> appNumeric = new TreeMap<>();
141        private Map<OWLClassExpression, Set<OWLDataProperty>> appSD = new TreeMap<>();
142
143        // most general applicable properties
144        private Map<OWLClassExpression,Set<OWLObjectProperty>> mgr = new TreeMap<>();
145        private Map<OWLClassExpression,Set<OWLDataProperty>> mgbd = new TreeMap<>();
146        private Map<OWLClassExpression,Set<OWLDataProperty>> mgNumeric = new TreeMap<>();
147        private Map<OWLClassExpression,Set<OWLDataProperty>> mgDT = new TreeMap<>();
148        private Map<OWLClassExpression,Set<OWLDataProperty>> mgsd = new TreeMap<>();
149
150        // numeric values splitter
151        private ValuesSplitter numericValuesSplitter;
152
153        // splits for double datatype properties in ascending order
154        private Map<OWLDataProperty,List<OWLLiteral>> splits = new TreeMap<>();
155
156        @ConfigOption(description = "the number of generated split intervals for numeric types", defaultValue = "12")
157        private int maxNrOfSplits = 12;
158
159        // data structure for a simple frequent pattern matching preprocessing phase
160        @ConfigOption(defaultValue = "3", description = "minimum number an individual or literal has to be seen in the " +
161                        "knowledge base before considering it for inclusion in concepts")
162        private int frequencyThreshold = CommonConfigOptions.valueFrequencyThresholdDefault;
163        // data structure with identified frequent values
164        private Map<OWLObjectPropertyExpression, Set<OWLIndividual>> frequentValues = new HashMap<>();
165        // frequent data values
166        private Map<OWLDataProperty, Set<OWLLiteral>> frequentDataValues = new HashMap<>();
167
168        // statistics
169        public long mComputationTimeNs = 0;
170        public long topComputationTimeNs = 0;
171
172        @ConfigOption(defaultValue="true")
173        private boolean applyAllFilter = true;
174
175        @ConfigOption(defaultValue="true", description = "throwing out all refinements with " +
176                        "duplicate \u2203 r for any r")
177        private boolean applyExistsFilter = true;
178
179        @ConfigOption(description="support of universal restrictions (owl:allValuesFrom), e.g. \u2200 r.C ", defaultValue="true")
180        private boolean useAllConstructor = true;
181
182        @ConfigOption(description="support of existential restrictions (owl:someValuesFrom), e.g. \u2203 r.C ", defaultValue="true")
183        private boolean useExistsConstructor = true;
184
185        @ConfigOption(description="support of has value constructor (owl:hasValue), e.g. \u2203 r.{a} ", defaultValue="false")
186        private boolean useHasValueConstructor = false;
187
188        @ConfigOption(description = "support of has value constructor (owl:hasValue), e.g. \u2203 r.{20} ", defaultValue = "false")
189        private boolean useDataHasValueConstructor = false;
190
191        @ConfigOption(description="support of qualified cardinality restrictions (owl:minCardinality, owl:maxCardinality, owl:exactCardinality), e.g. \u2265 3 r.C ", defaultValue="true")
192        private boolean useCardinalityRestrictions = true;
193
194        @ConfigOption(description="support of local reflexivity of an object property expression (owl:hasSelf), e.g. \u2203 loves.Self for a narcissistic", defaultValue="false")
195        private boolean useHasSelf = false;
196
197        @ConfigOption(description="support of negation (owl:complementOf), e.g. \u00AC C ", defaultValue="true")
198        private boolean useNegation = true;
199
200        @ConfigOption(description="support of inverse object properties (owl:inverseOf), e.g. r\u207B.C ", defaultValue="false")
201        private boolean useInverse = false;
202
203        @ConfigOption(description="support of boolean datatypes (xsd:boolean), e.g. \u2203 r.{true} ", defaultValue="true")
204        private boolean useBooleanDatatypes = true;
205
206        @ConfigOption(description="support of numeric datatypes (xsd:int, xsd:double, ...), e.g. \u2203 r.{true} ", defaultValue="true")
207        private boolean useNumericDatatypes = true;
208
209        @ConfigOption(defaultValue="true")
210        private boolean useTimeDatatypes = true;
211
212        @ConfigOption(description="support of string datatypes (xsd:string), e.g. \u2203 r.{\"SOME_STRING\"} ",defaultValue="false")
213        private boolean useStringDatatypes = false;
214
215        @ConfigOption(defaultValue="true", description = "skip combination of intersection between disjoint classes")
216        private boolean disjointChecks = true;
217
218        @ConfigOption(defaultValue="true")
219        private boolean instanceBasedDisjoints = true;
220
221        @ConfigOption(defaultValue="false", description = "if enabled, generalise by removing parts of a disjunction")
222        private boolean dropDisjuncts = false;
223
224        @ConfigOption(description="universal restrictions on a property r are only used when there is already a cardinality and/or existential restriction on r",
225                        defaultValue="true")
226        private boolean useSomeOnly = true;
227
228        // caches for reasoner queries
229        private Map<OWLClassExpression,Map<OWLClassExpression,Boolean>> cachedDisjoints = new TreeMap<>();
230
231//      private Map<OWLClass,Map<OWLClass,Boolean>> abDisjoint = new TreeMap<OWLClass,Map<OWLClass,Boolean>>();
232//      private Map<OWLClass,Map<OWLClass,Boolean>> notABDisjoint = new TreeMap<OWLClass,Map<OWLClass,Boolean>>();
233//      private Map<OWLClass,Map<OWLClass,Boolean>> notABMeaningful = new TreeMap<OWLClass,Map<OWLClass,Boolean>>();
234
235        @ConfigOption(description = "whether to generate object complement while refining", defaultValue = "false")
236        private boolean useObjectValueNegation = false;
237
238        @ConfigOption(description = "class expression length metric (should match learning algorithm usage)", defaultValue = "default cel_metric")
239        private OWLClassExpressionLengthMetric lengthMetric = OWLClassExpressionLengthMetric.getDefaultMetric();
240        private OWLDataFactory df = new OWLDataFactoryImpl();
241
242        public RhoDRDown() {}
243
244        /**
245         * Copy constructor
246         */
247        public RhoDRDown(RhoDRDown op) {
248                setApplyAllFilter(op.applyAllFilter);
249                setCardinalityLimit(op.cardinalityLimit);
250                setClassHierarchy(op.classHierarchy);
251                setObjectPropertyHierarchy(op.objectPropertyHierarchy);
252                setDataPropertyHierarchy(op.dataPropertyHierarchy);
253                setDropDisjuncts(op.dropDisjuncts);
254                setInstanceBasedDisjoints(op.instanceBasedDisjoints);
255                setReasoner(op.reasoner);
256                setStartClass(op.startClass);
257                setUseAllConstructor(op.useAllConstructor);
258                setUseCardinalityRestrictions(op.useCardinalityRestrictions);
259                setUseExistsConstructor(op.useExistsConstructor);
260                setUseNegation(op.useNegation);
261                setUseHasValueConstructor(op.useHasValueConstructor);
262                setUseObjectValueNegation(op.useObjectValueNegation);
263                setFrequencyThreshold(op.frequencyThreshold);
264                setUseDataHasValueConstructor(op.useDataHasValueConstructor);
265                setUseBooleanDatatypes(op.useBooleanDatatypes);
266                setUseStringDatatypes(op.useStringDatatypes);
267                setUseNumericDatatypes(op.useNumericDatatypes);
268                setUseTimeDatatypes(op.useTimeDatatypes);
269                initialized = false;
270        }
271
272        private <T> Set<T> frequentObjects(Collection<? extends Collection<T>> c, int frequencyThreshold) {
273                final int t = frequencyThreshold;
274                return c.stream()
275                                .flatMap(Collection::stream)
276                                .collect(Collectors.collectingAndThen(Collectors.groupingBy(Function.identity(), Collectors.counting()),
277                                                map -> {
278                                                        map.values().removeIf(v -> v < t);
279                                                        return map.keySet();
280                                                }));
281        }
282
283        @Override
284    public void init() throws ComponentInitException {
285                /*
286                if(initialized) {
287                        throw new ComponentInitException("Refinement operator cannot be initialised twice.");
288                }
289                */
290
291                if (classHierarchy == null) classHierarchy = reasoner.getClassHierarchy();
292                if (dataPropertyHierarchy == null) dataPropertyHierarchy = reasoner.getDatatypePropertyHierarchy();
293                if (objectPropertyHierarchy == null) objectPropertyHierarchy = reasoner.getObjectPropertyHierarchy();
294
295                logger.debug("classHierarchy: " + classHierarchy);
296                logger.debug("object properties: " + reasoner.getObjectProperties());
297
298                // query reasoner for domains and ranges
299                // (because they are used often in the operator)
300                opDomains = reasoner.getObjectPropertyDomains();
301                opRanges = reasoner.getObjectPropertyRanges();
302                dpDomains = reasoner.getDataPropertyDomains();
303
304                // r. some {ind}
305                if (useHasValueConstructor) {
306                        for (OWLObjectProperty op : objectPropertyHierarchy.getEntities()) {
307
308                                Map<OWLIndividual, SortedSet<OWLIndividual>> propertyMembers = reasoner.getPropertyMembers(op);
309
310                                // compute the frequency of all individuals used as object and filter by threshold
311                                Set<OWLIndividual> frequentInds = frequentObjects(propertyMembers.values(), frequencyThreshold);
312                                frequentValues.put(op, frequentInds);
313
314                                // inv(r). some {ind}
315                                if(useInverse) {
316                                        // it's a bit easier for inverse properties since we have a mapping from each individual to
317                                        // all related individuals, thus, the freuqncy of each individual as subject is just the number
318                                        // of objects
319                                        frequentInds = propertyMembers.entrySet().stream().collect(Collectors.collectingAndThen(
320                                                        Collectors.toMap(Entry::getKey, e -> e.getValue().size()), map -> {
321                                                                map.values().removeIf(v -> v < frequencyThreshold);
322                                                                return map.keySet();
323                                        }));
324                                        frequentValues.put(op.getInverseProperty(), frequentInds);
325                                }
326                        }
327                }
328
329                // r. some {lit}
330                if(useDataHasValueConstructor) {
331                        for(OWLDataProperty dp : dataPropertyHierarchy.getEntities()) {
332                                Set<OWLLiteral> frequentLiterals = frequentObjects(reasoner.getDatatypeMembers(dp).values(), frequencyThreshold);
333                                frequentDataValues.put(dp, frequentLiterals);
334                        }
335                }
336
337                // compute splits for numeric data properties
338                if(useNumericDatatypes) {
339                        if(reasoner instanceof SPARQLReasoner
340                                        && !((SPARQLReasoner)reasoner).isUseGenericSplitsCode()) {
341                                // TODO SPARQL support for splits
342                                logger.warn("Numeric Facet restrictions are not (yet) implemented for " + AnnComponentManager.getName(reasoner) + ", option ignored");
343                        } else {
344                                // create default splitter if none was set
345                                if(numericValuesSplitter == null) {
346                                        numericValuesSplitter = new DefaultNumericValuesSplitter(reasoner, df, maxNrOfSplits);
347                                }
348                                splits.putAll(numericValuesSplitter.computeSplits());
349                                if (logger.isDebugEnabled()) {
350                                        logger.debug( sparql_debug, "Numeric Splits: {}", splits);
351                                }
352                        }
353                }
354
355                // compute splits for time data properties
356                if (useTimeDatatypes) {
357                        if(reasoner instanceof SPARQLReasoner
358                                        && !((SPARQLReasoner)reasoner).isUseGenericSplitsCode()) {
359                                // TODO SPARQL support for splits
360                                logger.warn("Time based Facet restrictions are not (yet) implemented for " + AnnComponentManager.getName(reasoner) + ", option ignored");
361                        } else {
362                                ValuesSplitter splitter = new DefaultDateTimeValuesSplitter(reasoner, df, maxNrOfSplits);
363                                splits.putAll(splitter.computeSplits());
364                        }
365                }
366
367                // determine the maximum number of fillers for each role
368                // (up to a specified cardinality maximum)
369                if(useCardinalityRestrictions) {
370                        if(reasoner instanceof SPARQLReasoner) {
371                                logger.warn("Cardinality restrictions in Sparql not fully implemented, defaulting to 10.");
372                        }
373                        for(OWLObjectProperty op : objectPropertyHierarchy.getEntities()) {
374                                if(reasoner instanceof SPARQLReasoner) {
375                                        // TODO SPARQL support for cardinalities
376                                        maxNrOfFillers.put(op, 10);
377                                } else {
378                                        int maxFillers = Math.min(cardinalityLimit,
379                                                        reasoner.getPropertyMembers(op).values().stream()
380                                                                        .mapToInt(Set::size)
381                                                                        .max().orElse(0));
382                                        maxNrOfFillers.put(op, maxFillers);
383
384//                                      int percentile95 = (int) new Percentile().evaluate(
385//                                                      reasoner.getPropertyMembers(op).entrySet().stream()
386//                                                      .mapToDouble(entry -> (double)entry.getValue().size())
387//                                                      .toArray(), 95);
388//                                      System.out.println("Prop " + op);
389//                                      System.out.println("max: " + maxFillers);
390//                                      System.out.println("95th: " + percentile95);
391
392                                        // handle inverse properties
393                                        if(useInverse) {
394                                                maxFillers = 0;
395        
396                                                Multimap<OWLIndividual, OWLIndividual> map = HashMultimap.create();
397        
398                                                for (Entry<OWLIndividual, SortedSet<OWLIndividual>> entry : reasoner.getPropertyMembers(op).entrySet()) {
399                                                        OWLIndividual subject = entry.getKey();
400                                                        SortedSet<OWLIndividual> objects = entry.getValue();
401        
402                                                        for (OWLIndividual obj : objects) {
403                                                                map.put(obj, subject);
404                                                        }
405                                                }
406        
407                                                for (Entry<OWLIndividual, Collection<OWLIndividual>> entry : map.asMap().entrySet()) {
408                                                        Collection<OWLIndividual> inds = entry.getValue();
409                                                        if (inds.size() > maxFillers)
410                                                                maxFillers = inds.size();
411                                                        if (maxFillers >= cardinalityLimit) {
412                                                                maxFillers = cardinalityLimit;
413                                                                break;
414                                                        }
415                                                }
416                                                maxNrOfFillers.put(op.getInverseProperty(), maxFillers);
417                                        }
418                                }
419                        }
420                }
421
422                startClass = OWLAPIUtils.classExpressionPropertyExpanderChecked(startClass, reasoner, df, logger);
423
424                if(classHierarchy == null) {
425                        classHierarchy = reasoner.getClassHierarchy();
426                }
427                if(objectPropertyHierarchy == null) {
428                        objectPropertyHierarchy = reasoner.getObjectPropertyHierarchy();
429                }
430                if(dataPropertyHierarchy == null) {
431                        dataPropertyHierarchy = reasoner.getDatatypePropertyHierarchy();
432                }
433
434                initialized = true;
435        }
436
437        protected void isFinal() {
438                if (initialized) throw new IllegalStateException(this.getClass() + " already initialised in " + Thread.currentThread().getStackTrace()[2].getMethodName());
439        }
440
441        @Override
442        public Set<OWLClassExpression> refine(OWLClassExpression concept) {
443                throw new RuntimeException();
444        }
445
446        @Override
447        public Set<OWLClassExpression> refine(OWLClassExpression description, int maxLength) {
448                // check that maxLength is valid
449                if(maxLength < OWLClassExpressionUtils.getLength(description, lengthMetric)) {
450                        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 + ")");
451                }
452                return refine(description, maxLength, null, startClass);
453        }
454
455        @Override
456        public Set<OWLClassExpression> refine(OWLClassExpression description, int maxLength,
457                        List<OWLClassExpression> knownRefinements) {
458                return refine(description, maxLength, knownRefinements, startClass);
459        }
460
461        @SuppressWarnings({"unchecked"})
462        public Set<OWLClassExpression> refine(OWLClassExpression description, int maxLength,
463                        List<OWLClassExpression> knownRefinements, OWLClassExpression currDomain) {
464
465//              System.out.println("|- " + description + " " + currDomain + " " + maxLength);
466
467                // actions needing to be performed if this is the first time the
468                // current domain is used
469                if(!currDomain.isOWLThing() && !topARefinementsLength.containsKey(currDomain)){
470                        topARefinementsLength.put(currDomain, 0);
471                }
472
473                // check whether using list or set makes more sense
474                // here; and whether HashSet or TreeSet should be used
475                // => TreeSet because duplicates are possible
476                Set<OWLClassExpression> refinements = new TreeSet<>();
477
478                // used as temporary variable
479                Set<OWLClassExpression> tmp;
480
481                if(description.isOWLThing()) {
482                        // extends top refinements if necessary
483                        if(currDomain.isOWLThing()) {
484                                if(maxLength>topRefinementsLength)
485                                        computeTopRefinements(maxLength);
486                                refinements = (TreeSet<OWLClassExpression>) topRefinementsCumulative.get(maxLength).clone();
487                        } else {
488                                if(maxLength>topARefinementsLength.get(currDomain)) {
489                                        computeTopRefinements(maxLength, currDomain);
490                                }
491                                refinements = (TreeSet<OWLClassExpression>) topARefinementsCumulative.get(currDomain).get(maxLength).clone();
492                        }
493//                      refinements.addAll(classHierarchy.getMoreSpecialConcepts(description));
494                } else if(description.isOWLNothing()) {
495                        // cannot be further refined
496                } else if(!description.isAnonymous()) {
497                        refinements.addAll(classHierarchy.getSubClasses(description, true));
498                        refinements.remove(df.getOWLNothing());
499                } else if (description instanceof OWLObjectComplementOf) {
500                        OWLClassExpression operand = ((OWLObjectComplementOf) description).getOperand();
501                        if(!operand.isAnonymous()){
502                                tmp = classHierarchy.getSuperClasses(operand, true);
503
504                                for(OWLClassExpression c : tmp) {
505                                        if(!c.isOWLThing()){
506                                                refinements.add(df.getOWLObjectComplementOf(c));
507                                        }
508                                }
509                        }
510                } else if (description instanceof OWLObjectIntersectionOf) {
511                        List<OWLClassExpression> operands = ((OWLObjectIntersectionOf) description).getOperandsAsList();
512                        // refine one of the elements
513                        for(OWLClassExpression child : operands) {
514                                // refine the child; the new max length is the current max length minus
515                                // the currently considered concept plus the length of the child
516                                // TODO: add better explanation
517                                int length = OWLClassExpressionUtils.getLength(description, lengthMetric);
518                                int childLength = OWLClassExpressionUtils.getLength(child, lengthMetric);
519                                tmp = refine(child, maxLength - length + childLength, null, currDomain);
520
521                                // create new intersection
522                                for(OWLClassExpression c : tmp) {
523                                        if(!useSomeOnly || isCombinable(description, c)) {
524                                                List<OWLClassExpression> newChildren = new ArrayList<>(operands);
525                                                newChildren.add(c);
526                                                newChildren.remove(child);
527                                                Collections.sort(newChildren);
528                                                OWLClassExpression mc = new OWLObjectIntersectionOfImplExt(newChildren);
529
530                                                // clean concept and transform it to ordered negation normal form
531                                                // (non-recursive variant because only depth 1 was modified)
532                                                mc = ConceptTransformation.cleanConceptNonRecursive(mc);
533                                                mc = ConceptTransformation.nnf(mc);
534
535                                                // check whether the intersection is OK (sanity checks), then add it
536                                                if(checkIntersection((OWLObjectIntersectionOf) mc))
537                                                        refinements.add(mc);
538                                        }
539                                }
540
541                        }
542
543                } else if (description instanceof OWLObjectUnionOf) {
544                        // refine one of the elements
545                        List<OWLClassExpression> operands = ((OWLObjectUnionOf) description).getOperandsAsList();
546                        for(OWLClassExpression child : operands) {
547//                              System.out.println("union child: " + child + " " + maxLength + " " + description.getLength() + " " + child.getLength());
548
549                                // refine child
550                                int length = OWLClassExpressionUtils.getLength(description, lengthMetric);
551                                int childLength = OWLClassExpressionUtils.getLength(child, lengthMetric);
552                                tmp = refine(child, maxLength - length + childLength, null, currDomain);
553
554                                // construct union (see above)
555                                for(OWLClassExpression c : tmp) {
556                                        List<OWLClassExpression> newChildren = new ArrayList<>(operands);
557                                        newChildren.remove(child);
558                                        newChildren.add(c);
559                                        Collections.sort(newChildren);
560                                        OWLClassExpression md = new OWLObjectUnionOfImplExt(newChildren);
561
562                                        // transform to ordered negation normal form
563                                        md = ConceptTransformation.nnf(md);
564                                        // note that we do not have to call clean here because a disjunction will
565                                        // never be nested in another disjunction in this operator
566
567                                        refinements.add(md);
568                                }
569                        }
570
571                        // if enabled, we can remove elements of the disjunction
572                        if(dropDisjuncts) {
573                                // A1 OR A2 => {A1,A2}
574                                if(operands.size() == 2) {
575                                        refinements.add(operands.get(0));
576                                        refinements.add(operands.get(1));
577                                } else {
578                                        // copy children list and remove a different element in each turn
579                                        for (OWLClassExpression op : operands) {
580                                                List<OWLClassExpression> newChildren = new LinkedList<>(operands);
581                                                newChildren.remove(op);
582                                                OWLObjectUnionOf md = new OWLObjectUnionOfImplExt(newChildren);
583                                                refinements.add(md);
584                                        }
585                                }
586                        }
587
588                } else if (description instanceof OWLObjectSomeValuesFrom) {
589                        OWLObjectPropertyExpression role = ((OWLObjectSomeValuesFrom) description).getProperty();
590                        OWLClassExpression filler = ((OWLObjectSomeValuesFrom) description).getFiller();
591
592                        // we need the context of the filler which is either the domain (in case of an inverse property) or the range of p
593                        OWLClassExpression domain = role.isAnonymous()
594                                        ? opDomains.get(role.getNamedProperty()) // inv(p) -> D = domain(p)
595                                        : opRanges.get(role.asOWLObjectProperty()); // p -> D = range(p)
596
597                        // rule 1: EXISTS r.D => EXISTS r.E
598                        tmp = refine(filler, maxLength-lengthMetric.objectSomeValuesLength-lengthMetric.objectProperyLength, null, domain);
599
600                        for(OWLClassExpression c : tmp){
601                                refinements.add(df.getOWLObjectSomeValuesFrom(role, c));
602                        }
603
604                        // rule 2: EXISTS r.D => EXISTS s.D or EXISTS r^-1.D => EXISTS s^-1.D
605                        Set<OWLObjectProperty> moreSpecialRoles = objectPropertyHierarchy.getMoreSpecialRoles(role.getNamedProperty());
606
607                        for (OWLObjectProperty moreSpecialRole : moreSpecialRoles) {
608                                refinements.add(df.getOWLObjectSomeValuesFrom(moreSpecialRole, filler));
609                        }
610
611                        // rule 3: EXISTS r.D => >= 2 r.D
612                        // (length increases by 1 so we have to check whether max length is sufficient)
613                        if (useCardinalityRestrictions &&
614                                        maxLength > OWLClassExpressionUtils.getLength(description, lengthMetric) &&
615                                        maxNrOfFillers.get(role) > 1) {
616                                refinements.add(df.getOWLObjectMinCardinality(2, role, filler));
617
618                        }
619
620                        // rule 4: EXISTS r.TOP => EXISTS r.{value}
621                        if(useHasValueConstructor && filler.isOWLThing()){ // && !role.isAnonymous()) {
622                                // watch out for frequent patterns
623                                Set<OWLIndividual> frequentInds = frequentValues.get(role);
624                                if(frequentInds != null) {
625                                        for(OWLIndividual ind : frequentInds) {
626                                                OWLObjectHasValue ovr = df.getOWLObjectHasValue(role, ind);
627                                                refinements.add(ovr);
628                                                // rule 4b : EXISTS r.TOP => EXISTS r.not {value}
629                                                if (useObjectValueNegation) {
630                                                        if (maxLength > OWLClassExpressionUtils.getLength(description, lengthMetric)) {
631                                                                refinements.add(df.getOWLObjectSomeValuesFrom(role, df.getOWLObjectComplementOf(df.getOWLObjectOneOf(ind))));
632                                                        }
633                                                }
634                                        }
635                                }
636                        }
637
638                } else if (description instanceof OWLObjectAllValuesFrom) {
639                        refinements.addAll(refine((OWLObjectAllValuesFrom) description, maxLength));
640                } else if (description instanceof OWLObjectCardinalityRestriction) {
641                        OWLObjectPropertyExpression role = ((OWLObjectCardinalityRestriction) description).getProperty();
642                        OWLClassExpression filler = ((OWLObjectCardinalityRestriction) description).getFiller();
643                        OWLClassExpression range = role.isAnonymous() ? opDomains.get(role.getNamedProperty()) : opRanges.get(role);
644                        int cardinality = ((OWLObjectCardinalityRestriction) description).getCardinality();
645                        if(description instanceof OWLObjectMaxCardinality) {
646                                // rule 1: <= x r.C =>  <= x r.D
647                                if(useNegation || cardinality > 0){
648                                        tmp = refine(filler, maxLength-lengthMetric.objectCardinalityLength-lengthMetric.objectProperyLength, null, range);
649
650                                        for(OWLClassExpression d : tmp) {
651                                                refinements.add(df.getOWLObjectMaxCardinality(cardinality,role,d));
652                                        }
653                                }
654
655                                // rule 2: <= x r.C  =>  <= (x-1) r.C
656//                              int number = max.getNumber();
657                                if((useNegation && cardinality > 1) || (!useNegation && cardinality > 2)){
658                                        refinements.add(df.getOWLObjectMaxCardinality(cardinality-1,role,filler));
659                                }
660
661                        } else if(description instanceof OWLObjectMinCardinality) {
662                                tmp = refine(filler, maxLength-lengthMetric.objectCardinalityLength-lengthMetric.objectProperyLength, null, range);
663
664                                for(OWLClassExpression d : tmp) {
665                                        refinements.add(df.getOWLObjectMinCardinality(cardinality,role,d));
666                                }
667
668                                // >= x r.C  =>  >= (x+1) r.C
669//                              int number = min.getNumber();
670                                if(cardinality < maxNrOfFillers.get(role)){
671                                        refinements.add(df.getOWLObjectMinCardinality(cardinality+1,role,filler));
672                                }
673                        } else if(description instanceof OWLObjectExactCardinality) {
674                                tmp = refine(filler, maxLength-lengthMetric.objectCardinalityLength-lengthMetric.objectProperyLength, null, range);
675
676                                for(OWLClassExpression d : tmp) {
677                                        refinements.add(df.getOWLObjectExactCardinality(cardinality,role,d));
678                                }
679
680                                // >= x r.C  =>  >= (x+1) r.C
681//                              int number = min.getNumber();
682                                if(cardinality < maxNrOfFillers.get(role)){
683                                        refinements.add(df.getOWLObjectExactCardinality(cardinality+1,role,filler));
684                                }
685                        }
686                } else if (description instanceof OWLDataSomeValuesFrom) {
687                        OWLDataProperty dp = ((OWLDataSomeValuesFrom) description).getProperty().asOWLDataProperty();
688                        OWLDataRange dr = ((OWLDataSomeValuesFrom) description).getFiller();
689                        if(dr instanceof OWLDatatypeRestriction){
690                                OWLDatatype datatype = ((OWLDatatypeRestriction) dr).getDatatype();
691                                Set<OWLFacetRestriction> facetRestrictions = ((OWLDatatypeRestriction) dr).getFacetRestrictions();
692
693                                OWLDatatypeRestriction newDatatypeRestriction = null;
694                                if(OWLAPIUtils.isNumericDatatype(datatype) || OWLAPIUtils.dtDatatypes.contains(datatype)){
695                                        for (OWLFacetRestriction facetRestriction : facetRestrictions) {
696                                                OWLFacet facet = facetRestriction.getFacet();
697
698                                                OWLLiteral value =  facetRestriction.getFacetValue();
699
700                                                if(facet == OWLFacet.MAX_INCLUSIVE){
701                                                        // find out which split value was used
702                                                        int splitIndex = splits.get(dp).lastIndexOf(value);
703                                                        if(splitIndex == -1)
704                                                                throw new Error("split error");
705                                                        int newSplitIndex = splitIndex - 1;
706                                                        if(newSplitIndex >= 0) {
707                                                                OWLLiteral newValue = splits.get(dp).get(newSplitIndex);
708                                                                newDatatypeRestriction = asDatatypeRestriction(dp, newValue, facet);
709                                                        }
710                                                } else if(facet == OWLFacet.MIN_INCLUSIVE){
711                                                        // find out which split value was used
712                                                        int splitIndex = splits.get(dp).lastIndexOf(value);
713                                                        if(splitIndex == -1)
714                                                                throw new Error("split error");
715                                                        int newSplitIndex = splitIndex + 1;
716                                                        if(newSplitIndex < splits.get(dp).size()) {
717                                                                OWLLiteral newValue = splits.get(dp).get(newSplitIndex);
718                                                                newDatatypeRestriction = asDatatypeRestriction(dp, newValue, facet);
719                                                        }
720                                                }
721                                        }
722                                }
723                                if(newDatatypeRestriction != null){
724                                        refinements.add(df.getOWLDataSomeValuesFrom(dp, newDatatypeRestriction));
725                                }
726                        }
727
728                } else if (description instanceof OWLDataHasValue) {
729                        OWLDataPropertyExpression dp = ((OWLDataHasValue) description).getProperty();
730                        OWLLiteral value = ((OWLDataHasValue) description).getFiller();
731
732                        if(!dp.isAnonymous()){
733                                Set<OWLDataProperty> subDPs = dataPropertyHierarchy.getMoreSpecialRoles(dp.asOWLDataProperty());
734                                for(OWLDataProperty subDP : subDPs) {
735                                        refinements.add(df.getOWLDataHasValue(subDP, value));
736                                }
737                        }
738                }
739
740                // if a refinement is not Bottom, Top, ALL r.Bottom a refinement of top can be appended
741                if(!description.isOWLThing() && !description.isOWLNothing()
742                                && !(description instanceof OWLObjectAllValuesFrom && ((OWLObjectAllValuesFrom)description).getFiller().isOWLNothing())) {
743                        // -1 because of the AND symbol which is appended
744                        int topRefLength = maxLength - OWLClassExpressionUtils.getLength(description, lengthMetric) - 1;
745
746                        // maybe we have to compute new top refinements here
747                        if(currDomain.isOWLThing()) {
748                                if(topRefLength > topRefinementsLength)
749                                        computeTopRefinements(topRefLength);
750                        } else if(topRefLength > topARefinementsLength.get(currDomain))
751                                computeTopRefinements(topRefLength, currDomain);
752
753                        if(topRefLength>0) {
754                                Set<OWLClassExpression> topRefs;
755                                if(currDomain.isOWLThing())
756                                        topRefs = topRefinementsCumulative.get(topRefLength);
757                                else
758                                        topRefs = topARefinementsCumulative.get(currDomain).get(topRefLength);
759
760                                for(OWLClassExpression c : topRefs) {
761                                        // true if refinement should be skipped due to filters,
762                                        // false otherwise
763                                        boolean skip = false;
764
765                                        // if a refinement of of the form ALL r, we check whether ALL r
766                                        // does not occur already
767                                        if(applyAllFilter) {
768                                                if(c instanceof OWLObjectAllValuesFrom) {
769                                                        if(description instanceof OWLNaryBooleanClassExpression){
770                                                                for(OWLClassExpression child : ((OWLNaryBooleanClassExpression) description).getOperands()) {
771                                                                        if(child instanceof OWLObjectAllValuesFrom) {
772                                                                                OWLObjectPropertyExpression r1 = ((OWLObjectAllValuesFrom) c).getProperty();
773                                                                                OWLObjectPropertyExpression r2 = ((OWLObjectAllValuesFrom) child).getProperty();
774                                                                                if(r1.equals(r2)){
775                                                                                        skip = true;
776                                                                                        break;
777                                                                                }
778                                                                        }
779                                                                }
780                                                        }
781                                                }
782                                        }
783
784                                        // we only add \forall r.C to an intersection if there is
785                                        // already some existential restriction \exists r.C
786                                        if(useSomeOnly) {
787                                                skip = !isCombinable(description, c);
788                                        }
789
790                                        // check for double datatype properties
791                                        /*
792                                        if(c instanceof DatatypeSomeRestriction &&
793                                                        description instanceof DatatypeSomeRestriction) {
794                                                DataRange dr = ((DatatypeSomeRestriction)c).getDataRange();
795                                                DataRange dr2 = ((DatatypeSomeRestriction)description).getDataRange();
796                                                // it does not make sense to have statements like height >= 1.8 AND height >= 1.7
797                                                if((dr instanceof DoubleMaxValue && dr2 instanceof DoubleMaxValue)
798                                                        ||(dr instanceof DoubleMinValue && dr2 instanceof DoubleMinValue))
799                                                        skip = true;
800                                        }*/
801
802                                        // perform a disjointness check when named classes are added;
803                                        // this can avoid a lot of superfluous computation in the algorithm e.g.
804                                        // when A1 looks good, so many refinements of the form (A1 OR (A2 AND A3))
805                                        // are generated which are all equal to A1 due to disjointness of A2 and A3
806                                        if(disjointChecks && !c.isAnonymous() && !description.isAnonymous() && isDisjoint(description, c)) {
807                                                skip = true;
808//                                              System.out.println(c + " ignored when refining " + description);
809                                        }
810
811                                        if(!skip) {
812                                                List<OWLClassExpression> operands = Lists.newArrayList(description, c);
813                                                Collections.sort(operands);
814                                                OWLObjectIntersectionOf mc = new OWLObjectIntersectionOfImplExt(operands);
815
816                                                // clean and transform to ordered negation normal form
817                                                mc = (OWLObjectIntersectionOf) ConceptTransformation.cleanConceptNonRecursive(mc);
818                                                mc = (OWLObjectIntersectionOf) ConceptTransformation.nnf(mc);
819
820                                                // last check before intersection is added
821                                                if(checkIntersection(mc))
822                                                        refinements.add(mc);
823                                        }
824                                }
825                        }
826                }
827
828//              for(OWLClassExpression refinement : refinements) {
829//                      if((refinement instanceof Intersection || refinement instanceof Union) && refinement.getChildren().size()<2) {
830//                              System.out.println(OWLClassExpression + " " + refinement + " " + currDomain + " " + maxLength);
831//                              System.exit(0);
832//                      }
833//              }
834//              System.out.println("++++++++\nREFINING: " + description + "   maxLength:" + maxLength);
835//              System.out.println(refinements);
836                return refinements;
837        }
838
839        private boolean isCombinable(OWLClassExpression ce, OWLClassExpression child) {
840                boolean combinable = true;
841
842                if(child instanceof OWLObjectAllValuesFrom) {
843                        boolean tmp = false;
844                        OWLObjectPropertyExpression r1 = ((OWLObjectAllValuesFrom) child).getProperty();
845                        if(ce instanceof OWLObjectIntersectionOf){
846                                for(OWLClassExpression operand : ((OWLObjectIntersectionOf) ce).getOperands()) {
847                                        if(child instanceof OWLObjectSomeValuesFrom) {
848                                                OWLObjectPropertyExpression r2 = ((OWLObjectSomeValuesFrom) operand).getProperty();
849                                                if(r1.equals(r2)){
850                                                        tmp = true;
851                                                        break;
852                                                }
853                                        }
854                                }
855                        } else if(ce instanceof OWLObjectSomeValuesFrom) {
856                                OWLObjectPropertyExpression r2 = ((OWLObjectSomeValuesFrom) ce).getProperty();
857                                if(r1.equals(r2)){
858                                        tmp = true;
859                                }
860                        }
861                        combinable = tmp;
862                }
863                return combinable;
864        }
865
866        private Set<OWLClassExpression> refine(OWLObjectAllValuesFrom ce, int maxLength) {
867                Set<OWLClassExpression> refinements = new HashSet<>();
868
869                OWLObjectPropertyExpression role = ce.getProperty();
870                OWLClassExpression filler = ce.getFiller();
871                OWLClassExpression range = role.isAnonymous() ? opDomains.get(role.getNamedProperty()) : opRanges.get(role);
872
873                // rule 1: ALL r.D => ALL r.E
874                Set<OWLClassExpression> tmp = refine(filler, maxLength-lengthMetric.objectAllValuesLength-lengthMetric.objectProperyLength, null, range);
875
876                for(OWLClassExpression c : tmp) {
877                        refinements.add(df.getOWLObjectAllValuesFrom(role, c));
878                }
879
880                // rule 2: ALL r.D => ALL r.BOTTOM if D is a most specific atomic concept
881                if(!filler.isOWLNothing() && !filler.isAnonymous() && tmp.size()==0) {
882                        refinements.add(df.getOWLObjectAllValuesFrom(role, df.getOWLNothing()));
883                }
884
885                // rule 3: ALL r.D => ALL s.D or ALL r^-1.D => ALL s^-1.D
886                Set<OWLObjectProperty> subProperties = objectPropertyHierarchy.getMoreSpecialRoles(role.getNamedProperty());
887
888                for (OWLObjectProperty subProperty : subProperties) {
889                        refinements.add(df.getOWLObjectAllValuesFrom(subProperty, filler));
890                }
891
892                // rule 4: ALL r.D => <= (maxFillers-1) r.D
893                // (length increases by 1 so we have to check whether max length is sufficient)
894                // => commented out because this is actually not a downward refinement
895//              if(useCardinalityRestrictions) {
896//                      if(maxLength > ce.getLength() && maxNrOfFillers.get(ar)>1) {
897//                              ObjectMaxCardinalityRestriction max = new ObjectMaxCardinalityRestriction(maxNrOfFillers.get(ar)-1,role,description.getChild(0));
898//                              refinements.add(max);
899//                      }
900//              }
901
902                return refinements;
903        }
904
905        // when a child of an intersection is refined and reintegrated into the
906        // intersection, we can perform some sanity checks;
907        // method returns true if everything is OK and false otherwise
908        // TODO: can be implemented more efficiently if the newly added child
909        // is given as parameter
910        public static boolean checkIntersection(OWLObjectIntersectionOf intersection) {
911                // rule 1: max. restrictions at most once
912                boolean maxDoubleOccurence = false;
913                // rule 2: min restrictions at most once
914                boolean minDoubleOccurence = false;
915                // rule 3: no double occurences of boolean datatypes
916                TreeSet<OWLDataProperty> occuredDP = new TreeSet<>();
917                // rule 4: no double occurences of hasValue restrictions
918                TreeSet<OWLObjectPropertyExpression> occuredVR = new TreeSet<>();
919                // rule 5: max. restrictions at most once
920                                boolean maxIntOccurence = false;
921                                // rule 6: min restrictions at most once
922                                boolean minIntOccurence = false;
923
924                for(OWLClassExpression child : intersection.getOperands()) {
925                        if(child instanceof OWLDataSomeValuesFrom) {
926                                OWLDataRange dr = ((OWLDataSomeValuesFrom)child).getFiller();
927                                if(dr instanceof OWLDatatypeRestriction){
928                                        OWLDatatype datatype = ((OWLDatatypeRestriction) dr).getDatatype();
929                                        for (OWLFacetRestriction facetRestriction : ((OWLDatatypeRestriction) dr).getFacetRestrictions()) {
930                                                OWLFacet facet = facetRestriction.getFacet();
931                                                if (facet == OWLFacet.MIN_INCLUSIVE) {
932                                                        if (datatype.isDouble()) {
933                                                                if (minDoubleOccurence) {
934                                                                        return false;
935                                                                } else {
936                                                                        minDoubleOccurence = true;
937                                                                }
938                                                        } else if (datatype.isInteger()) {
939                                                                if (minIntOccurence) {
940                                                                        return false;
941                                                                } else {
942                                                                        minIntOccurence = true;
943                                                                }
944                                                        }
945                                                } else if (facet == OWLFacet.MAX_INCLUSIVE) {
946                                                        if (datatype.isDouble()) {
947                                                                if (maxDoubleOccurence) {
948                                                                        return false;
949                                                                } else {
950                                                                        maxDoubleOccurence = true;
951                                                                }
952                                                        } else if (datatype.isInteger()) {
953                                                                if (maxIntOccurence) {
954                                                                        return false;
955                                                                } else {
956                                                                        maxIntOccurence = true;
957                                                                }
958                                                        }
959                                                }
960                                        }
961                                }
962                        } else if(child instanceof OWLDataHasValue) {
963                                OWLDataProperty dp = ((OWLDataHasValue) child).getProperty().asOWLDataProperty();
964//                              System.out.println("dp: " + dp);
965                                // return false if the boolean property exists already
966                                if(!occuredDP.add(dp))
967                                        return false;
968                        } else if(child instanceof OWLObjectHasValue) {
969                                OWLObjectPropertyExpression op = ((OWLObjectHasValue) child).getProperty();
970                                if(!occuredVR.add(op))
971                                        return false;
972                        }
973//                      System.out.println(child.getClass());
974                }
975                return true;
976        }
977
978        /**
979         * By default, the operator does not specialize e.g. (A or B) to A, because
980         * it only guarantees weak completeness. Under certain circumstances, e.g.
981         * refinement of a fixed given concept, it can be useful to allow such
982         * refinements, which can be done by passing the parameter true to this method.
983         * @param dropDisjuncts Whether to remove disjuncts in refinement process.
984         */
985        public void setDropDisjuncts(boolean dropDisjuncts) {
986                this.dropDisjuncts = dropDisjuncts;
987        }
988
989        private void computeTopRefinements(int maxLength) {
990                computeTopRefinements(maxLength, null);
991        }
992
993        private void computeTopRefinements(int maxLength, OWLClassExpression domain) {
994                long topComputationTimeStartNs = System.nanoTime();
995//              System.out.println("computing top refinements for " + domain + " up to length " + maxLength);
996
997                if(domain == null && m.size() == 0)
998                        computeM();
999
1000                if(domain != null && !mA.containsKey(domain))
1001                        computeM(domain);
1002
1003                int refinementsLength;
1004
1005                if(domain == null) {
1006                        refinementsLength = topRefinementsLength;
1007                } else {
1008                        if(!topARefinementsLength.containsKey(domain))
1009                                topARefinementsLength.put(domain,0);
1010
1011                        refinementsLength = topARefinementsLength.get(domain);
1012                }
1013
1014                // compute all possible combinations of the disjunction
1015                for(int i = refinementsLength+1; i <= maxLength; i++) {
1016                        combos.put(i,MathOperations.getCombos(i, mMaxLength));
1017
1018                        // initialise the refinements with empty sets
1019                        if(domain == null) {
1020                                topRefinements.put(i, new TreeSet<>());
1021                        } else {
1022                                if(!topARefinements.containsKey(domain))
1023                                        topARefinements.put(domain, new TreeMap<>());
1024                                topARefinements.get(domain).put(i, new TreeSet<>());
1025                        }
1026
1027                        for(List<Integer> combo : combos.get(i)) {
1028
1029                                // combination is a single number => try to use M
1030                                if(combo.size()==1) {
1031                                        // note we cannot use "put" instead of "addAll" because there
1032                                        // can be several combos for one length
1033                                        if(domain == null)
1034                                                topRefinements.get(i).addAll(m.get(i));
1035                                        else
1036                                                topARefinements.get(domain).get(i).addAll(mA.get(domain).get(i));
1037                                // combinations has several numbers => generate disjunct
1038                                } else {
1039
1040                                        // check whether the combination makes sense, i.e. whether
1041                                        // all lengths mentioned in it have corresponding elements
1042                                        // e.g. when negation is deactivated there won't be elements of
1043                                        // length 2 in M
1044                                        boolean validCombo = true;
1045                                        for(Integer j : combo) {
1046                                                if((domain == null && m.get(j).size()==0) ||
1047                                                                (domain != null && mA.get(domain).get(j).size()==0))
1048                                                        validCombo = false;
1049                                        }
1050
1051                                        if(validCombo) {
1052
1053                                                SortedSet<OWLObjectUnionOf> baseSet = new TreeSet<>();
1054                                                for(Integer j : combo) {
1055                                                        if(domain == null)
1056                                                                baseSet = MathOperations.incCrossProduct(baseSet, m.get(j));
1057                                                        else
1058                                                                baseSet = MathOperations.incCrossProduct(baseSet, mA.get(domain).get(j));
1059                                                }
1060
1061                                                // convert all concepts in ordered negation normal form
1062                                                Set<OWLObjectUnionOf> tmp = new HashSet<>();
1063                                                for(OWLClassExpression concept : baseSet) {
1064                                                        tmp.add((OWLObjectUnionOf) ConceptTransformation.nnf(concept));
1065                                                }
1066                                                baseSet = new TreeSet<>(tmp);
1067
1068                                                // apply the exists filter (throwing out all refinements with
1069                                                // double \exists r for any r)
1070                                                // TODO: similar filtering can be done for boolean datatype
1071                                                // properties
1072                                                if(applyExistsFilter) {
1073                                                        baseSet.removeIf(MathOperations::containsDoubleObjectSomeRestriction);
1074                                                }
1075
1076                                                // add computed refinements
1077                                                if(domain == null) {
1078                                                        topRefinements.get(i).addAll(baseSet);
1079                                                } else {
1080                                                        topARefinements.get(domain).get(i).addAll(baseSet);
1081                                                }
1082                                        }
1083                                }
1084                        }
1085
1086                        // create cumulative versions of refinements such that they can
1087                        // be accessed easily
1088                        TreeSet<OWLClassExpression> cumulativeRefinements = new TreeSet<>();
1089                        for(int j=1; j<=i; j++) {
1090                                if(domain == null) {
1091                                        cumulativeRefinements.addAll(topRefinements.get(j));
1092                                } else {
1093                                        cumulativeRefinements.addAll(topARefinements.get(domain).get(j));
1094                                }
1095                        }
1096
1097                        if(domain == null) {
1098                                topRefinementsCumulative.put(i, cumulativeRefinements);
1099                        } else {
1100                                if(!topARefinementsCumulative.containsKey(domain))
1101                                        topARefinementsCumulative.put(domain, new TreeMap<>());
1102                                topARefinementsCumulative.get(domain).put(i, cumulativeRefinements);
1103                        }
1104                }
1105
1106                // register new top refinements length
1107                if(domain == null)
1108                        topRefinementsLength = maxLength;
1109                else
1110                        topARefinementsLength.put(domain,maxLength);
1111
1112                topComputationTimeNs += System.nanoTime() - topComputationTimeStartNs;
1113
1114//              if(domain == null) {
1115//                      System.out.println("computed top refinements up to length " + topRefinementsLength + ": " + topRefinementsCumulative.get(maxLength));
1116//              } else {
1117//                      System.out.println("computed top refinements up to length " + topARefinementsLength + ": (domain: "+domain+"): " + topARefinementsCumulative.get(domain).get(maxLength));
1118//              }
1119        }
1120
1121        // compute M_\top
1122        private void computeM() {
1123                long mComputationTimeStartNs = System.nanoTime();
1124                logger.debug(sparql_debug, "computeM");
1125                // initialise all possible lengths (1 to mMaxLength)
1126                for(int i=1; i<=mMaxLength; i++) {
1127                        m.put(i, new TreeSet<>());
1128                }
1129
1130                SortedSet<OWLClassExpression> m1 = classHierarchy.getSubClasses(df.getOWLThing(), true);
1131                m.get(lengthMetric.classLength).addAll(m1);
1132
1133                if(useNegation) {
1134                        int lc = lengthMetric.objectComplementLength + lengthMetric.classLength;
1135                        Set<OWLClassExpression> m2tmp = classHierarchy.getSuperClasses(df.getOWLNothing(), true);
1136                        for(OWLClassExpression c : m2tmp) {
1137                                if(!c.isOWLThing()) {
1138                                        m.get(lc).add(df.getOWLObjectComplementOf(c));
1139                                }
1140                        }
1141                }
1142
1143                if(useExistsConstructor) {
1144                        int lc = lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength;
1145                        int lc_i = lengthMetric.objectSomeValuesLength + lengthMetric.objectInverseLength + lengthMetric.classLength;
1146                        for(OWLObjectProperty r : objectPropertyHierarchy.getMostGeneralRoles()) {
1147                                m.get(lc).add(df.getOWLObjectSomeValuesFrom(r, df.getOWLThing()));
1148
1149                                if(useInverse) {
1150                                        m.get(lc_i).add(df.getOWLObjectSomeValuesFrom(r.getInverseProperty(), df.getOWLThing()));
1151                                }
1152                        }
1153                }
1154
1155                if(useAllConstructor) {
1156                        // we allow \forall r.\top here because otherwise the operator
1157                        // becomes too difficult to manage due to dependencies between
1158                        // M_A and M_A' where A'=ran(r)
1159                        int lc = lengthMetric.objectAllValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength;
1160                        int lc_i = lengthMetric.objectAllValuesLength + lengthMetric.objectInverseLength + lengthMetric.classLength;
1161                        for(OWLObjectProperty r : objectPropertyHierarchy.getMostGeneralRoles()) {
1162                                m.get(lc).add(df.getOWLObjectAllValuesFrom(r, df.getOWLThing()));
1163
1164                                if(useInverse) {
1165                                        m.get(lc_i).add(df.getOWLObjectAllValuesFrom(r.getInverseProperty(), df.getOWLThing()));
1166                                }
1167                        }
1168                }
1169
1170                // boolean datatypes, e.g. testPositive = true
1171                if(useBooleanDatatypes) {
1172                        Set<OWLDataProperty> booleanDPs = Sets.intersection(dataPropertyHierarchy.getEntities(), reasoner.getBooleanDatatypeProperties());
1173                        logger.debug(sparql_debug, "BOOL DPs:"+booleanDPs);
1174                        int lc = lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength;
1175                        for(OWLDataProperty dp : booleanDPs) {
1176                                m.get(lc).add(df.getOWLDataHasValue(dp, df.getOWLLiteral(true)));
1177                                m.get(lc).add(df.getOWLDataHasValue(dp, df.getOWLLiteral(false)));
1178                        }
1179                }
1180
1181                if(useNumericDatatypes) {
1182                        Set<OWLDataProperty> numericDPs = Sets.intersection(dataPropertyHierarchy.getEntities(), reasoner.getNumericDataProperties());
1183                        logger.debug(sparql_debug, "Numeric DPs:"+numericDPs);
1184                        int lc = lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1;
1185                        for(OWLDataProperty dp : numericDPs) {
1186                                addNumericFacetRestrictions(lc, dp);
1187                        }
1188                }
1189
1190                if(useTimeDatatypes) {
1191                        Set<OWLDataProperty> dataProperties = dataPropertyHierarchy.getEntities().stream()
1192                                        .filter(dp ->
1193                                        {
1194                                                OWLDatatype datatype = reasoner.getDatatype(dp);
1195                                                return (datatype != null && OWLAPIUtils.dtDatatypes.contains(datatype));
1196                                        }).collect(Collectors.toSet());
1197                        int lc = lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1;
1198                        for(OWLDataProperty dp : dataProperties) {
1199                                addNumericFacetRestrictions(lc, dp);
1200                        }
1201                }
1202
1203                if(useDataHasValueConstructor) {
1204                        Set<OWLDataProperty> stringDPs = Sets.intersection(dataPropertyHierarchy.getEntities(), reasoner.getStringDatatypeProperties());
1205                        logger.debug(sparql_debug, "STRING DPs:"+stringDPs);
1206                        int lc = lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength;
1207                        for(OWLDataProperty dp : stringDPs) {
1208                                // loop over frequent values
1209                                Set<OWLLiteral> freqValues = frequentDataValues.get(dp);
1210                                for(OWLLiteral lit : freqValues) {
1211                                        m.get(lc).add(df.getOWLDataHasValue(dp, lit));
1212                                }
1213                        }
1214                }
1215
1216                if(useHasValueConstructor) {
1217                        int lc = lengthMetric.objectHasValueLength + lengthMetric.objectProperyLength;
1218                        int lc_i = lengthMetric.objectHasValueLength + lengthMetric.objectInverseLength;
1219
1220                        for(OWLObjectProperty p : objectPropertyHierarchy.getMostGeneralRoles()) {
1221                                Set<OWLIndividual> values = frequentValues.get(p);
1222                                values.forEach(val -> m.get(lc).add(df.getOWLObjectHasValue(p, val)));
1223
1224                                if(useInverse) {
1225                                        values.forEach(val -> m.get(lc_i).add(df.getOWLObjectHasValue(p.getInverseProperty(), val)));
1226                                }
1227                        }
1228                }
1229
1230                if(useCardinalityRestrictions) {
1231                        logger.debug(sparql_debug, "most general properties:");
1232                        int lc = lengthMetric.objectCardinalityLength + lengthMetric.objectProperyLength + lengthMetric.classLength;
1233                        for(OWLObjectProperty r : objectPropertyHierarchy.getMostGeneralRoles()) {
1234                                int maxFillers = maxNrOfFillers.get(r);
1235                                logger.debug(sparql_debug, "`"+r+"="+maxFillers);
1236                                // zero fillers: <= -1 r.C does not make sense
1237                                // one filler: <= 0 r.C is equivalent to NOT EXISTS r.C,
1238                                // but we still keep it, because ALL r.NOT C may be difficult to reach
1239                                if((useNegation && maxFillers > 0) || (!useNegation && maxFillers > 1))
1240                                        m.get(lc).add(df.getOWLObjectMaxCardinality(maxFillers-1, r, df.getOWLThing()));
1241
1242//                              m.get(lc).add(df.getOWLObjectExactCardinality(1, r, df.getOWLThing()));
1243                        }
1244                }
1245
1246                if(useHasSelf) {
1247                        int lc = lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.objectHasSelfLength;
1248                        for(OWLObjectProperty p : objectPropertyHierarchy.getMostGeneralRoles()) {
1249                                m.get(lc).add(df.getOWLObjectHasSelf(p));
1250                        }
1251                }
1252
1253                logger.debug(sparql_debug, "m: " + m);
1254
1255                mComputationTimeNs += System.nanoTime() - mComputationTimeStartNs;
1256        }
1257
1258        private void addNumericFacetRestrictions(int lc, OWLDataProperty dp) {
1259                if(splits.get(dp) != null && splits.get(dp).size() > 0) {
1260                        OWLLiteral min = splits.get(dp).get(0);
1261                        OWLLiteral max = splits.get(dp).get(splits.get(dp).size()-1);
1262
1263                                OWLDatatypeRestriction restriction = asDatatypeRestriction(dp, min, OWLFacet.MIN_INCLUSIVE);
1264                                m.get(lc).add(df.getOWLDataSomeValuesFrom(dp, restriction));
1265
1266                                restriction = asDatatypeRestriction(dp, max, OWLFacet.MAX_INCLUSIVE);
1267                                m.get(lc).add(df.getOWLDataSomeValuesFrom(dp, restriction));
1268                }
1269        }
1270
1271        private OWLDatatypeRestriction asDatatypeRestriction(OWLDataProperty dp, OWLLiteral value, OWLFacet facet) {
1272                OWLDatatype datatype = reasoner.getDatatype(dp);
1273
1274                OWLDatatypeRestriction restriction = df.getOWLDatatypeRestriction(
1275                                datatype,
1276                                Collections.singleton(df.getOWLFacetRestriction(
1277                                                facet,
1278                                                value)));
1279                return restriction;
1280        }
1281
1282        // computation of the set M_A
1283        // a major difference compared to the ILP 2007 \rho operator is that
1284        // M is finite and contains elements of length (currently) at most 3
1285        private void computeM(OWLClassExpression nc) {
1286                long mComputationTimeStartNs = System.nanoTime();
1287
1288                mA.put(nc, new TreeMap<>());
1289                // initialise all possible lengths (1 to mMaxLength)
1290                for(int i=1; i<=mMaxLength; i++) {
1291                        mA.get(nc).put(i, new TreeSet<>());
1292                }
1293
1294                // most general classes, which are not disjoint with nc and provide real refinement
1295                SortedSet<OWLClassExpression> m1 = getClassCandidates(nc);
1296                mA.get(nc).get(lengthMetric.classLength).addAll(m1);
1297
1298                // most specific negated classes, which are not disjoint with nc
1299                if(useNegation) {
1300                        SortedSet<OWLClassExpression> m2;
1301                        m2 = getNegClassCandidates(nc);
1302                        mA.get(nc).get(lengthMetric.classLength + lengthMetric.objectComplementLength).addAll(m2);
1303                }
1304
1305                // compute applicable properties
1306                computeMg(nc);
1307
1308                // boolean datatypes, e.g. testPositive = true
1309                if(useBooleanDatatypes) {
1310                        int lc = lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength;
1311                        Set<OWLDataProperty> booleanDPs = mgbd.get(nc);
1312                        for (OWLDataProperty dp : booleanDPs) {
1313                                mA.get(nc).get(lc).add(df.getOWLDataHasValue(dp, df.getOWLLiteral(true)));
1314                                mA.get(nc).get(lc).add(df.getOWLDataHasValue(dp, df.getOWLLiteral(false)));
1315                        }
1316                }
1317
1318                if(useExistsConstructor) {
1319                        int lc = lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength;
1320                        for(OWLObjectProperty r : mgr.get(nc)) {
1321                                mA.get(nc).get(lc).add(df.getOWLObjectSomeValuesFrom(r, df.getOWLThing()));
1322                        }
1323                }
1324
1325                if(useAllConstructor) {
1326                        // we allow \forall r.\top here because otherwise the operator
1327                        // becomes too difficult to manage due to dependencies between
1328                        // M_A and M_A' where A'=ran(r)
1329                        int lc = lengthMetric.objectAllValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength;
1330                        for(OWLObjectProperty r : mgr.get(nc)) {
1331                                mA.get(nc).get(lc).add(df.getOWLObjectAllValuesFrom(r, df.getOWLThing()));
1332                        }
1333                }
1334
1335                if(useNumericDatatypes) {
1336                        Set<OWLDataProperty> numericDPs = mgNumeric.get(nc);
1337                        int lc = lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1;
1338
1339                        for(OWLDataProperty dp : numericDPs) {
1340                                List<OWLLiteral> splitLiterals = splits.get(dp);
1341                                if(splitLiterals != null && splitLiterals.size() > 0) {
1342                                        OWLLiteral min = splits.get(dp).get(0);
1343                                        OWLLiteral max = splits.get(dp).get(splits.get(dp).size()-1);
1344                                        mA.get(nc).get(lc).add(df.getOWLDataSomeValuesFrom(dp, asDatatypeRestriction(dp, min, OWLFacet.MIN_INCLUSIVE)));
1345                                        mA.get(nc).get(lc).add(df.getOWLDataSomeValuesFrom(dp, asDatatypeRestriction(dp, max, OWLFacet.MAX_INCLUSIVE)));
1346                                }
1347                        }
1348                }
1349
1350                if(useTimeDatatypes) {
1351                        Set<OWLDataProperty> dtDPs = mgDT.get(nc);
1352                        int lc = lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1;
1353
1354                        for(OWLDataProperty dp : dtDPs) {
1355                                if(splits.get(dp).size() > 0) {
1356                                        OWLLiteral min = splits.get(dp).get(0);
1357                                        OWLLiteral max = splits.get(dp).get(splits.get(dp).size()-1);
1358                                        mA.get(nc).get(lc).add(df.getOWLDataSomeValuesFrom(dp, asDatatypeRestriction(dp, min, OWLFacet.MIN_INCLUSIVE)));
1359                                        mA.get(nc).get(lc).add(df.getOWLDataSomeValuesFrom(dp, asDatatypeRestriction(dp, max, OWLFacet.MAX_INCLUSIVE)));
1360                                }
1361                        }
1362                }
1363
1364                if(useDataHasValueConstructor) {
1365                        Set<OWLDataProperty> stringDPs = mgsd.get(nc);
1366                        int lc = lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength;
1367                        for(OWLDataProperty dp : stringDPs) {
1368                                // loop over frequent values
1369                                Set<OWLLiteral> freqValues = frequentDataValues.get(dp);
1370                                for(OWLLiteral lit : freqValues) {
1371                                        mA.get(nc).get(lc).add(df.getOWLDataHasValue(dp, lit));
1372                                }
1373                        }
1374                }
1375
1376                if(useHasValueConstructor) {
1377                        int lc = lengthMetric.objectHasValueLength + lengthMetric.objectProperyLength;
1378                        int lc_i = lengthMetric.objectHasValueLength + lengthMetric.objectInverseLength;
1379//
1380//                      m.get(lc).addAll(
1381//                                      mgr.get(nc).stream()
1382//                                                      .flatMap(p -> frequentValues.get(p).stream()
1383//                                                                      .map(val -> df.getOWLObjectHasValue(p, val)))
1384//                                                      .collect(Collectors.toSet()));
1385                        for(OWLObjectProperty p : mgr.get(nc)) {
1386                                Set<OWLIndividual> values = frequentValues.get(p);
1387                                values.forEach(val -> m.get(lc).add(df.getOWLObjectHasValue(p, val)));
1388
1389                                if(useInverse) {
1390                                        values.forEach(val -> m.get(lc_i).add(df.getOWLObjectHasValue(p.getInverseProperty(), val)));
1391                                }
1392                        }
1393                }
1394
1395                if(useCardinalityRestrictions) {
1396                        int lc = lengthMetric.objectCardinalityLength + lengthMetric.objectProperyLength + lengthMetric.classLength;
1397                        for(OWLObjectProperty r : mgr.get(nc)) {
1398                                int maxFillers = maxNrOfFillers.get(r);
1399                                // zero fillers: <= -1 r.C does not make sense
1400                                // one filler: <= 0 r.C is equivalent to NOT EXISTS r.C,
1401                                // but we still keep it, because ALL r.NOT C may be difficult to reach
1402                                if((useNegation && maxFillers > 0) || (!useNegation && maxFillers > 1)) {
1403                                        mA.get(nc).get(lc).add(df.getOWLObjectMaxCardinality(maxFillers-1, r, df.getOWLThing()));
1404                                }
1405
1406                                // = 1 r.C
1407//                              mA.get(nc).get(lc).add(df.getOWLObjectExactCardinality(1, r, df.getOWLThing()));
1408                        }
1409                }
1410
1411                if(useHasSelf) {
1412                        int lc = lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.objectHasSelfLength;
1413                        for(OWLObjectProperty p : mgr.get(nc)) {
1414                                m.get(lc).add(df.getOWLObjectHasSelf(p));
1415                        }
1416                }
1417
1418                logger.debug(sparql_debug, "m for " + nc + ": " + mA.get(nc));
1419
1420                mComputationTimeNs += System.nanoTime() - mComputationTimeStartNs;
1421        }
1422
1423        // get candidates for a refinement of \top restricted to a class B
1424        public SortedSet<OWLClassExpression> getClassCandidates(OWLClassExpression index) {
1425                return getClassCandidatesRecursive(index, df.getOWLThing());
1426        }
1427
1428        private SortedSet<OWLClassExpression> getClassCandidatesRecursive(OWLClassExpression index, OWLClassExpression upperClass) {
1429                SortedSet<OWLClassExpression> candidates = new TreeSet<>();
1430
1431                SortedSet<OWLClassExpression> subClasses = classHierarchy.getSubClasses(upperClass, true);
1432
1433                if(reasoner instanceof SPARQLReasoner) {
1434                        Collection<? extends OWLClassExpression> meaningfulClasses = ((SPARQLReasoner)reasoner).getMeaningfulClasses(index, subClasses);
1435                        candidates.addAll(meaningfulClasses);
1436                        // recursive call, i.e. go class hierarchy down for non-meaningful classes
1437//                      for (OWLClassExpression cls : Sets.difference(superClasses, meaningfulClasses)) {
1438//                              candidates.addAll(getNegClassCandidatesRecursive(index, cls));
1439//                      }
1440                } else {
1441
1442                        // we descend the subsumption hierarchy to ensure that we get
1443                        // the most general concepts satisfying the criteria
1444                        for(OWLClassExpression candidate :  subClasses) {
1445//                                      System.out.println("testing " + candidate + " ... ");
1446        //                              NamedClass candidate = (OWLClass) d;
1447                                        // check disjointness with index (if not no further traversal downwards is necessary)
1448                                        if(!isDisjoint(candidate,index)) {
1449//                                              System.out.println( " passed disjointness test ... ");
1450                                                // check whether the class is meaningful, i.e. adds something to the index
1451                                                // to do this, we need to make sure that the class is not a superclass of the
1452                                                // index (otherwise we get nothing new) - for instance based disjoints, we
1453                                                // make sure that there is at least one individual, which is not already in the
1454                                                // upper class
1455                                                boolean meaningful;
1456                                                if(instanceBasedDisjoints) {
1457                                                        // bug: tests should be performed against the index, not the upper class
1458        //                                              SortedSet<OWLIndividual> tmp = rs.getIndividuals(upperClass);
1459                                                        SortedSet<OWLIndividual> tmp = reasoner.getIndividuals(index);
1460                                                        tmp.removeAll(reasoner.getIndividuals(candidate));
1461        //                                              System.out.println("  instances of " + index + " and not " + candidate + ": " + tmp.size());
1462                                                        meaningful = tmp.size() != 0;
1463                                                } else {
1464                                                        meaningful = !isDisjoint(df.getOWLObjectComplementOf(candidate),index);
1465                                                }
1466
1467                                                if(meaningful) {
1468                                                        // candidate went successfully through all checks
1469                                                        candidates.add(candidate);
1470        //                                              System.out.println(" real refinement");
1471                                                } else {
1472                                                        // descend subsumption hierarchy to find candidates
1473        //                                              System.out.println(" enter recursion");
1474                                                        candidates.addAll(getClassCandidatesRecursive(index, candidate));
1475                                                }
1476                                        }
1477        //                              else {
1478        //                                      System.out.println(" ruled out, because it is disjoint");
1479        //                              }
1480                        }
1481                }
1482//              System.out.println("cc method exit");
1483                return candidates;
1484        }
1485
1486        // get candidates for a refinement of \top restricted to a class B
1487        public SortedSet<OWLClassExpression> getNegClassCandidates(OWLClassExpression index) {
1488                return getNegClassCandidatesRecursive(index, df.getOWLNothing(), null);
1489        }
1490
1491        private SortedSet<OWLClassExpression> getNegClassCandidatesRecursive(OWLClassExpression index, OWLClassExpression lowerClass, Set<OWLClassExpression> seenClasses) {
1492                if (seenClasses == null) { seenClasses = new TreeSet<>(); }
1493                SortedSet<OWLClassExpression> candidates = new TreeSet<>();
1494//              System.out.println("index " + index + " lower class " + lowerClass);
1495
1496                SortedSet<OWLClassExpression> superClasses = classHierarchy.getSuperClasses(lowerClass);
1497
1498                if(reasoner instanceof SPARQLReasoner) {
1499                        Collection<? extends OWLClassExpression> meaningfulClasses = ((SPARQLReasoner)reasoner).getMeaningfulClasses(index, superClasses);
1500                        candidates.addAll(meaningfulClasses);
1501                        // recursive call, i.e. go class hierarchy up for non-meaningful classes
1502//                      for (OWLClassExpression cls : Sets.difference(superClasses, meaningfulClasses)) {
1503//                              candidates.addAll(getNegClassCandidatesRecursive(index, cls));
1504//                      }
1505                } else {
1506                        for(OWLClassExpression candidate : superClasses) {
1507                                if(!candidate.isOWLThing()) {
1508                                        OWLObjectComplementOf negatedCandidate = df.getOWLObjectComplementOf(candidate);
1509
1510                                        // check disjointness with index/range (should not be disjoint otherwise not useful)
1511                                        if(!isDisjoint(negatedCandidate,index)) {
1512                                                boolean meaningful;
1513
1514                                                if(instanceBasedDisjoints) {
1515                                                        SortedSet<OWLIndividual> tmp = reasoner.getIndividuals(index);
1516                                                        tmp.removeAll(reasoner.getIndividuals(negatedCandidate));
1517                                                        meaningful = tmp.size() != 0;
1518                                                } else {
1519                                                        meaningful = !isDisjoint(candidate,index);
1520                                                }
1521
1522                                                if(meaningful) {
1523                                                        candidates.add(negatedCandidate);
1524                                                } else if (!seenClasses.contains(candidate)) {
1525                                                        seenClasses.add(candidate);
1526                                                        candidates.addAll(getNegClassCandidatesRecursive(index, candidate, seenClasses));
1527                                                }
1528                                        }
1529                                }
1530                        }
1531                }
1532
1533                return candidates;
1534        }
1535
1536        private void computeMg(OWLClassExpression domain) {
1537                // compute the applicable properties if this has not been done yet
1538                if(appOP.get(domain) == null)
1539                        computeApp(domain);
1540
1541                // initialise mgr, mgbd, mgdd, mgsd
1542                mgr.put(domain, new TreeSet<>());
1543                mgbd.put(domain, new TreeSet<>());
1544                mgNumeric.put(domain, new TreeSet<>());
1545                mgsd.put(domain, new TreeSet<>());
1546                mgDT.put(domain, new TreeSet<>());
1547
1548                SortedSet<OWLObjectProperty> mostGeneral = objectPropertyHierarchy.getMostGeneralRoles();
1549                computeMgrRecursive(domain, mostGeneral, mgr.get(domain));
1550                SortedSet<OWLDataProperty> mostGeneralDP = dataPropertyHierarchy.getMostGeneralRoles();
1551                // we make the (reasonable) assumption here that all sub and super
1552                // datatype properties have the same type (e.g. boolean, integer, double)
1553                Set<OWLDataProperty> mostGeneralBDP = Sets.intersection(mostGeneralDP, reasoner.getBooleanDatatypeProperties());
1554                Set<OWLDataProperty> mostGeneralNumericDPs = Sets.intersection(mostGeneralDP, reasoner.getNumericDataProperties());
1555                Set<OWLDataProperty> mostGeneralStringDPs = Sets.intersection(mostGeneralDP, reasoner.getStringDatatypeProperties());
1556                computeMgbdRecursive(domain, mostGeneralBDP, mgbd.get(domain));
1557                computeMostGeneralNumericDPRecursive(domain, mostGeneralNumericDPs, mgNumeric.get(domain));
1558                computeMostGeneralStringDPRecursive(domain, mostGeneralStringDPs, mgsd.get(domain));
1559        }
1560
1561        private void computeMgrRecursive(OWLClassExpression domain, Set<OWLObjectProperty> currProperties, Set<OWLObjectProperty> mgrTmp) {
1562                for(OWLObjectProperty prop : currProperties) {
1563                        if(appOP.get(domain).contains(prop))
1564                                mgrTmp.add(prop);
1565                        else
1566                                computeMgrRecursive(domain, objectPropertyHierarchy.getMoreSpecialRoles(prop), mgrTmp);
1567                }
1568        }
1569
1570        private void computeMgbdRecursive(OWLClassExpression domain, Set<OWLDataProperty> currProperties, Set<OWLDataProperty> mgbdTmp) {
1571                for(OWLDataProperty prop : currProperties) {
1572                        if(appBD.get(domain).contains(prop))
1573                                mgbdTmp.add(prop);
1574                        else
1575                                computeMgbdRecursive(domain, dataPropertyHierarchy.getMoreSpecialRoles(prop), mgbdTmp);
1576                }
1577        }
1578
1579        private void computeMostGeneralNumericDPRecursive(OWLClassExpression domain, Set<OWLDataProperty> currProperties, Set<OWLDataProperty> mgddTmp) {
1580                for(OWLDataProperty prop : currProperties) {
1581                        if(appNumeric.get(domain).contains(prop))
1582                                mgddTmp.add(prop);
1583                        else
1584                                computeMostGeneralNumericDPRecursive(domain, dataPropertyHierarchy.getMoreSpecialRoles(prop), mgddTmp);
1585                }
1586        }
1587        private void computeMostGeneralStringDPRecursive(OWLClassExpression domain, Set<OWLDataProperty> currProperties, Set<OWLDataProperty> mgddTmp) {
1588                for(OWLDataProperty prop : currProperties) {
1589                        if(appSD.get(domain).contains(prop))
1590                                mgddTmp.add(prop);
1591                        else
1592                                computeMostGeneralStringDPRecursive(domain, dataPropertyHierarchy.getMoreSpecialRoles(prop), mgddTmp);
1593                }
1594        }
1595
1596        // computes the set of applicable properties for a given class
1597        private void computeApp(OWLClassExpression domain) {
1598                Set<OWLObjectProperty> applicableRoles = new TreeSet<>();
1599                Set<OWLObjectProperty> mostGeneral = objectPropertyHierarchy.getMostGeneralRoles();
1600                if (reasoner instanceof SPARQLReasoner) {
1601                        Set<OWLObjectProperty> roleAppRoles = ((SPARQLReasoner) reasoner).getApplicableProperties(domain, mostGeneral);
1602                        applicableRoles.addAll(roleAppRoles);
1603                } else {
1604                        SortedSet<OWLIndividual> individuals1 = reasoner.getIndividuals(domain);
1605                        // object properties
1606                        for (OWLObjectProperty role : mostGeneral) {
1607                                // TODO: currently we just rely on named classes as roles,
1608                                // instead of computing dom(r) and ran(r)
1609                                OWLClassExpression d = opDomains.get(role);
1610
1611                                Set<OWLIndividual> individuals2 = new HashSet<>();
1612                                for (Entry<OWLIndividual, SortedSet<OWLIndividual>> entry : reasoner.getPropertyMembers(role).entrySet()) {
1613                                        OWLIndividual ind = entry.getKey();
1614                                        if (!entry.getValue().isEmpty()) {
1615                                                individuals2.add(ind);
1616                                        }
1617                                }
1618
1619                                boolean disjoint = Sets.intersection(individuals1, individuals2).isEmpty();
1620//                      if(!isDisjoint(domain,d))
1621                                if (!disjoint) {
1622                                        applicableRoles.add(role);
1623                                }
1624
1625                        }
1626                }
1627
1628                appOP.put(domain, applicableRoles);
1629
1630                // boolean datatype properties
1631                Set<OWLDataProperty> allBDPs = Sets.intersection(dataPropertyHierarchy.getEntities(), reasoner.getBooleanDatatypeProperties());
1632                Set<OWLDataProperty> applicableBDPs = new TreeSet<>();
1633                for(OWLDataProperty role : allBDPs) {
1634//                      Description d = (OWLClass) rs.getDomain(role);
1635                        OWLClassExpression d = dpDomains.get(role);
1636                        if(!isDisjoint(domain,d))
1637                                applicableBDPs.add(role);
1638                }
1639                appBD.put(domain, applicableBDPs);
1640
1641                // numeric data properties
1642                Set<OWLDataProperty> allNumericDPs = Sets.intersection(dataPropertyHierarchy.getEntities(), reasoner.getNumericDataProperties());
1643                Set<OWLDataProperty> applicableNumericDPs = new TreeSet<>();
1644                for(OWLDataProperty role : allNumericDPs) {
1645                        // get domain of property
1646                        OWLClassExpression d = dpDomains.get(role);
1647                        // check if it's not disjoint with current class expression
1648                        if(!isDisjoint(domain,d))
1649                                applicableNumericDPs.add(role);
1650                }
1651                appNumeric.put(domain, applicableNumericDPs);
1652
1653                // string datatype properties
1654                Set<OWLDataProperty> allSDPs = Sets.intersection(dataPropertyHierarchy.getEntities(), reasoner.getStringDatatypeProperties());
1655                Set<OWLDataProperty> applicableSDPs = new TreeSet<>();
1656                for(OWLDataProperty role : allSDPs) {
1657//                      Description d = (OWLClass) rs.getDomain(role);
1658                        OWLClassExpression d = dpDomains.get(role);
1659//                      System.out.println("domain: " + d);
1660                        if(!isDisjoint(domain,d))
1661                                applicableSDPs.add(role);
1662                }
1663                appSD.put(domain, applicableSDPs);
1664
1665        }
1666
1667        // returns true if the intersection contains elements disjoint
1668        // to the given OWLClassExpression (if true adding the OWLClassExpression to
1669        // the intersection results in a OWLClassExpression equivalent to bottom)
1670        // e.g. OldPerson AND YoungPerson; Nitrogen-34 AND Tin-113
1671        // Note: currently we only check named classes in the intersection,
1672        // it would be interesting to see whether it makes sense to extend this
1673        // (advantage: less refinements, drawback: operator will need infinitely many
1674        // reasoner queries in the long run)
1675        @SuppressWarnings({"unused"})
1676        private boolean containsDisjoints(OWLObjectIntersectionOf intersection, OWLClassExpression d) {
1677                List<OWLClassExpression> children = intersection.getOperandsAsList();
1678                for(OWLClassExpression child : children) {
1679                        if(d.isOWLNothing())
1680                                return true;
1681                        else if(!child.isAnonymous()) {
1682                                if(isDisjoint(child, d))
1683                                        return true;
1684                        }
1685                }
1686                return false;
1687        }
1688
1689        private boolean isDisjoint(OWLClassExpression d1, OWLClassExpression d2) {
1690                if(d1.isOWLThing() || d2.isOWLThing()) {
1691                        return false;
1692                }
1693
1694                if(d1.isOWLNothing() || d2.isOWLNothing()) {
1695                        return true;
1696                }
1697
1698                // check whether we have cached this query
1699                Map<OWLClassExpression,Boolean> tmp = cachedDisjoints.get(d1);
1700                if(tmp != null && tmp.containsKey(d2)) {
1701                        return tmp.get(d2);
1702                }
1703
1704                // compute the disjointness
1705                Boolean result;
1706                if (instanceBasedDisjoints) {
1707                        result = isDisjointInstanceBased(d1, d2);
1708                } else {
1709                        OWLClassExpression d = df.getOWLObjectIntersectionOf(d1, d2);
1710                        result = reasoner.isSuperClassOf(df.getOWLNothing(), d);
1711                }
1712                // add the result to the cache (we add it twice such that
1713                // the order of access does not matter)
1714
1715                // create new entries if necessary
1716                if (tmp == null)
1717                        cachedDisjoints.put(d1, new TreeMap<>());
1718                if (!cachedDisjoints.containsKey(d2))
1719                        cachedDisjoints.put(d2, new TreeMap<>());
1720
1721                // add result symmetrically in the OWLClassExpression matrix
1722                cachedDisjoints.get(d1).put(d2, result);
1723                cachedDisjoints.get(d2).put(d1, result);
1724                //                      System.out.println("---");
1725                return result;
1726        }
1727
1728        private boolean isDisjointInstanceBased(OWLClassExpression d1, OWLClassExpression d2) {
1729                if(reasoner instanceof SPARQLReasoner) {
1730                        SortedSet<OWLIndividual> individuals = reasoner.getIndividuals(df.getOWLObjectIntersectionOf(d1, d2));
1731                        return individuals.isEmpty();
1732                } else {
1733                        SortedSet<OWLIndividual> d1Instances = reasoner.getIndividuals(d1);
1734                        SortedSet<OWLIndividual> d2Instances = reasoner.getIndividuals(d2);
1735
1736                        for(OWLIndividual d1Instance : d1Instances) {
1737                                if(d2Instances.contains(d1Instance))
1738                                        return false;
1739                        }
1740                        return true;
1741                }
1742        }
1743
1744        /*
1745        // computes whether two classes are disjoint; this should be computed
1746        // by the reasoner only ones and otherwise taken from a matrix
1747        private boolean isDisjoint(OWLClass a, OWLClassExpression d) {
1748                // we need to test whether A AND B is equivalent to BOTTOM
1749                Description d2 = new Intersection(a, d);
1750                return rs.subsumes(new Nothing(), d2);
1751        }*/
1752
1753        // we need to test whether NOT A AND B is equivalent to BOTTOM
1754        @SuppressWarnings("unused")
1755        private boolean isNotADisjoint(OWLClass a, OWLClass b) {
1756//              Map<OWLClass,Boolean> tmp = notABDisjoint.get(a);
1757//              Boolean tmp2 = null;
1758//              if(tmp != null)
1759//                      tmp2 = tmp.get(b);
1760//
1761//              if(tmp2==null) {
1762                OWLClassExpression notA = df.getOWLObjectComplementOf(a);
1763                OWLClassExpression d = df.getOWLObjectIntersectionOf(notA, b);
1764                        Boolean result = reasoner.isSuperClassOf(df.getOWLNothing(), d);
1765                        // ... add to cache ...
1766                        return result;
1767//              } else
1768//                      return tmp2;
1769        }
1770
1771        // we need to test whether NOT A AND B = B
1772        // (if not then NOT A is not meaningful in the sense that it does
1773        // not semantically add anything to B)
1774        @SuppressWarnings("unused")
1775        private boolean isNotAMeaningful(OWLClass a, OWLClass b) {
1776                OWLClassExpression notA = df.getOWLObjectComplementOf(a);
1777                OWLClassExpression d = df.getOWLObjectIntersectionOf(notA, b);
1778                // check b subClassOf b AND NOT A (if yes then it is not meaningful)
1779                return !reasoner.isSuperClassOf(d, b);
1780        }
1781
1782        public int getFrequencyThreshold() {
1783                return frequencyThreshold;
1784        }
1785
1786        public void setFrequencyThreshold(int frequencyThreshold) {
1787                this.frequencyThreshold = frequencyThreshold;
1788        }
1789
1790        public boolean isUseDataHasValueConstructor() {
1791                return useDataHasValueConstructor;
1792        }
1793
1794        public void setUseDataHasValueConstructor(boolean useDataHasValueConstructor) {
1795                isFinal();
1796                this.useDataHasValueConstructor = useDataHasValueConstructor;
1797        }
1798
1799        public boolean isApplyAllFilter() {
1800                return applyAllFilter;
1801        }
1802
1803        public void setApplyAllFilter(boolean applyAllFilter) {
1804                this.applyAllFilter = applyAllFilter;
1805        }
1806
1807        public boolean isApplyExistsFilter() {
1808                return applyExistsFilter;
1809        }
1810
1811        public void setApplyExistsFilter(boolean applyExistsFilter) {
1812                this.applyExistsFilter = applyExistsFilter;
1813        }
1814
1815        public boolean isUseAllConstructor() {
1816                return useAllConstructor;
1817        }
1818
1819        public void setUseAllConstructor(boolean useAllConstructor) {
1820                this.useAllConstructor = useAllConstructor;
1821        }
1822
1823        public boolean isUseExistsConstructor() {
1824                return useExistsConstructor;
1825        }
1826
1827        public void setUseExistsConstructor(boolean useExistsConstructor) {
1828                this.useExistsConstructor = useExistsConstructor;
1829        }
1830
1831        public boolean isUseHasValueConstructor() {
1832                return useHasValueConstructor;
1833        }
1834
1835        public void setUseHasValueConstructor(boolean useHasValueConstructor) {
1836                isFinal();
1837                this.useHasValueConstructor = useHasValueConstructor;
1838        }
1839
1840        public boolean isUseCardinalityRestrictions() {
1841                return useCardinalityRestrictions;
1842        }
1843
1844        public void setUseCardinalityRestrictions(boolean useCardinalityRestrictions) {
1845                isFinal();
1846                this.useCardinalityRestrictions = useCardinalityRestrictions;
1847        }
1848
1849        public boolean isUseHasSelf() {
1850                return useHasSelf;
1851        }
1852
1853        public void setUseHasSelf(boolean useHasSelf) {
1854                this.useHasSelf = useHasSelf;
1855        }
1856
1857        public boolean isUseNegation() {
1858                return useNegation;
1859        }
1860
1861        public void setUseNegation(boolean useNegation) {
1862                this.useNegation = useNegation;
1863        }
1864
1865        public boolean isUseBooleanDatatypes() {
1866                return useBooleanDatatypes;
1867        }
1868
1869        public void setUseBooleanDatatypes(boolean useBooleanDatatypes) {
1870                this.useBooleanDatatypes = useBooleanDatatypes;
1871        }
1872
1873        public boolean isUseStringDatatypes() {
1874                return useStringDatatypes;
1875        }
1876
1877        public void setUseStringDatatypes(boolean useStringDatatypes) {
1878                this.useStringDatatypes = useStringDatatypes;
1879        }
1880
1881        public boolean isInstanceBasedDisjoints() {
1882                return instanceBasedDisjoints;
1883        }
1884
1885        public void setInstanceBasedDisjoints(boolean instanceBasedDisjoints) {
1886                this.instanceBasedDisjoints = instanceBasedDisjoints;
1887        }
1888
1889        public AbstractReasonerComponent getReasoner() {
1890                return reasoner;
1891        }
1892
1893    @Autowired
1894        public void setReasoner(AbstractReasonerComponent reasoner) {
1895                this.reasoner = reasoner;
1896        }
1897
1898        public ClassHierarchy getSubHierarchy() {
1899                return classHierarchy;
1900        }
1901
1902        public void setSubHierarchy(ClassHierarchy subHierarchy) {
1903                this.classHierarchy = subHierarchy;
1904        }
1905
1906        public OWLClassExpression getStartClass() {
1907                return startClass;
1908        }
1909
1910        @Override
1911        public void setStartClass(OWLClassExpression startClass) {
1912                this.startClass = startClass;
1913        }
1914
1915        public int getCardinalityLimit() {
1916                return cardinalityLimit;
1917        }
1918
1919        public void setCardinalityLimit(int cardinalityLimit) {
1920                this.cardinalityLimit = cardinalityLimit;
1921        }
1922
1923        public ObjectPropertyHierarchy getObjectPropertyHierarchy() {
1924                return objectPropertyHierarchy;
1925        }
1926
1927        @Override
1928    public void setObjectPropertyHierarchy(ObjectPropertyHierarchy objectPropertyHierarchy) {
1929                this.objectPropertyHierarchy = objectPropertyHierarchy;
1930        }
1931
1932        public DatatypePropertyHierarchy getDataPropertyHierarchy() {
1933                return dataPropertyHierarchy;
1934        }
1935
1936        @Override
1937    public void setDataPropertyHierarchy(DatatypePropertyHierarchy dataPropertyHierarchy) {
1938                this.dataPropertyHierarchy = dataPropertyHierarchy;
1939        }
1940
1941        @Override
1942        public void setReasoner(Reasoner reasoner) {
1943                this.reasoner = (AbstractReasonerComponent) reasoner;
1944        }
1945
1946        @Override @NoConfigOption
1947        public void setClassHierarchy(ClassHierarchy classHierarchy) {
1948                this.classHierarchy = classHierarchy;
1949        }
1950
1951        /**
1952         * @param useObjectValueNegation the useObjectValueNegation to set
1953         */
1954        public void setUseObjectValueNegation(boolean useObjectValueNegation) {
1955                this.useObjectValueNegation = useObjectValueNegation;
1956        }
1957
1958        public boolean isUseNumericDatatypes() {
1959                return useNumericDatatypes;
1960        }
1961
1962        public void setUseNumericDatatypes(boolean useNumericDatatypes) {
1963                isFinal();
1964                this.useNumericDatatypes = useNumericDatatypes;
1965        }
1966
1967        /**
1968         * @param useInverse whether to use inverse properties in property restrictions
1969         */
1970        public void setUseInverse(boolean useInverse) {
1971                isFinal();
1972                this.useInverse = useInverse;
1973        }
1974
1975        /**
1976         * @param useSomeOnly whether to allow universal restrictions on a property r only if there exists already
1977         * a existential restriction on the same property in an intersection
1978         */
1979        public void setUseSomeOnly(boolean useSomeOnly) {
1980                this.useSomeOnly = useSomeOnly;
1981        }
1982
1983        /**
1984         * @param useTimeDatatypes whether to use data/time literal restrictions
1985         */
1986        public void setUseTimeDatatypes(boolean useTimeDatatypes) {
1987                isFinal();
1988                this.useTimeDatatypes = useTimeDatatypes;
1989        }
1990
1991        public int getMaxNrOfSplits() {
1992                return maxNrOfSplits;
1993        }
1994
1995        public void setMaxNrOfSplits(int maxNrOfSplits) {
1996                this.maxNrOfSplits = maxNrOfSplits;
1997        }
1998
1999        public boolean isDisjointChecks() {
2000                return disjointChecks;
2001        }
2002
2003        public void setDisjointChecks(boolean disjointChecks) {
2004                this.disjointChecks = disjointChecks;
2005        }
2006
2007        @Override
2008        public OWLClassExpressionLengthMetric getLengthMetric() {
2009                return lengthMetric;
2010        }
2011
2012        @Override
2013        public void setLengthMetric(OWLClassExpressionLengthMetric lengthMetric) {
2014                this.lengthMetric = lengthMetric;
2015
2016                mMaxLength = max (
2017                                lengthMetric.classLength,
2018                                lengthMetric.objectComplementLength + lengthMetric.classLength,
2019                                lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength,
2020                                lengthMetric.objectSomeValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength + lengthMetric.objectInverseLength,
2021                                lengthMetric.objectAllValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength,
2022                                lengthMetric.objectAllValuesLength + lengthMetric.objectProperyLength + lengthMetric.classLength + lengthMetric.objectInverseLength,
2023                                lengthMetric.dataHasValueLength + lengthMetric.dataProperyLength,
2024                                lengthMetric.dataSomeValuesLength + lengthMetric.dataProperyLength + 1,
2025                                lengthMetric.objectCardinalityLength + lengthMetric.objectProperyLength + lengthMetric.classLength);
2026
2027                logger.debug("mMaxLength = " + mMaxLength);
2028        }
2029
2030        /**
2031         * Set the splitter used to precompute possible splits for data properties with numeric ranges. Those splits
2032         * will then be used to create facet restrictions during refinement.
2033         *
2034         * @param numericValuesSplitter
2035         */
2036        public void setNumericValuesSplitter(ValuesSplitter numericValuesSplitter) {
2037                this.numericValuesSplitter = numericValuesSplitter;
2038        }
2039}