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.core;
020
021import org.dllearner.core.annotations.NoConfigOption;
022import org.dllearner.core.config.ConfigOption;
023import org.dllearner.core.owl.ClassHierarchy;
024import org.dllearner.core.owl.DatatypePropertyHierarchy;
025import org.dllearner.core.owl.ObjectPropertyHierarchy;
026import org.dllearner.accuracymethods.AccMethodFMeasure;
027import org.dllearner.accuracymethods.AccMethodPredAcc;
028import org.dllearner.accuracymethods.AccMethodTwoValued;
029import org.dllearner.learningproblems.PosNegLP;
030import org.dllearner.utilities.Helper;
031import org.dllearner.utilities.ReasoningUtils;
032import org.dllearner.utilities.datastructures.DescriptionSubsumptionTree;
033import org.dllearner.utilities.owl.ConceptTransformation;
034import org.dllearner.utilities.owl.EvaluatedDescriptionSet;
035import org.dllearner.utilities.owl.OWLClassExpressionMinimizer;
036import org.joda.time.Period;
037import org.joda.time.format.PeriodFormatter;
038import org.joda.time.format.PeriodFormatterBuilder;
039import org.semanticweb.owlapi.io.OWLObjectRenderer;
040import org.semanticweb.owlapi.model.*;
041import org.semanticweb.owlapi.util.OWLObjectDuplicator;
042import org.semanticweb.owlapi.vocab.OWLRDFVocabulary;
043import org.slf4j.Logger;
044import org.slf4j.LoggerFactory;
045import org.springframework.beans.factory.annotation.Autowired;
046import uk.ac.manchester.cs.owl.owlapi.OWLClassImpl;
047import uk.ac.manchester.cs.owl.owlapi.OWLDataFactoryImpl;
048
049import java.text.DecimalFormat;
050import java.util.*;
051import java.util.concurrent.TimeUnit;
052
053/**
054 * Abstract superclass of all class expression learning algorithm implementations.
055 * Includes support for anytime learning algorithms and resumable
056 * learning algorithms. Provides methods for filtering the best
057 * descriptions found by the algorithm. As results of the algorithm,
058 * you can either get only descriptions or evaluated descriptions.
059 * Evaluated descriptions have information about accuracy and
060 * example coverage associated with them. However, retrieving those
061 * may require addition reasoner queries, because the learning
062 * algorithms usually use but do not necessarily store this information.
063 * 
064 * Changes (March/April 2011): Learning algorithms no longer have to use
065 * this class, but it still serves as a prototypical template for class
066 * expression learning algorithms.
067 * 
068 * @author Jens Lehmann
069 *
070 */
071public abstract class AbstractCELA extends AbstractComponent implements ClassExpressionLearningAlgorithm, StoppableLearningAlgorithm {
072        
073        private static final Logger logger = LoggerFactory.getLogger(AbstractCELA.class);
074
075        @NoConfigOption
076        protected OWLObjectRenderer renderer = StringRenderer.getRenderer();
077        
078        protected EvaluatedDescriptionSet bestEvaluatedDescriptions = new EvaluatedDescriptionSet(AbstractCELA.MAX_NR_OF_RESULTS);
079        protected DecimalFormat dfPercent = new DecimalFormat("0.00%");
080        protected String baseURI;
081        protected Map<String, String> prefixes;
082        protected OWLDataFactory dataFactory = new OWLDataFactoryImpl();
083        
084        protected static final OWLClass OWL_THING = new OWLClassImpl(OWLRDFVocabulary.OWL_THING.getIRI());
085        protected static final OWLClass OWL_NOTHING = new OWLClassImpl(OWLRDFVocabulary.OWL_NOTHING.getIRI());
086        
087        protected long nanoStartTime;
088        protected boolean isRunning = false;
089        protected boolean stop = false;
090        
091        protected OWLClassExpressionMinimizer minimizer;
092        
093        @ConfigOption(defaultValue="true", description="Specifies whether returned expressions should " +
094                        "be minimised by removing those parts, which are not needed. (Basically the minimiser tries to find the " +
095                        "shortest expression which is equivalent to the learned expression). Turning this feature off may improve " +
096                        "performance.")
097        private boolean useMinimizer = true;
098        
099        @ConfigOption(defaultValue = "10", description = "maximum execution of the algorithm in seconds")
100        protected long maxExecutionTimeInSeconds = 10;
101
102        /**
103         * The learning problem variable, which must be used by
104         * all learning algorithm implementations.
105         */
106        @ConfigOption(description="The Learning Problem variable to use in this algorithm")
107        protected AbstractClassExpressionLearningProblem<? extends Score> learningProblem;
108        
109        /**
110         * The reasoning service variable, which must be used by
111         * all learning algorithm implementations.
112         */
113        @ConfigOption(description="The reasoner variable to use for this learning problem")
114        protected AbstractReasonerComponent reasoner;
115
116        protected OWLObjectDuplicator duplicator = new OWLObjectDuplicator(new OWLDataFactoryImpl());
117        
118        @ConfigOption(description="List of classes that are allowed")
119        protected Set<OWLClass> allowedConcepts = null;
120        @ConfigOption(description="List of classes to ignore")
121        protected Set<OWLClass> ignoredConcepts = null;
122        @ConfigOption(description="List of object properties to allow")
123        protected Set<OWLObjectProperty> allowedObjectProperties = null;
124        @ConfigOption(description="List of object properties to ignore")
125        protected Set<OWLObjectProperty> ignoredObjectProperties = null;
126        @ConfigOption(description="List of data properties to allow")
127        protected Set<OWLDataProperty> allowedDataProperties = null;
128        @ConfigOption(description="List of data properties to ignore")
129        protected Set<OWLDataProperty> ignoredDataProperties = null;
130
131    /**
132     * Default Constructor
133     */
134    public AbstractCELA(){}
135    
136        /**
137         * Each learning algorithm gets a learning problem and
138         * a reasoner as input.
139         * @param learningProblem The learning problem to solve.
140         * @param reasoningService The reasoner connecting to the
141         * underlying knowledge base.
142         */
143        public AbstractCELA(AbstractClassExpressionLearningProblem learningProblem, AbstractReasonerComponent reasoningService) {
144                this.learningProblem = learningProblem;
145                this.reasoner = reasoningService;
146        }
147        
148        /**
149         * Call this when you want to change the learning problem, but
150         * leave everything else as is. Method can be used to apply
151         * a configured algorithm to different learning problems.
152         * Implementations, which do not only use the provided learning
153         * algorithm variable, must make sure that a call to this method
154         * indeed changes the learning problem.
155         * @param learningProblem The new learning problem.
156         */
157        public void changeLearningProblem(AbstractClassExpressionLearningProblem learningProblem) {
158                this.learningProblem = learningProblem;
159        }
160
161        /**
162         * Call this when you want to change the reasoning service, but
163         * leave everything else as is. Method can be used to use
164         * a configured algorithm with different reasoners.
165         * Implementations, which do not only use the provided reasoning
166         * service class variable, must make sure that a call to this method
167         * indeed changes the reasoning service.
168         * @param reasoningService The new reasoning service.
169         */
170        public void changeReasonerComponent(AbstractReasonerComponent reasoningService) {
171                this.reasoner = reasoningService;
172        }
173        
174        /**
175         * This is the maximum number of results, which the learning
176         * algorithms are asked to store. (Note, that algorithms are not
177         * required to store any results except the best one, so this limit
178         * is used to limit the performance cost for those which
179         * choose to store results.)
180         */
181        public static final int MAX_NR_OF_RESULTS = 100;
182
183        /**
184         * @see #getCurrentlyBestEvaluatedDescription()
185         * @return The best class OWLClassExpression found by the learning algorithm so far.
186         */
187        public OWLClassExpression getCurrentlyBestDescription() {
188                return getCurrentlyBestEvaluatedDescription().getDescription();
189        }
190        
191        /**
192         * @see #getCurrentlyBestEvaluatedDescriptions()
193         * @return The best class descriptions found by the learning algorithm so far.
194         */
195        public List<OWLClassExpression> getCurrentlyBestDescriptions() {
196                return bestEvaluatedDescriptions.toDescriptionList();
197        }
198        
199        /**
200         * @see #getCurrentlyBestEvaluatedDescriptions(int)
201         * @param nrOfDescriptions Limit for the number or returned descriptions.
202         * @return The best class descriptions found by the learning algorithm so far.
203         */
204        @Override
205        public synchronized List<OWLClassExpression> getCurrentlyBestDescriptions(int nrOfDescriptions) {
206                return getCurrentlyBestDescriptions(nrOfDescriptions, false);
207        }
208        
209        /**
210         * @see #getCurrentlyBestEvaluatedDescriptions(int,double,boolean)
211         * @param nrOfDescriptions Limit for the number or returned descriptions.
212         * @param filterNonMinimalDescriptions Remove non-minimal descriptions (e.g. those which can be shortened
213         * to an equivalent concept) from the returned set.
214         * @return The best class descriptions found by the learning algorithm so far.
215         */
216        public synchronized List<OWLClassExpression> getCurrentlyBestDescriptions(int nrOfDescriptions, boolean filterNonMinimalDescriptions) {
217                List<OWLClassExpression> currentlyBest = getCurrentlyBestDescriptions();
218                List<OWLClassExpression> returnList = new LinkedList<>();
219                for(OWLClassExpression ed : currentlyBest) {
220                        if(returnList.size() >= nrOfDescriptions) {
221                                return returnList;
222                        }
223                        
224                        if(!filterNonMinimalDescriptions || ConceptTransformation.isDescriptionMinimal(ed)) {
225                                returnList.add(ed);
226                        }
227                        
228                }
229                return returnList;
230        }
231        
232        /**
233         * Returns the best descriptions obtained so far.
234         * @return Best class class expression found so far.
235         */
236        public EvaluatedDescription<? extends Score> getCurrentlyBestEvaluatedDescription() {
237                return bestEvaluatedDescriptions.getSet().last();
238        }
239        
240        /**
241         * Returns a sorted set of the best descriptions found so far. We
242         * assume that they are ordered such that the best ones come in
243         * last. (In Java, iterators traverse a SortedSet in ascending order.)
244         * @return Best class descriptions found so far.
245         */
246        public NavigableSet<? extends EvaluatedDescription<? extends Score>> getCurrentlyBestEvaluatedDescriptions() {
247                return bestEvaluatedDescriptions.getSet();
248        }
249        
250        /**
251         * Returns a filtered list of currently best class descriptions.
252         * 
253         * @param nrOfDescriptions Maximum number of restrictions. Use Integer.MAX_VALUE
254         * if you do not want this filter to be active.
255         * 
256         * @param accuracyThreshold Minimum accuracy. All class descriptions with lower
257         * accuracy are disregarded. Specify a value between 0.0 and 1.0. Use 0.0 if
258         * you do not want this filter to be active.
259         * 
260         * @param filterNonMinimalDescriptions If true, non-minimal descriptions are
261         * filtered, e.g. ALL r.TOP (being equivalent to TOP), male AND male (can be
262         * shortened to male). Currently, non-minimal descriptions are just skipped,
263         * i.e. they are completely omitted from the return list. Later, implementation
264         * might be changed to return shortened versions of those descriptions.
265         * 
266         * @return A list of currently best class descriptions.
267         */
268        public synchronized List<? extends EvaluatedDescription<? extends Score>> getCurrentlyBestEvaluatedDescriptions(int nrOfDescriptions, double accuracyThreshold, boolean filterNonMinimalDescriptions) {
269                NavigableSet<? extends EvaluatedDescription<? extends Score>> currentlyBest = getCurrentlyBestEvaluatedDescriptions();
270                List<EvaluatedDescription<? extends Score>> returnList = new LinkedList<>();
271                for(EvaluatedDescription<? extends Score> ed : currentlyBest.descendingSet()) {
272                        // once we hit a OWLClassExpression with a below threshold accuracy, we simply return
273                        // because learning algorithms are advised to order descriptions by accuracy,
274                        // so we won't find any concept with higher accuracy in the remaining list
275                        if(ed.getAccuracy() < accuracyThreshold) {
276                                return returnList;
277                        }
278
279                        // return if we have sufficiently many descriptions
280                        if(returnList.size() >= nrOfDescriptions) {
281                                return returnList;
282                        }
283                        
284                        if(!filterNonMinimalDescriptions || ConceptTransformation.isDescriptionMinimal(ed.getDescription())) {
285                                // before we add the OWLClassExpression we replace EXISTS r.TOP with
286                                // EXISTS r.range(r) if range(r) is atomic
287                                // (we need to clone, otherwise we change descriptions which could
288                                // be in the search of the learning algorithm, which leads to
289                                // unpredictable behaviour)
290                                OWLClassExpression d = duplicator.duplicateObject(ed.getDescription());
291                                
292                                //commented out because reasoner is called. leads in swing applications sometimes to exceptions
293//                              ConceptTransformation.replaceRange(d, reasoner);
294                                ed.setDescription(d);
295                                
296                                returnList.add(ed);
297                        }
298                        
299                }
300                return returnList;
301        }
302        
303        /**
304         * Return the best currently found concepts up to some maximum
305         * count (no minimality filter used).
306         * @param nrOfDescriptions Maximum number of descriptions returned.
307         * @return Return value is getCurrentlyBestDescriptions(nrOfDescriptions, 0.0, false).
308         */
309        @Override
310        public synchronized List<? extends EvaluatedDescription<? extends Score>> getCurrentlyBestEvaluatedDescriptions(int nrOfDescriptions) {
311                return getCurrentlyBestEvaluatedDescriptions(nrOfDescriptions, 0.0, false);
312        }
313        
314        /**
315         * Returns a fraction of class descriptions with sufficiently high accuracy.
316         * @param accuracyThreshold Only return solutions with this accuracy or higher.
317         * @return Return value is getCurrentlyBestDescriptions(Integer.MAX_VALUE, accuracyThreshold, false).
318         */
319        public synchronized  List<? extends EvaluatedDescription<? extends Score>> getCurrentlyBestEvaluatedDescriptions(double accuracyThreshold) {
320                return getCurrentlyBestEvaluatedDescriptions(Integer.MAX_VALUE, accuracyThreshold, false);
321        }
322        
323        public synchronized  List<? extends EvaluatedDescription<? extends Score>> getCurrentlyBestMostGeneralEvaluatedDescriptions() {
324                List<? extends EvaluatedDescription<? extends Score>> l = getCurrentlyBestEvaluatedDescriptions(getCurrentlyBestEvaluatedDescriptions().last().getAccuracy());
325                DescriptionSubsumptionTree t = new DescriptionSubsumptionTree(reasoner);
326                t.insert(l);
327                return t.getMostGeneralDescriptions(true);
328        }
329                
330        /**
331         * Returns all learning problems supported by this component. This can be used to indicate that, e.g.
332         * an algorithm is only suitable for positive only learning.
333         * @return All classes implementing learning problems, which are supported by this learning algorithm.
334         */
335        public static Collection<Class<? extends AbstractClassExpressionLearningProblem>> supportedLearningProblems() {
336                return new LinkedList<>();
337        }
338        
339        // central function for printing description
340        protected String descriptionToString(OWLClassExpression description) {
341                return renderer.render(description);
342        }
343                
344        
345        protected String getSolutionString() {
346                int current = 1;
347                String str = "";
348                for(EvaluatedDescription<? extends Score> ed : getCurrentlyBestEvaluatedDescriptions().descendingSet()) {
349                        // temporary code
350                        OWLClassExpression description = ed.getDescription();
351                        String descriptionString = descriptionToString(description);
352                        if(learningProblem instanceof PosNegLP) {
353                                Set<OWLIndividual> positiveExamples = ((PosNegLP)learningProblem).getPositiveExamples();
354                                Set<OWLIndividual> negativeExamples = ((PosNegLP)learningProblem).getNegativeExamples();
355                                ReasoningUtils reasoningUtil = learningProblem.getReasoningUtil();
356                                
357                                str += current + ": " + descriptionString + " (pred. acc.: "
358                                                + dfPercent.format(reasoningUtil.getAccuracyOrTooWeak2(new AccMethodPredAcc(true), description, positiveExamples, negativeExamples, 1))
359                                                + ", F-measure: "+ dfPercent.format(reasoningUtil.getAccuracyOrTooWeak2(new AccMethodFMeasure(true), description, positiveExamples, negativeExamples, 1));
360
361                                AccMethodTwoValued accuracyMethod = ((PosNegLP)learningProblem).getAccuracyMethod();
362                                if ( !(accuracyMethod instanceof AccMethodPredAcc)
363                                                && !(accuracyMethod instanceof AccMethodFMeasure) ) {
364                                        str += ", " + AnnComponentManager.getName(accuracyMethod) + ": " + dfPercent.format(ed.getAccuracy());
365                                }
366                                str += ")\n";
367                        } else {
368                                str += current + ": " + descriptionString + " " + dfPercent.format(ed.getAccuracy()) + "\n";
369//                              System.out.println(ed);
370                        }
371                        current++;
372                }
373                return str;
374        }
375        
376        /**
377         * Computes an internal class hierarchy that only contains classes
378         * that are allowed.
379         * @return optimized class hierarchy
380         */
381        protected ClassHierarchy initClassHierarchy() {
382                // we ignore all unsatisfiable classes
383                Set<OWLClass> unsatisfiableClasses = null;
384                try {
385                unsatisfiableClasses = reasoner.getInconsistentClasses();
386                } catch (UnsupportedOperationException e) {
387                        logger.warn("Ignoring unsatisfiable check due to "+e.getStackTrace()[0]);
388                }
389                if(unsatisfiableClasses != null && !unsatisfiableClasses.isEmpty()) {
390                        logger.warn("Ignoring unsatisfiable classes " + unsatisfiableClasses);
391                        if(ignoredConcepts == null) {
392                                ignoredConcepts = unsatisfiableClasses;
393                        } else {
394                                ignoredConcepts.addAll(unsatisfiableClasses);
395                        }
396                }
397                
398                
399                Set<OWLClass> usedConcepts;
400                if(allowedConcepts != null) {
401                        // sanity check to control if no non-existing concepts are in the list
402                        Helper.checkConcepts(reasoner, allowedConcepts);
403                        usedConcepts = allowedConcepts;
404                } else if(ignoredConcepts != null) {
405                        usedConcepts = Helper.computeConceptsUsingIgnoreList(reasoner, ignoredConcepts);
406                } else {
407                        usedConcepts = Helper.computeConcepts(reasoner);
408                }
409                
410                ClassHierarchy hierarchy = (ClassHierarchy) reasoner.getClassHierarchy().cloneAndRestrict(new HashSet<OWLClassExpression>(usedConcepts));
411//              hierarchy.thinOutSubsumptionHierarchy();
412                return hierarchy;
413        }
414        
415        /**
416         * Computes an internal object property hierarchy that only contains
417         * object properties that are allowed.
418         * @return optimized object property hierarchy
419         */
420        protected ObjectPropertyHierarchy initObjectPropertyHierarchy() {
421                Set<OWLObjectProperty> usedProperties;
422                if(allowedObjectProperties != null) {
423                        // sanity check to control if no non-existing object properties are in the list
424                        Helper.checkRoles(reasoner, allowedObjectProperties);
425                        usedProperties = allowedObjectProperties;
426                } else if(ignoredObjectProperties != null) {
427                        usedProperties = Helper.computeEntitiesUsingIgnoreList(reasoner, EntityType.OBJECT_PROPERTY, ignoredObjectProperties);
428                } else {
429                        usedProperties = Helper.computeEntities(reasoner, EntityType.OBJECT_PROPERTY);
430                }
431                
432                ObjectPropertyHierarchy hierarchy = (ObjectPropertyHierarchy) reasoner.getObjectPropertyHierarchy().cloneAndRestrict(usedProperties);
433//              hierarchy.thinOutSubsumptionHierarchy();
434                
435                return hierarchy;
436        }
437        
438        /**
439         * Computes an internal data property hierarchy that only contains
440         * data properties that are allowed.
441         * @return optimized data property hierarchy
442         */
443        protected DatatypePropertyHierarchy initDataPropertyHierarchy() {
444                Set<OWLDataProperty> usedProperties;
445                if(allowedDataProperties != null) {
446                        // sanity check to control if no non-existing data properties are in the list
447                        Helper.checkEntities(reasoner, allowedDataProperties);
448                        usedProperties = allowedDataProperties;
449                } else if(ignoredDataProperties != null) {
450                        usedProperties = Helper.computeEntitiesUsingIgnoreList(reasoner, EntityType.DATA_PROPERTY, ignoredDataProperties);
451                } else {
452                        usedProperties = Helper.computeEntities(reasoner, EntityType.DATA_PROPERTY);
453                }
454
455                DatatypePropertyHierarchy hierarchy = (DatatypePropertyHierarchy) reasoner.getDatatypePropertyHierarchy().cloneAndRestrict(usedProperties);
456//              hierarchy.thinOutSubsumptionHierarchy();
457                return hierarchy;
458        }
459        
460        protected boolean isTimeExpired() {
461                return getCurrentRuntimeInMilliSeconds() >= TimeUnit.SECONDS.toMillis(maxExecutionTimeInSeconds);
462        }
463
464        protected long getCurrentRuntimeInMilliSeconds() {
465                return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - nanoStartTime);
466        }
467
468        protected long getRemainingRuntimeInMilliseconds() {
469                return Math.max(0, TimeUnit.SECONDS.toMillis(maxExecutionTimeInSeconds) - getCurrentRuntimeInMilliSeconds());
470        }
471        
472        protected String getDurationAsString(long durationInMillis) {
473                PeriodFormatter formatter = new PeriodFormatterBuilder()
474             .appendDays().appendSuffix("d")
475             .appendHours().appendSuffix("h")
476             .appendMinutes().appendSuffix("m")
477             .appendSeconds().appendSuffix("s")
478             .appendMillis().appendSuffix("ms")
479             .printZeroNever()
480             .toFormatter();
481                
482                return formatter.print(new Period(durationInMillis));
483        }
484        
485        protected OWLClassExpression rewrite(OWLClassExpression ce) {
486                // minimize class expression (expensive!) - also performs some human friendly rewrites
487                OWLClassExpression niceDescription;
488                if (useMinimizer) {
489                        niceDescription = minimizer.minimizeClone(ce);
490                } else {
491                        niceDescription = ce;
492                }
493                
494                // replace \exists r.\top with \exists r.range(r) which is easier to read for humans
495                niceDescription = ConceptTransformation.replaceRange(niceDescription, reasoner);
496                
497                niceDescription = ConceptTransformation.appendSomeValuesFrom(niceDescription);
498                
499                return niceDescription;
500        }
501
502    /**
503     * The learning problem variable, which must be used by
504     * all learning algorithm implementations.
505     */
506        @Override
507    public AbstractClassExpressionLearningProblem<? extends Score> getLearningProblem() {
508        return learningProblem;
509    }
510
511    @Autowired
512    @Override
513    public void setLearningProblem(LearningProblem learningProblem) {
514        this.learningProblem = (AbstractClassExpressionLearningProblem<? extends Score>)learningProblem;
515    }
516
517    /**
518     * The reasoning service variable, which must be used by
519     * all learning algorithm implementations.
520     */
521    public AbstractReasonerComponent getReasoner() {
522        return reasoner;
523    }
524
525    @Autowired
526    public void setReasoner(AbstractReasonerComponent reasoner) {
527        this.reasoner = reasoner;
528        
529        baseURI = reasoner.getBaseURI();
530                prefixes = reasoner.getPrefixes();
531    }
532    
533    @Override
534        public void stop() {
535                stop = true;
536        }
537        
538        @Override
539        public boolean isRunning() {
540                return isRunning;
541        }
542        
543        public Set<OWLClass> getAllowedConcepts() {
544                return allowedConcepts;
545        }
546
547        public void setAllowedConcepts(Set<OWLClass> allowedConcepts) {
548                this.allowedConcepts = allowedConcepts;
549        }
550
551        public Set<OWLClass> getIgnoredConcepts() {
552                return ignoredConcepts;
553        }
554
555        public void setIgnoredConcepts(Set<OWLClass> ignoredConcepts) {
556                this.ignoredConcepts = ignoredConcepts;
557        }
558        
559        /**
560         * @param allowedObjectProperties the allowed object properties to set
561         */
562        public void setAllowedObjectProperties(Set<OWLObjectProperty> allowedObjectProperties) {
563                this.allowedObjectProperties = allowedObjectProperties;
564        }
565        
566        /**
567         * @return the allowed object properties
568         */
569        public Set<OWLObjectProperty> getAllowedObjectProperties() {
570                return allowedObjectProperties;
571        }
572        
573        /**
574         * @param ignoredObjectProperties the ignored object properties to set
575         */
576        public void setIgnoredObjectProperties(Set<OWLObjectProperty> ignoredObjectProperties) {
577                this.ignoredObjectProperties = ignoredObjectProperties;
578        }
579        
580        /**
581         * @return the ignored object properties
582         */
583        public Set<OWLObjectProperty> getIgnoredObjectProperties() {
584                return ignoredObjectProperties;
585        }
586        
587        /**
588         * @param allowedDataProperties the allowed data properties to set
589         */
590        public void setAllowedDataProperties(Set<OWLDataProperty> allowedDataProperties) {
591                this.allowedDataProperties = allowedDataProperties;
592        }
593        
594        /**
595         * @return the allowed data properties
596         */
597        public Set<OWLDataProperty> getAllowedDataProperties() {
598                return allowedDataProperties;
599        }
600        
601        /**
602         * @param ignoredDataProperties the ignored data properties to set
603         */
604        public void setIgnoredDataProperties(Set<OWLDataProperty> ignoredDataProperties) {
605                this.ignoredDataProperties = ignoredDataProperties;
606        }
607        
608        /**
609         * @return the ignored data properties
610         */
611        public Set<OWLDataProperty> getIgnoredDataProperties() {
612                return ignoredDataProperties;
613        }
614        
615        public boolean isUseMinimizer() {
616                return useMinimizer;
617        }
618
619        public void setUseMinimizer(boolean useMinimizer) {
620                this.useMinimizer = useMinimizer;
621        }
622        
623        public long getMaxExecutionTimeInSeconds() {
624                return maxExecutionTimeInSeconds;
625        }
626
627        /**
628         * Set the max. execution time in seconds of the algorithm.  It's expected that the
629         * algorithm will terminate gracefully.
630         *
631         * @param maxExecutionTimeInSeconds max. execution time in seconds
632         */
633        public void setMaxExecutionTimeInSeconds(long maxExecutionTimeInSeconds) {
634                this.maxExecutionTimeInSeconds = maxExecutionTimeInSeconds;
635        }
636
637        /**
638         * Set the max. execution time of the algorithm. It's expected that the
639         * algorithm will terminate gracefully.
640         *
641         * @param maxExecutionTime max. execution time
642         * @param timeUnit the time unit
643         */
644        public void setMaxExecutionTime(long maxExecutionTime, TimeUnit timeUnit) {
645                this.maxExecutionTimeInSeconds = timeUnit.toSeconds(maxExecutionTime);
646        }
647        
648        /**
649         * @param renderer the renderer of OWL objects to set
650         */
651        public void setRenderer(OWLObjectRenderer renderer) {
652                this.renderer = renderer;
653        }
654    
655    /**
656         * The goal of this method is to rewrite the class expression CE, to get a more informative one by e.g.
657         * <ul><li>replacing the role fillers in CE with the range of the property, if exists.</li></ul>
658         * @param ce the class expression
659         * @return the modified class expression
660         */
661        protected OWLClassExpression getNiceDescription(OWLClassExpression ce){
662                OWLClassExpression rewrittenClassExpression = ce;
663                if(ce instanceof OWLObjectIntersectionOf){
664                        Set<OWLClassExpression> newOperands = new TreeSet<>(((OWLObjectIntersectionOf) ce).getOperands());
665                        for (OWLClassExpression operand : ((OWLObjectIntersectionOf) ce).getOperands()) {
666                                newOperands.add(getNiceDescription(operand));
667                        }
668                        rewrittenClassExpression = dataFactory.getOWLObjectIntersectionOf(newOperands);
669                } else if(ce instanceof OWLObjectSomeValuesFrom) {
670                        // \exists r.\bot \equiv \bot
671                        OWLObjectProperty property = ((OWLObjectSomeValuesFrom) ce).getProperty().asOWLObjectProperty();
672                        OWLClassExpression filler = ((OWLObjectSomeValuesFrom) ce).getFiller();
673                        if(filler.isOWLThing()) {
674                                OWLClassExpression range = reasoner.getRange(property);
675                                filler = range;
676                        } else if(filler.isAnonymous()){
677                                filler = getNiceDescription(filler);
678                        }
679                        rewrittenClassExpression = dataFactory.getOWLObjectSomeValuesFrom(property, filler);
680                }
681                return rewrittenClassExpression;
682        }
683}