001    /**
002     * Copyright (C) 2007-2011, 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     */
019    
020    package org.dllearner.algorithms.refinement;
021    
022    import java.io.File;
023    import java.text.DecimalFormat;
024    import java.util.Collection;
025    import java.util.Comparator;
026    import java.util.HashMap;
027    import java.util.Iterator;
028    import java.util.LinkedList;
029    import java.util.List;
030    import java.util.Set;
031    import java.util.SortedSet;
032    import java.util.TreeSet;
033    
034    import org.apache.log4j.Level;
035    import org.apache.log4j.Logger;
036    import org.dllearner.core.AbstractCELA;
037    import org.dllearner.core.AbstractLearningProblem;
038    import org.dllearner.core.AbstractReasonerComponent;
039    import org.dllearner.core.configurators.ROLearnerConfigurator;
040    import org.dllearner.core.options.BooleanConfigOption;
041    import org.dllearner.core.options.CommonConfigMappings;
042    import org.dllearner.core.options.CommonConfigOptions;
043    import org.dllearner.core.options.ConfigEntry;
044    import org.dllearner.core.options.ConfigOption;
045    import org.dllearner.core.options.DoubleConfigOption;
046    import org.dllearner.core.options.InvalidConfigOptionValueException;
047    import org.dllearner.core.options.StringConfigOption;
048    import org.dllearner.core.owl.Description;
049    import org.dllearner.core.owl.Intersection;
050    import org.dllearner.core.owl.NamedClass;
051    import org.dllearner.core.owl.ObjectProperty;
052    import org.dllearner.core.owl.Thing;
053    import org.dllearner.core.owl.Union;
054    import org.dllearner.learningproblems.EvaluatedDescriptionPosNeg;
055    import org.dllearner.learningproblems.PosNegLP;
056    import org.dllearner.learningproblems.ScorePosNeg;
057    import org.dllearner.refinementoperators.RhoDown;
058    import org.dllearner.utilities.Files;
059    import org.dllearner.utilities.Helper;
060    import org.dllearner.utilities.owl.ConceptComparator;
061    import org.dllearner.utilities.owl.ConceptTransformation;
062    import org.dllearner.utilities.owl.EvaluatedDescriptionPosNegComparator;
063    
064    public class ROLearner extends AbstractCELA {
065            
066            private ROLearnerConfigurator configurator;
067            @Override
068            public ROLearnerConfigurator getConfigurator(){
069                    return configurator;
070            }
071            
072            private static Logger logger = Logger
073            .getLogger(AbstractCELA.class); 
074            
075            private String logLevel = CommonConfigOptions.logLevelDefault;
076            
077            public enum Heuristic { LEXICOGRAPHIC, FLEXIBLE }
078            
079            // configuration options
080            private boolean writeSearchTree;
081            private File searchTreeFile;
082            private boolean replaceSearchTree = false;
083            private static String defaultSearchTreeFile = "log/searchTree.txt";
084            private Heuristic heuristic = Heuristic.LEXICOGRAPHIC;
085            Set<NamedClass> allowedConcepts;
086            Set<ObjectProperty> allowedRoles;
087            Set<NamedClass> ignoredConcepts;
088            Set<ObjectProperty> ignoredRoles;
089            // these are computed as the result of the previous four settings
090            Set<NamedClass> usedConcepts;
091            Set<ObjectProperty> usedRoles;    
092            private boolean applyAllFilter = true;
093            private boolean applyExistsFilter = true;       
094            private boolean useTooWeakList = true;
095            private boolean useOverlyGeneralList = true;
096            private boolean useShortConceptConstruction = true;
097            private double horizontalExpansionFactor = 0.6;
098            private boolean improveSubsumptionHierarchy = true;
099            private boolean useAllConstructor = CommonConfigOptions.useAllConstructorDefault;
100            private boolean useExistsConstructor = CommonConfigOptions.useExistsConstructorDefault;
101            //this was added so you can switch algorithm without removing everything not applicable
102            @SuppressWarnings("unused")
103            private boolean useCardinalityRestrictions = CommonConfigOptions.useCardinalityRestrictionsDefault;
104            private boolean useNegation = CommonConfigOptions.useNegationDefault;
105            //TODO different standard options to CommonConfigOptions 
106            private boolean useBooleanDatatypes = false;
107            
108            
109            
110            //extended Options
111            private int maxExecutionTimeInSeconds = CommonConfigOptions.maxExecutionTimeInSecondsDefault;
112            private boolean maxExecutionTimeShown = false;
113            private int minExecutionTimeInSeconds = CommonConfigOptions.minExecutionTimeInSecondsDefault;
114            private boolean minExecutionTimeShown = false;
115            private int guaranteeXgoodDescriptions = CommonConfigOptions.guaranteeXgoodDescriptionsDefault;
116            private boolean guaranteeXgoodShown = false;
117            
118            
119            private boolean quiet = false;
120            
121            private boolean stop = false;
122            private boolean isRunning = false;
123            
124            private Comparator<Node> nodeComparator;
125            private NodeComparatorStable nodeComparatorStable = new NodeComparatorStable();
126            private ConceptComparator conceptComparator = new ConceptComparator();
127            // comparator for evaluated descriptions
128            private EvaluatedDescriptionPosNegComparator edComparator = new EvaluatedDescriptionPosNegComparator();
129            DecimalFormat df = new DecimalFormat(); 
130            
131            private PosNegLP learningProblem;
132            
133            // Menge von Kandidaten für Refinement
134            // (wird für Direktzugriff auf Baumknoten verwendet)
135            private TreeSet<Node> candidates;
136            // während eines Durchlaufs neu gefundene Knoten
137            private List<Node> newCandidates = new LinkedList<Node>();
138            // stabiles candidate set, da sich die Knoten nach dem einfügen nicht
139            // verschieben können => das Set enthält nicht die aktuellen horizontal
140            // expansions, es dient nur dazu die besten Konzepte zu speichern; hat also
141            // keine Funktion im Algorithmus
142            private TreeSet<Node> candidatesStable = new TreeSet<Node>(nodeComparatorStable);
143            // vorhandene Konzepte, die irgendwann als proper eingestuft worden
144            private SortedSet<Description> properRefinements = new TreeSet<Description>(conceptComparator);
145            // speichert Konzept und deren Evaluierung, um sie leicht wiederzufinden für
146            // Strategien wie Konzeptverkürzung etc.
147            // Zahl = covered negatives, -1 = too weak
148            // private Map<Concept, Integer> evaluationCache = new TreeMap<Concept, Integer>(conceptComparator);
149            // Blacklists
150            private SortedSet<Description> tooWeakList = new TreeSet<Description>(conceptComparator);
151            private SortedSet<Description> overlyGeneralList = new TreeSet<Description>(conceptComparator);
152            
153            // Lösungen protokollieren
154            boolean solutionFound = false;
155            List<Description> solutions = new LinkedList<Description>();        
156            
157            // verwendeter Refinement-Operator (momentan werden für Statistik RhoDown-spezifische
158            // Sachen abgefragt)
159            // RefinementOperator operator;
160            RhoDown operator;
161            
162            // Variablen zur Einstellung der Protokollierung
163            // boolean quiet = false;
164            boolean showBenchmarkInformation = false;
165            // the previous best node (used only for logging, such that we can
166            // detect whether a new best node has been found since the last time
167            // statistics were printed)
168            private Node previousBestNode;
169            
170            // record start node such that other applications can
171            // get information about the search tree
172            private Node startNode;
173            
174            // boolean createTreeString = false;
175            // String searchTree = new String();
176            TreeSet<Node> expandedNodes = new TreeSet<Node>(nodeComparatorStable);
177            
178            // Konfiguration des Algorithmus
179            // Faktor für horizontale Erweiterung (notwendig für completeness)
180            // double horizontalExpansionFactor = 0.6;      
181    
182            // statistische Variablen
183            private int maxRecDepth = 0;
184            private int maxNrOfRefinements = 0;
185            private int maxNrOfChildren = 0;
186            private int redundantConcepts = 0;
187            int maximumHorizontalExpansion;
188            int minimumHorizontalExpansion;
189            // private int propernessTests = 0;
190            private int propernessTestsReasoner = 0;
191            private int propernessTestsAvoidedByShortConceptConstruction = 0;
192            private int propernessTestsAvoidedByTooWeakList = 0;
193            private int conceptTestsTooWeakList = 0;
194            private int conceptTestsOverlyGeneralList = 0;
195            private int conceptTestsReasoner = 0;
196            
197            // Zeitvariablen
198            private long runtime;
199            private long algorithmStartTime;
200            private long propernessCalcTimeNs = 0;
201            private long propernessCalcReasoningTimeNs = 0; 
202            private long childConceptsDeletionTimeNs = 0;
203            private long refinementCalcTimeNs = 0;
204            private long redundancyCheckTimeNs = 0;
205            private long evaluateSetCreationTimeNs = 0;
206            private long improperConceptsRemovalTimeNs = 0;
207            long someTimeNs = 0;
208            int someCount = 0;
209            
210            // prefixes
211            private String baseURI;
212    
213            public ROLearner(PosNegLP learningProblem, AbstractReasonerComponent reasoningService) {
214                    super(learningProblem, reasoningService);
215                    this.learningProblem = learningProblem;
216                    this.configurator =  new ROLearnerConfigurator(this);
217                    baseURI = reasoningService.getBaseURI();
218                    
219            }
220            
221            public static Collection<Class<? extends AbstractLearningProblem>> supportedLearningProblems() {
222                    Collection<Class<? extends AbstractLearningProblem>> problems = new LinkedList<Class<? extends AbstractLearningProblem>>();
223                    problems.add(PosNegLP.class);
224                    return problems;
225            }
226            
227            public static Collection<ConfigOption<?>> createConfigOptions() {
228                    Collection<ConfigOption<?>> options = new LinkedList<ConfigOption<?>>();
229                    options.add(new BooleanConfigOption("writeSearchTree", "specifies whether to write a search tree", false));
230                    options.add(new StringConfigOption("searchTreeFile","file to use for the search tree", defaultSearchTreeFile));
231                    options.add(new BooleanConfigOption("replaceSearchTree","specifies whether to replace the search tree in the log file after each run or append the new search tree", false));
232                    StringConfigOption heuristicOption = new StringConfigOption("heuristic", "specifiy the heuristic to use", "lexicographic");
233                    heuristicOption.setAllowedValues(new String[] {"lexicographic", "flexible"});
234                    options.add(heuristicOption);
235                    options.add(new BooleanConfigOption("applyAllFilter", "usage of equivalence ALL R.C AND ALL R.D = ALL R.(C AND D)", true));
236                    options.add(new BooleanConfigOption("applyExistsFilter", "usage of equivalence EXISTS R.C OR EXISTS R.D = EXISTS R.(C OR D)", true));
237                    options.add(new BooleanConfigOption("useTooWeakList", "try to filter out too weak concepts without sending them to the reasoner", true));
238                    options.add(new BooleanConfigOption("useOverlyGeneralList", "try to find overly general concept without sending them to the reasoner", true));
239                    options.add(new BooleanConfigOption("useShortConceptConstruction", "shorten concept to see whether they already exist", true));
240                    DoubleConfigOption horizExp = new DoubleConfigOption("horizontalExpansionFactor", "horizontal expansion factor (see publication for description)", 0.6);
241                    horizExp.setLowerLimit(0.0);
242                    horizExp.setUpperLimit(1.0);
243                    options.add(horizExp);
244                    options.add(new BooleanConfigOption("improveSubsumptionHierarchy", "simplify subsumption hierarchy to reduce search space (see publication for description)", true));
245                    // TODO: replace by a general verbosity option for all components
246                    options.add(new BooleanConfigOption("quiet", "may be deprecated soon", false));
247                    // allowed/ignored concepts/roles could also be a reasoner option (?)
248                    options.add(CommonConfigOptions.allowedConcepts());
249                    options.add(CommonConfigOptions.ignoredConcepts());
250                    options.add(CommonConfigOptions.allowedRoles());
251                    options.add(CommonConfigOptions.ignoredRoles());
252                    options.add(CommonConfigOptions.useAllConstructor());
253                    options.add(CommonConfigOptions.useExistsConstructor());
254                    options.add(CommonConfigOptions.useNegation()); 
255                    options.add(CommonConfigOptions.useCardinalityRestrictions());  
256                    options.add(CommonConfigOptions.useBooleanDatatypes());
257                    options.add(CommonConfigOptions.maxExecutionTimeInSeconds());
258                    options.add(CommonConfigOptions.minExecutionTimeInSeconds());
259                    options.add(CommonConfigOptions.guaranteeXgoodDescriptions());
260                    options.add(CommonConfigOptions.getLogLevel());
261                    options.add(CommonConfigOptions.getInstanceBasedDisjoints());
262                    return options;
263            }
264            
265            /* (non-Javadoc)
266             * @see org.dllearner.core.Component#applyConfigEntry(org.dllearner.core.ConfigEntry)
267             */
268            @Override
269            @SuppressWarnings({"unchecked"})
270            public <T> void applyConfigEntry(ConfigEntry<T> entry) throws InvalidConfigOptionValueException {
271                    String name = entry.getOptionName();
272                    if(name.equals("writeSearchTree"))
273                            writeSearchTree = (Boolean) entry.getValue();
274                    else if(name.equals("searchTreeFile"))
275                            searchTreeFile = new File((String)entry.getValue());
276                    else if(name.equals("replaceSearchTree"))
277                            replaceSearchTree = (Boolean) entry.getValue();
278                    else if(name.equals("heuristic")) {
279                            String value = (String) entry.getValue();
280                            if(value.equals("lexicographic"))
281                                    heuristic = Heuristic.LEXICOGRAPHIC;
282                            else
283                                    heuristic = Heuristic.FLEXIBLE;
284                    } else if(name.equals("allowedConcepts")) {
285                            allowedConcepts = CommonConfigMappings.getAtomicConceptSet((Set<String>)entry.getValue());
286                    } else if(name.equals("allowedRoles")) {
287                            allowedRoles = CommonConfigMappings.getAtomicRoleSet((Set<String>)entry.getValue());
288                    } else if(name.equals("ignoredConcepts")) {
289                            ignoredConcepts = CommonConfigMappings.getAtomicConceptSet((Set<String>)entry.getValue());
290                    } else if(name.equals("ignoredRoles")) {
291                            ignoredRoles = CommonConfigMappings.getAtomicRoleSet((Set<String>)entry.getValue());
292                    } else if(name.equals("applyAllFilter")) {
293                            applyAllFilter = (Boolean) entry.getValue();
294                    } else if(name.equals("applyExistsFilter")) {
295                            applyExistsFilter = (Boolean) entry.getValue();
296                    } else if(name.equals("useTooWeakList")) {
297                            useTooWeakList = (Boolean) entry.getValue();
298                    } else if(name.equals("useOverlyGeneralList")) {
299                            useOverlyGeneralList = (Boolean) entry.getValue();
300                    } else if(name.equals("useShortConceptConstruction")) {
301                            useShortConceptConstruction = (Boolean) entry.getValue();
302                    } else if(name.equals("horzontalExpansionFactor")) {
303                            horizontalExpansionFactor = (Double) entry.getValue();
304                    } else if(name.equals("improveSubsumptionHierarchy")) {
305                            improveSubsumptionHierarchy = (Boolean) entry.getValue();
306                    } else if(name.equals("useAllConstructor")) {
307                            useAllConstructor = (Boolean) entry.getValue();
308                    } else if(name.equals("useExistsConstructor")) {
309                            useExistsConstructor = (Boolean) entry.getValue();
310                    }else if(name.equals("useCardinalityRestrictions")) {
311                                    useCardinalityRestrictions = (Boolean) entry.getValue();
312                    } else if(name.equals("useNegation")) {
313                            useNegation = (Boolean) entry.getValue();
314                    } else if(name.equals("useBooleanDatatypes")) {
315                            useBooleanDatatypes = (Boolean) entry.getValue();
316                    }else if(name.equals("maxExecutionTimeInSeconds")) {
317                            maxExecutionTimeInSeconds = (Integer) entry.getValue();
318                    }else if(name.equals("minExecutionTimeInSeconds")) {
319                            minExecutionTimeInSeconds = (Integer) entry.getValue();
320                    }else if(name.equals("guaranteeXgoodDescriptions")) {
321                            guaranteeXgoodDescriptions =  (Integer) entry.getValue();
322                    } else if(name.equals("logLevel")) {
323                            logLevel = ((String)entry.getValue()).toUpperCase();
324                    }
325                            
326            }
327    
328            /* (non-Javadoc)
329             * @see org.dllearner.core.Component#init()
330             */
331            @Override
332            public void init() {
333                    // set log level if the option has been set
334                    if(!logLevel.equals(CommonConfigOptions.logLevelDefault))               
335                            logger.setLevel(Level.toLevel(logLevel,Level.toLevel(CommonConfigOptions.logLevelDefault)));
336                    
337                    if(searchTreeFile == null)
338                            searchTreeFile = new File(defaultSearchTreeFile);
339    
340                    if(writeSearchTree)
341                            Files.clearFile(searchTreeFile);
342                    
343                    // adjust heuristic
344                    if(heuristic == Heuristic.LEXICOGRAPHIC) {
345                            nodeComparator = new NodeComparator();
346                    } else {
347                            nodeComparator = new NodeComparator2(learningProblem.getNegativeExamples().size(), learningProblem.getPercentPerLengthUnit());
348                    }
349                    
350                    // this.learningProblem2 = learningProblem2;
351                    operator = new RhoDown(reasoner, applyAllFilter, applyExistsFilter, useAllConstructor, useExistsConstructor, useNegation, useBooleanDatatypes);
352                    
353                    // candidate sets entsprechend der gewählten Heuristik initialisieren
354                    candidates = new TreeSet<Node>(nodeComparator);
355                    // newCandidates = new TreeSet<Node>(nodeComparator);
356                    
357                    if(allowedConcepts != null) {
358                            // sanity check to control if no non-existing concepts are in the list
359                            Helper.checkConcepts(reasoner, allowedConcepts);
360                            usedConcepts = allowedConcepts;
361                    } else if(ignoredConcepts != null) {
362                            usedConcepts = Helper.computeConceptsUsingIgnoreList(reasoner, ignoredConcepts);
363                    } else {
364                            usedConcepts = Helper.computeConcepts(reasoner);
365                    }
366                    
367                    if(allowedRoles != null) {
368                            Helper.checkRoles(reasoner, allowedRoles);
369                            usedRoles = allowedRoles;
370                    } else if(ignoredRoles != null) {
371                            Helper.checkRoles(reasoner, ignoredRoles);
372                            usedRoles = Helper.difference(reasoner.getObjectProperties(), ignoredRoles);
373                    } else {
374                            usedRoles = reasoner.getObjectProperties();
375                    }
376                    
377                    // prepare subsumption and role hierarchies, because they are needed
378                    // during the run of the algorithm
379    //              reasoner.prepareSubsumptionHierarchy(usedConcepts);
380                    if(improveSubsumptionHierarchy)
381                            reasoner.getClassHierarchy().thinOutSubsumptionHierarchy();
382    //              reasoner.prepareRoleHierarchy(usedRoles);
383            }
384            
385            public static String getName() {
386                    return "refinement operator based learning algorithm";
387            }
388            
389            private int coveredNegativesOrTooWeak(Description concept) {
390                    return learningProblem.coveredNegativeExamplesOrTooWeak(concept);
391            }
392            
393            private int getNumberOfNegatives() {
394                    return learningProblem.getNegativeExamples().size();
395            }
396            
397            // Kernalgorithmus
398            @Override
399            public void start() {
400                    isRunning = true;
401                    runtime=System.currentTimeMillis();
402                    // Suche wird mit Top-Konzept gestartet
403                    Thing top = new Thing();
404                    Node topNode = new Node(top);
405                    // int coveredNegativeExamples = learningProblem.coveredNegativeExamplesOrTooWeak(top);
406                    // aus Top folgen immer alle negativen Beispiele, d.h. es ist nur eine Lösung, wenn
407                    // es keine negativen Beispiele gibt
408                    int coveredNegativeExamples = getNumberOfNegatives();
409                    topNode.setCoveredNegativeExamples(coveredNegativeExamples);
410                    // topNode.setHorizontalExpansion(1); // die 0 ist eigentlich richtig, da keine Refinements
411                    // der Länge 1 untersucht wurden
412                    candidates.add(topNode);
413                    candidatesStable.add(topNode);
414                    startNode = topNode;
415                    // Abbruchvariable => beachten, dass bereits TOP eine Lösung sein kann
416                    solutionFound = (coveredNegativeExamples == 0);
417                    solutions = new LinkedList<Description>();
418                    if(solutionFound)
419                            solutions.add(top);
420                    
421                    int loop = 0;
422    
423                    // Voreinstellung für horizontal expansion
424                    maximumHorizontalExpansion = 0;
425                    minimumHorizontalExpansion = 0;         
426                    
427                    algorithmStartTime = System.nanoTime();
428                    
429                    //second set of lines below, sometimes doesn't go into while, see above
430                    handleStoppingConditions();
431                    
432                    // TODO: effizienter Traversal der Subsumption-Hierarchie
433                    // TODO: Äquivalenzen nutzen
434                    // TODO: Gibt es auch eine andere Abbruchbedingung? Es könnte sein, dass irgendwann keine
435                    // proper refinements mehr gefunden werden, aber wie stelle man das fest?
436                    while(!solutionFound && !stop) {
437                            
438                            if(!quiet) 
439                                    printStatistics(false);                 
440                            
441                            // besten Knoten nach Heuristik auswählen
442                            Node bestNode = candidates.last();
443                            // besten Knoten erweitern                      
444                            // newCandidates = new TreeSet<Node>(nodeComparator);     
445                            newCandidates.clear();
446                            candidates.remove(bestNode);
447                            extendNodeProper(bestNode, bestNode.getHorizontalExpansion()+1);
448                            candidates.add(bestNode);
449                            candidates.addAll(newCandidates);
450                            candidatesStable.addAll(newCandidates);                 
451                            
452                            // minimum horizontal expansion berechnen
453                            if(bestNode.getHorizontalExpansion()>maximumHorizontalExpansion)
454                                    maximumHorizontalExpansion = bestNode.getHorizontalExpansion();
455                            minimumHorizontalExpansion = (int) Math.floor(horizontalExpansionFactor*maximumHorizontalExpansion);
456                            
457                            // neu: es werden solange Knoten erweitert bis wirklich jeder Knoten die
458                            // notwendige minimum horizontal expansion hat
459                            boolean nodesExpanded;
460                            do {
461                                    nodesExpanded = false;
462                                    
463    
464                                    // es darf nicht candidatesStable geklont werden, da diese Menge nicht
465                                    // aktualisiert wird, also die falschen horizontal expansions vorliegen
466                                    // TODO: bei Tests war die Performance der clone-Operation ganz gut, aber
467                                    // es skaliert natürlich nicht so gut mit größer werdenden candidate set
468                                    // => Lösung ist vielleicht einfach einen Iterator zu verwenden und das
469                                    // aktuelle Konzept gleich hier zu löschen (wird dann bei expansion wieder
470                                    // hinzugefügt)
471                                    // TreeSet<Node> candidatesClone = (TreeSet<Node>) candidates.clone();
472                                    newCandidates.clear();
473    
474    
475                                    // for(Node candidate : candidatesClone) {
476                                    Iterator<Node> it = candidates.iterator();
477                                    List<Node> changedNodes = new LinkedList<Node>();
478                                     while(it.hasNext()){
479                                            Node candidate = it.next();
480                                            // alle Kandidaten, die nicht too weak sind und unter minimumHorizontalExpansion
481                                            // liegen, werden erweitert
482                                            if(!candidate.isTooWeak() && candidate.getHorizontalExpansion()<minimumHorizontalExpansion) {
483                                                    // Vorsicht, falls candidates irgendwann in extendProper benutzt
484                                                    // werden sollten! Es könnten auf diese Weise Knoten fehlen 
485                                                    // (momentan wird candidates nur zur Auswahl des besten Knotens
486                                                    // benutzt).
487                                                    it.remove();
488                                                    
489                                                    extendNodeProper(candidate, minimumHorizontalExpansion);
490                                                    nodesExpanded = true;
491    
492                                                    changedNodes.add(candidate);
493                                            }
494                                    }
495                                     
496                                    long someTimeStart = System.nanoTime();
497                                    someCount++;                             
498                                    // geänderte temporär entfernte Knoten wieder hinzufügen
499                                    candidates.addAll(changedNodes);
500                                    // neu gefundene Knoten hinzufügen
501                                    candidates.addAll(newCandidates);
502                                    candidatesStable.addAll(newCandidates);
503                                    someTimeNs += System.nanoTime() - someTimeStart;
504    
505                            } while(nodesExpanded && !stop);
506                            
507                            //System.out.println("candidate set:");
508                            //for(Node n : candidates) {
509                            //      System.out.println(n);
510                            //}
511                            
512                            if(writeSearchTree) {
513                                    // String treeString = "";
514                                    String treeString = "best expanded node: " + bestNode+ "\n";
515                                    if(expandedNodes.size()>1) {
516                                            treeString += "all expanded nodes:\n"; // due to minimum horizontal expansion:\n";
517                                            for(Node n : expandedNodes) {
518                                                    treeString += "   " + n + "\n";
519                                            }
520                                    }
521                                    expandedNodes.clear();
522                                    treeString += "horizontal expansion: " + minimumHorizontalExpansion + " to " + maximumHorizontalExpansion + "\n";
523                                    treeString += topNode.getTreeString();
524                                    treeString += "\n";
525                                    // System.out.println(treeString);
526                                    // searchTree += treeString + "\n";
527                                    // TODO: ev. immer nur einen search tree speichern und den an die
528                                    // Datei anhängen => spart Speicher
529                                    if(replaceSearchTree)
530                                            Files.createFile(searchTreeFile, treeString);
531                                    else
532                                            Files.appendFile(searchTreeFile, treeString);
533                            }//write search tree
534                            
535                            // Anzahl Schleifendurchläufe
536                            loop++;
537                            
538                            if(!quiet)
539                                    logger.debug("--- loop " + loop + " finished ---");     
540                            
541                            handleStoppingConditions();
542                            
543                    }//end while
544                    
545                    
546                    
547                    
548                    // Suchbaum in Datei schreiben
549    //              if(writeSearchTree)
550    //                      Files.createFile(searchTreeFile, searchTree);
551                    
552                    // Ergebnisausgabe
553                    /*
554                    System.out.println("candidate set:");
555                    for(Node n : candidates) {
556                            System.out.println(n);
557                    }*/
558                    
559                    // Set<Concept> solutionsSorted = new TreeSet(conceptComparator);
560                    // solutionsSorted.addAll(solutions);
561                    
562                    // System.out.println("retrievals:");
563                    // for(Concept c : ReasonerComponent.retrievals) {
564                    //      System.out.println(c);
565                    // }
566                    
567                    if(solutionFound) {
568                            logger.info("\nsolutions:");
569                            int show=1;
570                            for(Description c : solutions) {
571                                    logger.info(show+": " +c.toString(baseURI,null) + " (length " + c.getLength() +", depth " + c.getDepth() + ")");
572                                    //TODO remove this line maybe
573                                    // watch for String.replace Quick hack
574                                    logger.info("   MANCHESTER: " + 
575                                                    c.toManchesterSyntaxString(baseURI, new HashMap<String,String>()).
576                                                    replace("\"", ""));
577                                    logger.info("   KBSyntax: " + c.toKBSyntaxString());
578                                    if(show>=5){break;}
579                                    show++;
580                            }
581                            
582                    }
583                    logger.info("  horizontal expansion: " + minimumHorizontalExpansion + " to " + maximumHorizontalExpansion);
584                    logger.info("  size of candidate set: " + candidates.size());
585                    
586                    //logger.trace("test");
587                    //logger.trace(solutions.size());
588                    printBestSolutions(0);
589                    printStatistics(true);
590                    
591                    if(stop)
592                            logger.info("Algorithm stopped.");
593                    else
594                            logger.info("Algorithm terminated succesfully.");
595                    
596                    isRunning = false;
597            }
598            
599            // einfache Erweiterung des Knotens (ohne properness)
600            @SuppressWarnings({"unused"})
601            private void extendNodeSimple(Node node, int maxLength) {
602                    
603            }
604            
605            private void extendNodeProper(Node node, int maxLength) {
606                    // Rekursionsanfang ist das Konzept am Knoten selbst; danach wird der Operator
607                    // so lange darauf angewandt bis alle proper refinements bis zu maxLength
608                    // gefunden wurden
609                    long propCalcNsStart = System.nanoTime();
610                    
611                    if(writeSearchTree)
612                            expandedNodes.add(node);
613                    
614                    if(node.getChildren().size()>maxNrOfChildren)
615                            maxNrOfChildren = node.getChildren().size();
616                    
617                    // Knoten in instabiler Menge muss aktualisiert werden
618                    // => wird jetzt schon vom Algorithmus entfernt
619                    /*
620                    boolean remove = candidates.remove(node);
621                    
622                    if(!remove) {
623                            System.out.println(candidates);
624                            System.out.println(candidatesStable);
625                            System.out.println(node);
626                            
627                            throw new RuntimeException("remove failed");
628                    }*/
629                    
630                    extendNodeProper(node, node.getConcept(), maxLength, 0);
631                    node.setHorizontalExpansion(maxLength);
632                    
633                    // wird jetzt schon im Kernalgorithmus hinzugefügt
634                    /*
635                    boolean add = candidates.add(node);
636                    if(!add) {
637                            throw new RuntimeException("add failed");
638                    }*/
639                    
640                    // Knoten wird entfernt und wieder hinzugefügt, da sich seine
641                    // Position geändert haben könnte => geht noch nicht wg. ConcurrentModification
642                    // falls Knoten wg. min. horiz. exp. expandiert werden 
643                    // candidates.remove(node);
644                    // candidates.add(node);
645                    propernessCalcTimeNs += (System.nanoTime()-propCalcNsStart);
646            }
647            
648    
649            
650            // für alle proper refinements von concept bis maxLength werden Kinderknoten
651            // für node erzeugt;
652            // recDepth dient nur zur Protokollierung
653            private void extendNodeProper(Node node, Description concept, int maxLength, int recDepth) {
654                    
655                    // führe Methode nicht aus, wenn Algorithmus gestoppt wurde (alle rekursiven Funktionsaufrufe
656                    // werden nacheinander abgebrochen, so dass ohne weitere Reasoninganfragen relativ schnell beendet wird)
657                    if(stop)
658                            return;
659                    
660                    if(recDepth > maxRecDepth)
661                            maxRecDepth = recDepth;
662                    
663                    // Refinements berechnen => hier dürfen dürfen refinements <= horizontal expansion
664                    // des Konzepts nicht gelöscht werden!
665                    long refinementCalcTimeNsStart = System.nanoTime();
666                    Set<Description> refinements = operator.refine(concept, maxLength, null);
667                    refinementCalcTimeNs += System.nanoTime() - refinementCalcTimeNsStart;
668                    
669                    if(refinements.size()>maxNrOfRefinements)
670                            maxNrOfRefinements = refinements.size();
671                    
672                    long childConceptsDeletionTimeNsStart = System.nanoTime();
673                    // entferne aus den refinements alle Konzepte, die bereits Kinder des Knotens sind
674                    // for(Node n : node.getChildren()) {
675                    //      refinements.remove(n.getConcept());
676                    // }
677                    
678                    // das ist viel schneller, allerdings bekommt man ein anderes candidate set(??)
679                    refinements.removeAll(node.getChildConcepts());
680    
681                    childConceptsDeletionTimeNs += System.nanoTime() - childConceptsDeletionTimeNsStart;
682                    
683                    long evaluateSetCreationTimeNsStart = System.nanoTime();
684                    
685                    // alle Konzepte, die länger als horizontal expansion sind, müssen ausgewertet
686                    // werden
687                    Set<Description> toEvaluateConcepts = new TreeSet<Description>(conceptComparator);
688                    Iterator<Description> it = refinements.iterator();
689                    // for(Concept refinement : refinements) {
690                    while(it.hasNext()) {
691                            Description refinement = it.next();
692                            if(refinement.getLength()>node.getHorizontalExpansion()) {
693                                    // TODO: an dieser Stelle könnte man Algorithmen ansetzen lassen, die
694                                    // versuchen properness-Anfragen zu vermeiden:
695                                    // 1. Konzept kürzen und schauen, ob es Mutterkonzept entspricht
696                                    // 2. Blacklist, die überprüft, ob Konzept too weak ist
697                                    // (dann ist es auch proper)
698                                    
699                                    // sagt aus, ob festgestellt wurde, ob refinement proper ist
700                                    // (sagt nicht aus, dass das refinement proper ist!)
701                                    boolean propernessDetected = false;
702                                    
703                                    // 1. short concept construction
704                                    if(useShortConceptConstruction) {
705                                            // kurzes Konzept konstruieren
706                                            Description shortConcept = ConceptTransformation.getShortConcept(refinement, conceptComparator);
707                                            int n = conceptComparator.compare(shortConcept, concept);
708                                            
709                                            // Konzepte sind gleich also Refinement improper
710                                            if(n==0) {
711                                                    propernessTestsAvoidedByShortConceptConstruction++;
712                                                    propernessDetected = true;
713                                            }
714                                    }
715                                    
716                                    // 2. too weak test
717                                    if(!propernessDetected && useTooWeakList) {
718                                            if(refinement instanceof Intersection) {
719                                                    boolean tooWeakElement = containsTooWeakElement((Intersection)refinement);
720                                                    if(tooWeakElement) {
721                                                            propernessTestsAvoidedByTooWeakList++;
722                                                            conceptTestsTooWeakList++;
723                                                            propernessDetected = true;
724                                                            tooWeakList.add(refinement);
725                                                            
726                                                            // Knoten wird direkt erzeugt (es ist buganfällig zwei Plätze
727                                                            // zu haben, an denen Knoten erzeugt werden, aber es erscheint
728                                                            // hier am sinnvollsten)
729                                                            properRefinements.add(refinement);
730                                                            tooWeakList.add(refinement);
731                                                            
732                                                            Node newNode = new Node(refinement);
733                                                            newNode.setHorizontalExpansion(refinement.getLength()-1);
734                                                            newNode.setTooWeak(true);
735                                                            newNode.setQualityEvaluationMethod(Node.QualityEvaluationMethod.TOO_WEAK_LIST);
736                                                            node.addChild(newNode);
737                                                            
738                                                            // Refinement muss gelöscht werden, da es proper ist
739                                                            it.remove();
740                                                    }
741                                            }
742                                    }
743                                    
744                                    // properness konnte nicht vorher ermittelt werden
745                                    if(!propernessDetected)
746                                            toEvaluateConcepts.add(refinement);
747                                    
748                                            
749                            }
750                    }
751                    evaluateSetCreationTimeNs += System.nanoTime() - evaluateSetCreationTimeNsStart;
752                    
753                    // System.out.println(toEvaluateConcepts.size());
754                    
755                    Set<Description> improperConcepts = null;
756                    if(toEvaluateConcepts.size()>0) {
757                            // Test aller Konzepte auf properness (mit DIG in nur einer Anfrage)
758                            long propCalcReasoningStart = System.nanoTime();
759                            improperConcepts = reasoner.isSuperClassOf(toEvaluateConcepts, concept);
760                            propernessTestsReasoner+=toEvaluateConcepts.size();
761                            // boolean isProper = !learningProblem.getReasonerComponent().subsumes(refinement, concept);
762                            propernessCalcReasoningTimeNs += System.nanoTime() - propCalcReasoningStart;
763                    }
764    
765                    long improperConceptsRemovalTimeNsStart = System.nanoTime();
766                    // die improper Konzepte werden von den auszuwertenden gelöscht, d.h.
767                    // alle proper concepts bleiben übrig (einfache Umbenennung)
768                    if(improperConcepts != null)
769                            toEvaluateConcepts.removeAll(improperConcepts);
770                    Set<Description> properConcepts = toEvaluateConcepts;
771                    // alle proper concepts von refinements löschen
772                    refinements.removeAll(properConcepts);
773                    improperConceptsRemovalTimeNs += System.nanoTime() - improperConceptsRemovalTimeNsStart;
774                    
775                    for(Description refinement : properConcepts) {
776                            long redundancyCheckTimeNsStart = System.nanoTime();
777                            boolean nonRedundant = properRefinements.add(refinement);
778                            redundancyCheckTimeNs += System.nanoTime() - redundancyCheckTimeNsStart;
779                            
780                            if(!nonRedundant)
781                                    redundantConcepts++;
782                            
783                            // es wird nur ein neuer Knoten erzeugt, falls das Konzept nicht
784                            // schon existiert
785                            if(nonRedundant) {
786                            
787                                    // Knoten erzeugen
788                                    Node newNode = new Node(refinement);
789                                    // die -1 ist wichtig, da sonst keine gleich langen Refinements 
790                                    // für den neuen Knoten erlaubt wären z.B. person => male
791                                    newNode.setHorizontalExpansion(refinement.getLength()-1);
792    
793                                    
794                                    // hier finden Tests statt, die Retrieval-Anfrage vermeiden sollen
795                                    /*
796                                    Integer n = evaluationCache.get(concept);
797                                    // Konzept gefunden
798                                    if(n!=null) {
799                                            // Knoten erzeugen
800                                            Node newNode = new Node(refinement);
801                                            newNode.setHorizontalExpansion(refinement.getLength()-1);
802                                            node.addChild(newNode);
803                                            
804                                            // too weak
805                                            if(n==-1) {
806                                                    newNode.setTooWeak(true);
807                                            // nicht too weak
808                                            } else {
809                                                    // feststellen, ob proper => geht so nicht
810                                                    // gleiche covered negatives bedeutet nicht improper
811                                                    boolean proper = (n==node.getCoveredNegativeExamples());
812                                                    newNode.setCoveredNegativeExamples(n);
813                                                    
814                                            }
815                                    // Konzept nicht gefunden => muss ausgewertet werden
816                                    } else {
817                                            toEvaluateConcepts.add(refinement);
818                                    }
819                                    */
820                                    
821                                    boolean qualityKnown = false;
822                                    int quality = -2;
823                                    
824                                    // overly general list verwenden
825                                    if(useOverlyGeneralList && refinement instanceof Union) {
826                                            if(containsOverlyGeneralElement((Union)refinement)) {
827                                                    conceptTestsOverlyGeneralList++;
828                                                    quality = getNumberOfNegatives();
829                                                    qualityKnown = true;
830                                                    newNode.setQualityEvaluationMethod(Node.QualityEvaluationMethod.OVERLY_GENERAL_LIST);
831                                            }       
832                                    }
833                                    
834                                    // Qualität des Knotens auswerten
835                                    if(!qualityKnown) {
836                                            long propCalcReasoningStart2 = System.nanoTime();
837                                            conceptTestsReasoner++;
838                                            quality = coveredNegativesOrTooWeak(refinement);
839                                            propernessCalcReasoningTimeNs += System.nanoTime() - propCalcReasoningStart2;
840                                            newNode.setQualityEvaluationMethod(Node.QualityEvaluationMethod.REASONER);
841                                    }
842    
843                                    if(quality == -1) {
844                                            newNode.setTooWeak(true);
845                                            // Blacklist für too weak concepts
846                                            tooWeakList.add(refinement);
847                                    } else {
848                                            // Lösung gefunden
849                                            if(quality == 0) {
850                                                    solutionFound = true;
851                                                    solutions.add(refinement);
852                                            }                       
853                                            
854                                            newNode.setCoveredNegativeExamples(quality);
855                                            newCandidates.add(newNode);
856                                            // candidates.add(newNode);
857                                            // candidatesStable.add(newNode);
858                                    
859                                            
860                                            if(quality == getNumberOfNegatives())
861                                                    overlyGeneralList.add(refinement);
862                                            
863                                            // System.out.print(".");
864                                    }
865                                    
866                                    node.addChild(newNode);
867                            }                       
868                    }
869                    
870                    
871                    /*
872                    Iterator<Concept> it = refinements.iterator();
873                    while(it.hasNext()) {
874                            Concept refinement = it.next();
875                            if(refinement.getLength()>node.getHorizontalExpansion()) {
876                                    // Test auf properness
877                                    long propCalcReasoningStart = System.nanoTime();
878                                    boolean isProper = !learningProblem.getReasonerComponent().subsumes(refinement, concept);
879                                    propernessCalcReasoningTimeNs += System.nanoTime() - propCalcReasoningStart;
880                                    
881                                    if(isProper) {
882                                            long redundancyCheckTimeNsStart = System.nanoTime();
883                                            boolean nonRedundant = properRefinements.add(refinement);
884                                            redundancyCheckTimeNs += System.nanoTime() - redundancyCheckTimeNsStart;
885                                            
886                                            if(!nonRedundant)
887                                                    redundantConcepts++;
888                                            
889                                            // es wird nur ein neuer Knoten erzeugt, falls das Konzept nicht
890                                            // schon existiert
891                                            if(nonRedundant) {
892                                            
893                                                    // Knoten erzeugen
894                                                    Node newNode = new Node(refinement);
895                                                    // die -1 ist wichtig, da sonst keine gleich langen Refinements 
896                                                    // für den neuen Knoten erlaubt wären z.B. person => male
897                                                    newNode.setHorizontalExpansion(refinement.getLength()-1);
898                                                    node.addChild(newNode);
899                                                    
900                                                    // Qualität des Knotens auswerten
901                                                    long propCalcReasoningStart2 = System.nanoTime();
902                                                    int quality = learningProblem.coveredNegativeExamplesOrTooWeak(refinement);
903                                                    propernessCalcReasoningTimeNs += System.nanoTime() - propCalcReasoningStart2;
904                                                    
905                                                    if(quality == -1) {
906                                                            newNode.setTooWeak(true);
907                                                    } else {
908                                                            // Lösung gefunden
909                                                            if(quality == 0) {
910                                                                    solutionFound = true;
911                                                                    solutions.add(refinement);
912                                                            }                       
913                                                            
914                                                            newNode.setCoveredNegativeExamples(quality);
915                                                            newCandidates.add(newNode);
916                                                            
917                                                            // System.out.print(".");
918                                                    }
919                                            }
920                                            
921                                            // jedes proper Refinement wird gelöscht
922                                            it.remove();
923    
924                                    }
925                            }
926                    }
927                    */
928                    
929                    
930                    
931                    // es sind jetzt noch alle Konzepte übrig, die improper refinements sind
932                    // auf jedem dieser Konzepte wird die Funktion erneut aufgerufen, da sich
933                    // proper refinements ergeben könnten
934                    for(Description refinement : refinements) {
935                            // for(int i=0; i<=recDepth; i++)
936                            //      System.out.print("  ");
937                            // System.out.println("call: " + refinement + " [maxLength " + maxLength + "]");
938                            extendNodeProper(node, refinement, maxLength, recDepth+1);
939                            // for(int i=0; i<=recDepth; i++)
940                            //      System.out.print("  ");
941                            // System.out.println("finished: " + refinement + " [maxLength " + maxLength + "]");                    
942                    }
943            }
944            
945            private void printStatistics(boolean finalStats) {
946                    // TODO: viele Tests haben ergeben, dass man nie 100% mit der Zeitmessung abdecken
947                    // kann (zum einen weil Stringausgabe verzögert erfolgt und zum anderen weil 
948                    // Funktionsaufrufe, garbage collection, Zeitmessung selbst auch Zeit benötigt); 
949                    // es empfiehlt sich folgendes Vorgehen:
950                    // - Messung der Zeit eines Loops im Algorithmus
951                    // - Messung der Zeit für alle node extensions innerhalb eines Loops
952                    // => als Normalisierungsbasis empfehlen sich dann die Loopzeit statt
953                    // Algorithmuslaufzeit
954                    // ... momentan kann es aber auch erstmal so lassen
955    
956                    long algorithmRuntime = System.nanoTime() - algorithmStartTime;
957                    
958                    if(!finalStats) {
959                            Node bestNode = candidatesStable.last();
960                            boolean newBestNodeFound = false;
961                            if(!bestNode.equals(previousBestNode)) {
962                                    newBestNodeFound = true;
963                                    previousBestNode = bestNode;
964                            }
965                            if(newBestNodeFound)
966                                    logger.info("currently best node: " + bestNode);
967                            
968                            // Refinementoperator auf Konzept anwenden
969                            String bestNodeString = "currently best node: " + candidatesStable.last();
970                            // searchTree += bestNodeString + "\n";
971                            if(!newBestNodeFound)
972                                    logger.debug(bestNodeString);
973                            String expandedNodeString = "next expanded node: " + candidates.last();
974                            // searchTree += expandedNodeString + "\n";
975                            logger.debug(expandedNodeString);               
976                            logger.debug("algorithm runtime " + Helper.prettyPrintNanoSeconds(algorithmRuntime));
977                            String expansionString = "horizontal expansion: " + minimumHorizontalExpansion + " to " + maximumHorizontalExpansion;
978                            // searchTree += expansionString + "\n";
979                            logger.debug(expansionString);
980                            logger.debug("size of candidate set: " + candidates.size());
981                            // System.out.println("properness max recursion depth: " + maxRecDepth);
982                            // System.out.println("max. number of one-step refinements: " + maxNrOfRefinements);
983                            // System.out.println("max. number of children of a node: " + maxNrOfChildren);
984                            logger.debug("subsumption time: " + Helper.prettyPrintNanoSeconds(reasoner.getSubsumptionReasoningTimeNs()));
985                            logger.debug("instance check time: " + Helper.prettyPrintNanoSeconds(reasoner.getInstanceCheckReasoningTimeNs()));                                      
986                    }
987                    
988                    if(showBenchmarkInformation) {
989                            
990    
991                            long reasoningTime = reasoner.getOverallReasoningTimeNs();
992                            double reasoningPercentage = 100 * reasoningTime/(double)algorithmRuntime;
993                            long propWithoutReasoning = propernessCalcTimeNs-propernessCalcReasoningTimeNs;
994                            double propPercentage = 100 * propWithoutReasoning/(double)algorithmRuntime;
995                            double deletionPercentage = 100 * childConceptsDeletionTimeNs/(double)algorithmRuntime;
996                            long subTime = reasoner.getSubsumptionReasoningTimeNs();
997                            double subPercentage = 100 * subTime/(double)algorithmRuntime;
998                            double refinementPercentage = 100 * refinementCalcTimeNs/(double)algorithmRuntime;
999                            double redundancyCheckPercentage = 100 * redundancyCheckTimeNs/(double)algorithmRuntime;
1000                            double evaluateSetCreationTimePercentage = 100 * evaluateSetCreationTimeNs/(double)algorithmRuntime;
1001                            double improperConceptsRemovalTimePercentage = 100 * improperConceptsRemovalTimeNs/(double)algorithmRuntime;
1002                            double mComputationTimePercentage = 100 * operator.mComputationTimeNs/(double)algorithmRuntime;
1003                            double topComputationTimePercentage = 100 * operator.topComputationTimeNs/(double)algorithmRuntime;
1004                            double cleanTimePercentage = 100 * ConceptTransformation.cleaningTimeNs/(double)algorithmRuntime;
1005                            double onnfTimePercentage = 100 * ConceptTransformation.onnfTimeNs/(double)algorithmRuntime;
1006                            double shorteningTimePercentage = 100 * ConceptTransformation.shorteningTimeNs/(double)algorithmRuntime;
1007                            
1008                            // nur temporär 
1009                            double someTimePercentage = 100 * someTimeNs/(double)algorithmRuntime;                  
1010                            
1011                            System.out.println("reasoning percentage: " + df.format(reasoningPercentage) + "%");
1012                            System.out.println("   subsumption check time: " + df.format(subPercentage) + "%");             
1013                            System.out.println("proper calculation percentage (wo. reasoning): " + df.format(propPercentage) + "%");
1014                            System.out.println("   deletion time percentage: " + df.format(deletionPercentage) + "%");
1015                            System.out.println("   refinement calculation percentage: " + df.format(refinementPercentage) + "%");
1016                            System.out.println("      some time percentage: " + df.format(someTimePercentage) + "% " + Helper.prettyPrintNanoSeconds(someTimeNs) + " " + someCount + " times");
1017                            System.out.println("      m calculation percentage: " + df.format(mComputationTimePercentage) + "%");
1018                            System.out.println("      top calculation percentage: " + df.format(topComputationTimePercentage) + "%");
1019                            System.out.println("   redundancy check percentage: " + df.format(redundancyCheckPercentage) + "%");
1020                            System.out.println("   evaluate set creation time percentage: " + df.format(evaluateSetCreationTimePercentage) + "%");
1021                            System.out.println("   improper concepts removal time percentage: " + df.format(improperConceptsRemovalTimePercentage) + "%");
1022                            System.out.println("clean time percentage: " + df.format(cleanTimePercentage) + "%");
1023                            System.out.println("onnf time percentage: " + df.format(onnfTimePercentage) + "%");
1024                            System.out.println("shortening time percentage: " + df.format(shorteningTimePercentage) + "%");                 
1025                    }
1026                    logger.debug("properness tests (reasoner/short concept/too weak list): " + propernessTestsReasoner + "/" + propernessTestsAvoidedByShortConceptConstruction 
1027                                    + "/" + propernessTestsAvoidedByTooWeakList);
1028                    logger.debug("concept tests (reasoner/too weak list/overly general list/redundant concepts): " + conceptTestsReasoner + "/"
1029                                    + conceptTestsTooWeakList + "/" + conceptTestsOverlyGeneralList + "/" + redundantConcepts);     
1030            }
1031            
1032            private boolean containsTooWeakElement(Intersection mc) {
1033                    for(Description child : mc.getChildren()) {
1034                            if(tooWeakList.contains(child))
1035                                    return true;
1036                    }
1037                    return false;
1038            }
1039            
1040            private boolean containsOverlyGeneralElement(Union md) {
1041                    for(Description child : md.getChildren()) {
1042                            if(overlyGeneralList.contains(child))
1043                                    return true;
1044                    }
1045                    return false;
1046            }       
1047            
1048            @Override
1049            public Description getCurrentlyBestDescription() {
1050                    return candidatesStable.last().getConcept();
1051            }       
1052            
1053            @Override
1054            public EvaluatedDescriptionPosNeg getCurrentlyBestEvaluatedDescription() {
1055                    return new EvaluatedDescriptionPosNeg(candidatesStable.last().getConcept(), getSolutionScore());
1056            }
1057            
1058            @Override
1059            public TreeSet<EvaluatedDescriptionPosNeg> getCurrentlyBestEvaluatedDescriptions() {
1060                    int count = 0;
1061                    SortedSet<Node> rev = candidatesStable.descendingSet();
1062                    TreeSet<EvaluatedDescriptionPosNeg> cbd = new TreeSet<EvaluatedDescriptionPosNeg>(edComparator);
1063                    for(Node eb : rev) {
1064                            cbd.add(new EvaluatedDescriptionPosNeg(eb.getConcept(), getSolutionScore(eb.getConcept())));
1065                            // return a maximum of 200 elements (we need a maximum, because the
1066                            // candidate set can be very large)
1067                            if(count > 200)
1068                                    return cbd;
1069                            count++;
1070                    }
1071                    return cbd; 
1072            }       
1073            
1074            public void printBestSolutions(int nrOfSolutions){
1075                    if(!logLevel.equalsIgnoreCase("TRACE")){return;}
1076                    if(nrOfSolutions==0)nrOfSolutions=solutions.size();
1077                    int i=0;
1078                    for(;i<nrOfSolutions; i++) {
1079                            Description d = solutions.get(i);
1080                            logger.trace("  " + d.toString(baseURI,null) + " (length " + d.getLength() + " " +
1081                                            "" );
1082                    }
1083                            
1084            }
1085    
1086            @Override
1087            public synchronized List<Description> getCurrentlyBestDescriptions(int nrOfSolutions) {
1088                    List<Description> best = new LinkedList<Description>();
1089                    int i=0;
1090                    for(Node n : candidatesStable.descendingSet()) {
1091                            best.add(n.getConcept());
1092                            if(i==nrOfSolutions)
1093                                    return best;
1094                            i++;
1095                    }
1096                    return best;
1097            }
1098            
1099    //      @Override
1100            public ScorePosNeg getSolutionScore() {
1101                    return (ScorePosNeg) learningProblem.computeScore(getCurrentlyBestDescription());
1102            }
1103            
1104            
1105            public ScorePosNeg getSolutionScore(Description d) {
1106                    return (ScorePosNeg) learningProblem.computeScore(d);
1107            }
1108    
1109            @Override
1110            public void stop() {
1111                    stop = true;
1112            }
1113    
1114            /**
1115             * @return the startNode
1116             */
1117            public Node getStartNode() {
1118                    return startNode;
1119            }
1120            
1121            private void handleStoppingConditions(){
1122                    solutionFound = (guaranteeXgoodDescriptions() );
1123                    solutionFound = (minExecutionTimeReached()&& solutionFound);
1124                    if(maxExecutionTimeReached()) { stop();
1125                            if(solutions.size()>0)solutionFound = true;
1126                    }
1127            }
1128    
1129            
1130            private boolean guaranteeXgoodDescriptions(){
1131                    if(guaranteeXgoodShown)return true;
1132                    if(solutions.size()>guaranteeXgoodDescriptions){
1133                            logger.info("Minimum number ("+guaranteeXgoodDescriptions+") of good descriptions reached, stopping now...");
1134                            guaranteeXgoodShown=true;
1135                            return true;}
1136                    else return false;
1137                    
1138            }
1139            
1140            
1141            private boolean maxExecutionTimeReached(){
1142                    if(maxExecutionTimeInSeconds==0)return false;
1143                    if(maxExecutionTimeShown)return true;
1144                    long needed = System.currentTimeMillis()- this.runtime;
1145                    long maxMilliSeconds = maxExecutionTimeInSeconds *1000 ;
1146                    if(maxMilliSeconds<needed){
1147                            logger.info("Maximum time ("+maxExecutionTimeInSeconds+" seconds) reached, stopping now...");
1148                            maxExecutionTimeShown=true;
1149                            return true;}
1150                    else return false;
1151                    
1152            }
1153            
1154            
1155    
1156            /**
1157             * true if minExecutionTime reached
1158             * @return true
1159             */
1160            private boolean minExecutionTimeReached(){
1161                    if(minExecutionTimeShown)return true;
1162                    long needed = System.currentTimeMillis()- this.runtime;
1163                    long minMilliSeconds = minExecutionTimeInSeconds *1000 ;
1164                    if(minMilliSeconds<needed){
1165                            logger.info("Minimum time ("+minExecutionTimeInSeconds+" seconds) reached, stopping when next solution is found");
1166                            minExecutionTimeShown=true;
1167                            return true;}
1168                    else return false;
1169                    
1170            }
1171    
1172            /* (non-Javadoc)
1173             * @see org.dllearner.core.LearningAlgorithm#isRunning()
1174             */
1175            @Override
1176            public boolean isRunning() {
1177                    return isRunning;
1178            }
1179    
1180    }