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.algorithms.qtl;
020
021import com.google.common.collect.BiMap;
022import com.google.common.collect.HashBiMap;
023import com.google.common.collect.Sets;
024import org.apache.jena.rdf.model.Model;
025import org.apache.jena.rdf.model.ModelFactory;
026import com.jamonapi.MonitorFactory;
027import gnu.trove.map.TObjectIntMap;
028import gnu.trove.map.hash.TObjectIntHashMap;
029import org.aksw.jena_sparql_api.core.QueryExecutionFactory;
030import org.dllearner.algorithms.qtl.datastructures.impl.EvaluatedRDFResourceTree;
031import org.dllearner.algorithms.qtl.datastructures.impl.QueryTreeImpl.LiteralNodeConversionStrategy;
032import org.dllearner.algorithms.qtl.datastructures.impl.QueryTreeImpl.LiteralNodeSubsumptionStrategy;
033import org.dllearner.algorithms.qtl.datastructures.impl.RDFResourceTree;
034import org.dllearner.algorithms.qtl.heuristics.QueryTreeHeuristic;
035import org.dllearner.algorithms.qtl.heuristics.QueryTreeHeuristicSimple;
036import org.dllearner.algorithms.qtl.impl.QueryTreeFactory;
037import org.dllearner.algorithms.qtl.impl.QueryTreeFactoryBase;
038import org.dllearner.algorithms.qtl.operations.lgg.LGGGenerator;
039import org.dllearner.algorithms.qtl.operations.lgg.LGGGeneratorRDFS;
040import org.dllearner.algorithms.qtl.operations.lgg.LGGGeneratorSimple;
041import org.dllearner.algorithms.qtl.util.Entailment;
042import org.dllearner.core.*;
043import org.dllearner.core.StringRenderer.Rendering;
044import org.dllearner.core.config.ConfigOption;
045import org.dllearner.kb.OWLAPIOntology;
046import org.dllearner.kb.OWLFile;
047import org.dllearner.kb.SparqlEndpointKS;
048import org.dllearner.kb.sparql.ConciseBoundedDescriptionGenerator;
049import org.dllearner.kb.sparql.ConciseBoundedDescriptionGeneratorImpl;
050import org.dllearner.learningproblems.Heuristics;
051import org.dllearner.learningproblems.PosNegLP;
052import org.dllearner.learningproblems.QueryTreeScore;
053import org.semanticweb.owlapi.model.OWLClassExpression;
054import org.semanticweb.owlapi.model.OWLIndividual;
055import org.semanticweb.owlapi.util.SimpleShortFormProvider;
056import org.slf4j.Logger;
057import org.slf4j.LoggerFactory;
058import org.springframework.beans.factory.annotation.Autowired;
059
060import java.io.ByteArrayInputStream;
061import java.io.IOException;
062import java.text.DecimalFormat;
063import java.util.*;
064import java.util.Map.Entry;
065import java.util.concurrent.TimeUnit;
066
067@ComponentAnn(name="query tree learner with noise (disjunctive)", shortName="qtl2dis", version=0.8)
068public class QTL2Disjunctive extends AbstractCELA implements Cloneable{
069        
070        private static final Logger logger = LoggerFactory.getLogger(QTL2Disjunctive.class);
071        private final DecimalFormat dFormat = new DecimalFormat("0.00");
072        
073        private SparqlEndpointKS ks;
074        
075//      private LGGGenerator2 lggGenerator = new LGGGeneratorSimple();
076        private LGGGenerator lggGenerator;
077        
078        private QueryTreeFactory treeFactory;
079        private ConciseBoundedDescriptionGenerator cbdGen;
080        
081        private Queue<EvaluatedRDFResourceTree> todoList;
082        private SortedSet<EvaluatedRDFResourceTree> currentPartialSolutions;
083        
084        private double bestCurrentScore = 0d;
085        
086        private List<RDFResourceTree> currentPosExampleTrees = new ArrayList<>();
087        private List<RDFResourceTree> currentNegExampleTrees = new ArrayList<>();
088        private Set<OWLIndividual> currentPosExamples = new HashSet<>();
089        private Set<OWLIndividual> currentNegExamples = new HashSet<>();
090        
091        private BiMap<RDFResourceTree, OWLIndividual> tree2Individual = HashBiMap.create();
092
093        private PosNegLP lp;
094
095        private Model model;
096
097        private volatile boolean stop;
098        private boolean isRunning;
099
100        private List<EvaluatedRDFResourceTree> partialSolutions;
101        
102        private EvaluatedDescription<? extends Score> currentBestSolution;
103        
104        private QueryTreeHeuristic heuristic;
105        
106        //Parameters
107        @ConfigOption(defaultValue="0.0", description="the (approximated) percentage of noise within the examples")
108        private double noisePercentage = 0.0;
109        
110        private double coverageWeight = 0.8;
111        private double specifityWeight = 0.1;
112        
113        private double minCoveredPosExamplesFraction = 0.2;
114        
115        // maximum execution time to compute a part of the solution
116        private double maxTreeComputationTimeInSeconds = 10;
117        
118        @ConfigOption(defaultValue = "1", description = "how important it is not to cover negatives")
119        private double beta = 1;
120        
121        // minimum score a query tree must have to be part of the solution
122        private double minimumTreeScore = 0.3;
123        
124        // If TRUE the algorithm tries to cover all positive examples. Note that
125        // while this improves accuracy on the testing set,
126        // it may lead to overfitting
127        private boolean tryFullCoverage;
128
129        // algorithm will terminate immediately when a correct definition is found
130        private boolean stopOnFirstDefinition;
131        
132        // the (approximated) value of noise within the examples
133        private double noise = 0.0;
134        
135        private long partialSolutionStartTime;
136        
137        private double startPosExamplesSize;
138        private int expressionTests = 0;
139        
140        // the time needed until the best solution was found
141        private long timeBestSolutionFound = -1;
142        
143        LiteralNodeConversionStrategy[] strategies = new LiteralNodeConversionStrategy[]{
144                        LiteralNodeConversionStrategy.MIN,
145                        LiteralNodeConversionStrategy.MAX,
146                        LiteralNodeConversionStrategy.MIN_MAX,
147                        LiteralNodeConversionStrategy.DATATYPE
148        };
149        private QueryExecutionFactory qef;
150        
151        private Entailment entailment = Entailment.SIMPLE;
152        
153        private int maxTreeDepth = 2;
154        
155        private boolean useDisjunction = false;
156        
157        public QTL2Disjunctive() {}
158        
159        public QTL2Disjunctive(PosNegLP learningProblem, AbstractReasonerComponent reasoner) {
160                super(learningProblem, reasoner);
161                loadModel();
162        }
163        
164        public QTL2Disjunctive(PosNegLP lp, QueryExecutionFactory qef) {
165                super.learningProblem = lp;
166                this.lp = lp;
167                this.qef = qef;
168        }
169        
170        public QTL2Disjunctive(PosNegLP lp, SparqlEndpointKS ks) {
171                this(lp, ks.getQueryExecutionFactory());
172        }
173        
174//      public QTL2Disjunctive(PosNegLP lp, Model model) {
175//              this.learningProblem = lp;
176//              this.model = model;
177//      }
178        
179        /**
180         * Copy constructor.
181         * @param qtl the QTL2Disjunctive instance
182         */
183        public QTL2Disjunctive(QTL2Disjunctive qtl) {
184                super(qtl.getLearningProblem(), qtl.getReasoner());
185                this.model = ModelFactory.createDefaultModel();
186                this.model.add(qtl.model);
187                this.beta = qtl.beta;
188                this.maxExecutionTimeInSeconds = qtl.maxExecutionTimeInSeconds;
189                this.maxTreeComputationTimeInSeconds = qtl.maxTreeComputationTimeInSeconds;
190                this.tryFullCoverage = qtl.tryFullCoverage;
191                this.stopOnFirstDefinition = qtl.stopOnFirstDefinition;
192        }
193        
194        /* (non-Javadoc)
195         * @see org.dllearner.core.Component#init()
196         */
197        @Override
198        public void init() throws ComponentInitException {
199                logger.info("Initializing...");
200                if(!(learningProblem instanceof PosNegLP)){
201                        throw new IllegalArgumentException("Only PosNeg learning problems are supported");
202                }
203                lp = (PosNegLP) learningProblem;
204                
205                // get query execution factory from KS
206                if(qef == null) {
207                        qef = ks.getQueryExecutionFactory();
208                }
209                
210                if(treeFactory == null) {
211                        treeFactory = new QueryTreeFactoryBase();
212                }
213                cbdGen = new ConciseBoundedDescriptionGeneratorImpl(qef);
214
215                // set the used heuristic
216                if(heuristic == null){
217                        heuristic = new QueryTreeHeuristicSimple();
218                        heuristic.setPosExamplesWeight(beta);
219                }
220                
221                if(entailment == Entailment.SIMPLE) {
222                        lggGenerator = new LGGGeneratorSimple();
223                } else if(entailment == Entailment.RDFS){
224                        lggGenerator = new LGGGeneratorRDFS(reasoner);
225                }
226                
227                // generate the query trees
228                generateQueryTrees();
229                
230                startPosExamplesSize = currentPosExampleTrees.size();
231                
232                //console rendering of class expressions
233                StringRenderer.setRenderer(Rendering.MANCHESTER_SYNTAX);
234                StringRenderer.setShortFormProvider(new SimpleShortFormProvider());
235                
236                //compute the LGG for all examples
237                //this allows us to prune all other trees because we can omit paths in trees which are contained in all positive
238                //as well as negative examples
239//              List<RDFResourceTree> allExamplesTrees = new ArrayList<RDFResourceTree>();
240//              allExamplesTrees.addAll(currentPosExampleTrees);
241//              allExamplesTrees.addAll(currentNegExampleTrees);
242//              RDFResourceTree lgg = lggGenerator.getLGG(allExamplesTrees);
243//              lgg.dump();
244                
245                initialized = true;
246                logger.info("...initialization finished.");
247        }
248        
249        /**
250         * @param entailment the entailment to set
251         */
252        public void setEntailment(Entailment entailment) {
253                this.entailment = entailment;
254        }
255        
256        private void generateQueryTrees(){
257                logger.info("Generating trees...");
258                RDFResourceTree queryTree;
259                
260                // positive examples
261                if(currentPosExampleTrees.isEmpty()){
262                        for (OWLIndividual ind : lp.getPositiveExamples()) {
263                                try {
264                                        Model cbd = cbdGen.getConciseBoundedDescription(ind.toStringID(), maxTreeDepth);
265//                                      cbd.write(new FileOutputStream("/tmp/dbpedia-" + ind.toStringID().substring(ind.toStringID().lastIndexOf('/') + 1) + ".ttl"), "TURTLE", null);
266                                        queryTree = treeFactory.getQueryTree(ind.toStringID(), cbd, maxTreeDepth);
267                                        tree2Individual.put(queryTree, ind);
268                                        currentPosExampleTrees.add(queryTree);
269                                        currentPosExamples.add(ind);
270                                        logger.debug(ind.toStringID());
271                                        logger.debug(queryTree.getStringRepresentation());
272                                } catch (Exception e) {
273                                        logger.error("Failed to generate tree for resource " + ind, e);
274                                        throw new RuntimeException();
275                                }
276                        }
277                }
278                
279                // negative examples
280                if(currentNegExampleTrees.isEmpty()){
281                        for (OWLIndividual ind : lp.getNegativeExamples()) {
282                                try {
283                                        Model cbd = cbdGen.getConciseBoundedDescription(ind.toStringID(), maxTreeDepth);
284                                        queryTree = treeFactory.getQueryTree(ind.toStringID(), cbd, maxTreeDepth);
285                                        tree2Individual.put(queryTree, ind);
286                                        currentNegExampleTrees.add(queryTree);
287                                        currentNegExamples.add(ind);
288                                        logger.debug(ind.toStringID());
289                                        logger.debug(queryTree.getStringRepresentation());
290                                } catch (Exception e) {
291                                        logger.error("Failed to generate tree for resource " + ind, e);
292                                        throw new RuntimeException();
293                                }
294                        }
295                }
296                logger.info("...done.");
297        }
298        
299        /* (non-Javadoc)
300         * @see org.dllearner.core.LearningAlgorithm#start()
301         */
302        @Override
303        public void start() {
304
305                if(currentPosExampleTrees.isEmpty()) {
306                        logger.info("No positive examples given!");
307                        return;
308                }
309
310                printSetup();
311                logger.info("Running...");
312                
313                reset();
314                
315                int i = 1;
316                while(!terminationCriteriaSatisfied() && (useDisjunction || i == 1)){
317                        logger.info(i++ + ". iteration...");
318                        logger.info("#Remaining pos. examples:" + currentPosExampleTrees.size());
319                        logger.info("#Remaining neg. examples:" + currentNegExampleTrees.size());
320                        
321                        // compute best (partial) solution computed so far
322                        EvaluatedRDFResourceTree bestPartialSolution = computeBestPartialSolution();
323                        
324                        // add to set of partial solutions if criteria are satisfied
325                        if(bestPartialSolution.getScore() >= minimumTreeScore){
326                                
327                                partialSolutions.add(bestPartialSolution);
328                                
329                                // remove all examples covered by current partial solution
330                                RDFResourceTree tree;
331                                for (Iterator<RDFResourceTree> iterator = currentPosExampleTrees.iterator(); iterator.hasNext();) {
332                                        tree = iterator.next();
333                                        if(!bestPartialSolution.getFalseNegatives().contains(tree)){//a pos tree that is not covered
334                                                iterator.remove();
335                                                currentPosExamples.remove(tree2Individual.get(tree));
336                                        }
337                                }
338                                for (Iterator<RDFResourceTree> iterator = currentNegExampleTrees.iterator(); iterator.hasNext();) {
339                                        tree = iterator.next();
340                                        if(bestPartialSolution.getFalsePositives().contains(tree)){//a neg example that is covered
341                                                iterator.remove();
342                                                currentNegExamples.remove(tree2Individual.get(tree));
343                                        }
344                                }
345                                
346                                // (re)build the current combined solution from all partial solutions
347                                currentBestSolution = buildCombinedSolution();
348                                
349                                logger.info("combined accuracy: " + dFormat.format(currentBestSolution.getAccuracy()));
350                        } else {
351                                String message = "No partial tree found which satisfies the minimal criteria.";
352                                if(currentBestSolution != null) {
353                                        message += "- the best was: "
354                                                        + currentBestSolution.getDescription()
355                                                        + " with score " + currentBestSolution.getScore();
356                                }
357                                logger.info(message);
358                        }
359                        
360                }
361
362                isRunning = false;
363                
364                postProcess();
365                
366                long nanoEndTime = System.nanoTime();
367                logger.info("Finished in " + TimeUnit.NANOSECONDS.toMillis(nanoEndTime - nanoStartTime) + "ms.");
368                logger.info(expressionTests +" descriptions tested");
369                if(currentBestSolution != null) {
370                        logger.info("Combined solution:" + currentBestSolution.getDescription().toString().replace("\n", ""));
371                        logger.info(currentBestSolution.getScore().toString());
372                } else {
373                        logger.info("Could not find a solution in the given time.");
374                }
375        }
376        
377        /**
378         * This method can be called for clean up of the solutions and re-ranking.
379         */
380        private void postProcess() {
381                logger.trace("Post processing ...");
382                // pick solutions with same accuracy, i.e. in the pos only case
383                // covering the same number of positive examples
384                SortedSet<EvaluatedRDFResourceTree> solutions = getSolutions();
385                // pick solutions with accuracy above
386                // mas(maximum achievable score) - noise
387                List<EvaluatedRDFResourceTree> solutionsForPostProcessing = new ArrayList<>();
388                for (EvaluatedRDFResourceTree solution : solutions) {
389                        
390                        double accuracy = solution.getTreeScore().getAccuracy();
391                        
392                        double mas = heuristic.getMaximumAchievableScore(solution);
393                        
394                        double epsilon = 0.01;
395                        
396                        if(accuracy != mas && accuracy >= (mas - noise - epsilon)) {
397                                solutionsForPostProcessing.add(solution);
398                        }
399                }
400                
401                logger.trace("Finished post processing.");
402        }
403        
404        /**
405         * Compute a (partial) solution that covers as much positive examples as possible.
406         * @return a (partial) solution
407         */
408        private EvaluatedRDFResourceTree computeBestPartialSolution(){
409                logger.info("Computing best partial solution...");
410                bestCurrentScore = Double.NEGATIVE_INFINITY;
411                partialSolutionStartTime = System.currentTimeMillis();
412                initTodoList(currentPosExampleTrees, currentNegExampleTrees);
413                
414                EvaluatedRDFResourceTree bestPartialSolutionTree = null;
415                EvaluatedRDFResourceTree currentElement;
416                RDFResourceTree currentTree;
417                
418                // generate id for each pos and neg example tree
419                TObjectIntMap<RDFResourceTree> index = new TObjectIntHashMap<>(this.currentPosExampleTrees.size() + this.currentNegExampleTrees.size());
420                int id = 1;
421                for (RDFResourceTree posTree : currentPosExampleTrees) {
422                        index.put(posTree, id++);
423                }
424                Set<Set<RDFResourceTree>> processedCombinations = new HashSet<>();
425                
426                while(!partialSolutionTerminationCriteriaSatisfied()){
427                        logger.trace("ToDo list size: " + todoList.size());
428                        // pick best element from todo list
429                        currentElement = todoList.poll();
430                        currentTree = currentElement.getTree();
431                        
432                        logger.trace("Next tree: {} ({})", currentElement.getBaseQueryTrees(), currentElement.getTreeScore());
433                        
434                        // generate the LGG between the chosen tree and each false negative resp. uncovered positive example
435                        Collection<RDFResourceTree> falseNegatives = currentElement.getFalseNegatives();
436                        
437                        if(falseNegatives.isEmpty()) { // if the current solution covers already all pos examples
438//                              addToSolutions(bestPartialSolutionTree);
439//                              bestPartialSolutionTree = currentElement;
440                        }
441                        
442                        Iterator<RDFResourceTree> it = falseNegatives.iterator();
443                        while (it.hasNext() && !(useDisjunction && isPartialSolutionTimeExpired()) && !isTimeExpired()) {
444                                RDFResourceTree uncoveredTree = it.next();
445                                logger.trace("Uncovered tree: "  + uncoveredTree);
446                                
447                                // we should avoid the computation of lgg(t2,t1) if we already did lgg(t1,t2)
448                                Set<RDFResourceTree> baseQueryTrees = Sets.newTreeSet(currentElement.getBaseQueryTrees());
449                                baseQueryTrees.add(uncoveredTree);
450//                              String s = "";
451//                              for (RDFResourceTree queryTree : baseQueryTrees) {
452//                                      s += index.get(queryTree) + ",";
453//                              }
454//                              System.err.println(s);
455                                if(!processedCombinations.add(baseQueryTrees)) {
456//                                      System.err.println("skipping");
457//                                      continue;
458                                }
459                                
460                                // compute the LGG
461                                MonitorFactory.getTimeMonitor("lgg").start();
462                                ((LGGGeneratorSimple)lggGenerator).setTimeout(getRemainingPartialSolutionTime(), TimeUnit.SECONDS);
463                                RDFResourceTree lgg = lggGenerator.getLGG(currentTree, uncoveredTree);
464                                MonitorFactory.getTimeMonitor("lgg").stop();
465//                              System.out.println("COMPLETE:" + ((LGGGeneratorSimple)lggGenerator).isComplete());
466//                              logger.info("LGG: "  + lgg.getStringRepresentation());
467                                
468                                // redundancy check
469                                boolean redundant = isRedundant(lgg);
470                                if(redundant) {
471                                        logger.trace("redundant");
472                                        continue;
473                                }
474                                
475                                // evaluate the LGG
476                                Set<EvaluatedRDFResourceTree> solutions = evaluate(lgg, true);
477                                for (EvaluatedRDFResourceTree solution : solutions) {
478                                        solution.setBaseQueryTrees(baseQueryTrees);
479                                        logger.trace("solution: {} ({})", solution.getBaseQueryTrees(), solution.getTreeScore());
480                                        expressionTests++;
481                                        double score = solution.getScore();
482                                        double mas = heuristic.getMaximumAchievableScore(solution);
483                                        
484                                        if(score >= bestCurrentScore){
485                                                if(score > bestCurrentScore){
486                                                        timeBestSolutionFound = getCurrentRuntimeInMilliSeconds();
487                                                        logger.info("\tGot better solution after {}ms:" + solution.getTreeScore(), timeBestSolutionFound);
488                                                        logger.info("\t" + solutionAsString(solution.asEvaluatedDescription()));
489                                                        bestCurrentScore = score;
490                                                        bestPartialSolutionTree = solution;
491                                                }
492                                                // add to ToDo list, if not already contained in ToDo list or solution list
493                                                if(bestCurrentScore == 1.0 || mas > score){
494//                                                      todo(solution);
495                                                }
496                                        } else if(bestCurrentScore == 1.0 || mas >= bestCurrentScore){ // add to ToDo list if max. achievable score is higher
497//                                              todo(solution);
498                                        } else {
499                                                logger.trace("Too weak: {}", solution.getTreeScore());
500//                                              System.err.println(solution.getEvaluatedDescription());
501//                                              System.out.println("Too general");
502//                                              System.out.println("MAS=" + mas + "\nBest=" + bestCurrentScore);
503//                                              todo(solution);
504                                        }
505                                        todo(solution);
506                                        addToSolutions(solution);
507                                }
508                        }
509//                      addToSolutions(currentElement);
510                }
511                
512                long endTime = System.currentTimeMillis();
513                logger.info("...finished computing best partial solution in " + (endTime-partialSolutionStartTime) + "ms.");
514                EvaluatedDescription bestPartialSolution = bestPartialSolutionTree.asEvaluatedDescription();
515                
516                logger.info("Best partial solution: " + solutionAsString(bestPartialSolution) + "\n(" + bestPartialSolution.getScore() + ")");
517                
518                logger.trace("LGG time: " + MonitorFactory.getTimeMonitor("lgg").getTotal() + "ms");
519                logger.trace("Avg. LGG time: " + MonitorFactory.getTimeMonitor("lgg").getAvg() + "ms");
520                logger.info("#LGG computations: " + MonitorFactory.getTimeMonitor("lgg").getHits());
521                
522                logger.trace("Subsumption test time: " + MonitorFactory.getTimeMonitor("subsumption").getTotal() + "ms");
523                logger.trace("Avg. subsumption test time: " + MonitorFactory.getTimeMonitor("subsumption").getAvg() + "ms");
524                logger.trace("#Subsumption tests: " + MonitorFactory.getTimeMonitor("subsumption").getHits());
525                
526                return bestPartialSolutionTree;
527        }
528        
529        private String solutionAsString(EvaluatedDescription ed) {
530                return renderer.render(ed.getDescription()).replace("\n", "").replaceAll("\\\\s{2,}", " ");
531        }
532        
533        private boolean addToSolutions(EvaluatedRDFResourceTree solution) {
534                for (EvaluatedRDFResourceTree partialSolution : currentPartialSolutions) {
535                        if(QueryTreeUtils.sameTrees(partialSolution.getTree(), solution.getTree())) {
536                                return false;
537                        }
538                }
539                return currentPartialSolutions.add(solution);
540        }
541        
542        /**
543         * Initializes the ToDo list with all distinct trees contained in the given list of positive
544         * example trees {@code posExamples} and negative example trees {@code negExamples}.
545         * First, distinct trees are computed and afterwards, for each tree an initial score will be
546         *  computed.
547         * @param posExamples the positive example trees
548         * @param negExamples the negative example trees
549         */
550        private void initTodoList(List<RDFResourceTree> posExamples, List<RDFResourceTree> negExamples){
551                todoList = new PriorityQueue<>();
552                currentPartialSolutions = new TreeSet<>();
553//              EvaluatedRDFResourceTree dummy = new EvaluatedRDFResourceTree(new QueryTreeImpl<String>((N)"TOP"), trees, 0d);
554//              todoList.add(dummy);
555                
556                // compute distinct trees, i.e. check if some of the trees already cover others
557                Collection<RDFResourceTree> distinctTrees = new ArrayList<>();
558                for (RDFResourceTree queryTree : posExamples) {
559                        boolean distinct = true;
560                        for (RDFResourceTree otherTree : distinctTrees) {
561                                if(!queryTree.equals(otherTree)){
562                                        if(QueryTreeUtils.sameTrees(queryTree, otherTree)){
563                                                distinct = false;
564                                                break;
565                                        }
566                                }
567                        }
568                        if(distinct){
569                                distinctTrees.add(queryTree);
570                        }
571                }
572                
573                // compute an initial score
574                for (RDFResourceTree queryTree : distinctTrees) {
575                        EvaluatedRDFResourceTree evaluatedQueryTree = evaluateSimple(queryTree, false);
576                        evaluatedQueryTree.setBaseQueryTrees(Collections.singleton(queryTree));
577                        todoList.add(evaluatedQueryTree);
578                }
579        }
580        
581        /**
582         * @return TRUE if the query tree is already contained in the solutions or
583         * todo list, otherwise FALSE
584         */
585        private boolean isRedundant(RDFResourceTree tree) {
586                //check if not already contained in todo list
587                for (EvaluatedRDFResourceTree evTree : todoList) {
588                        if(QueryTreeUtils.sameTrees(tree, evTree.getTree())){
589                                logger.trace("Not added to TODO list: Already contained in.");
590//                              logger.trace(evTree.getBaseQueryTrees().toString());
591                                return true;
592                        }
593                }
594                
595                //check if not already contained in solutions
596                for (EvaluatedRDFResourceTree evTree : currentPartialSolutions) {
597                        if(QueryTreeUtils.sameTrees(tree, evTree.getTree())){
598                                logger.trace("Not added to partial solutions list: Already contained in.");
599                                return true;
600                        }
601                }
602                return false;
603        }
604        
605        /**
606         * Add tree to ToDo list if not already contained in that list or the solutions.
607         * @param solution the solution
608         */
609        private void todo(EvaluatedRDFResourceTree solution){
610                logger.trace("Added to TODO list.");
611                todoList.add(solution);
612        }
613        
614        private EvaluatedRDFResourceTree evaluateSimple(RDFResourceTree tree, boolean useSpecifity){
615                //1. get a score for the coverage = recall oriented
616                //compute positive examples which are not covered by LGG
617                List<RDFResourceTree> uncoveredPositiveExampleTrees = getUncoveredTrees(tree, currentPosExampleTrees);
618                Set<OWLIndividual> uncoveredPosExamples = new TreeSet<>();
619                for (RDFResourceTree queryTree : uncoveredPositiveExampleTrees) {
620                        uncoveredPosExamples.add(tree2Individual.get(queryTree));
621                }
622                //compute negative examples which are covered by LGG
623                Collection<RDFResourceTree> coveredNegativeExampleTrees = getCoveredTrees(tree, currentNegExampleTrees);
624                Set<OWLIndividual> coveredNegExamples = new TreeSet<>();
625                for (RDFResourceTree queryTree : coveredNegativeExampleTrees) {
626                        coveredNegExamples.add(tree2Individual.get(queryTree));
627                }
628                //compute score
629                int coveredPositiveExamples = currentPosExampleTrees.size() - uncoveredPositiveExampleTrees.size();
630                double recall = coveredPositiveExamples / (double)currentPosExampleTrees.size();
631                double precision = (coveredNegativeExampleTrees.size() + coveredPositiveExamples == 0)
632                                                ? 0
633                                                : coveredPositiveExamples / (double)(coveredPositiveExamples + coveredNegativeExampleTrees.size());
634                
635                double coverageScore = Heuristics.getFScore(recall, precision, beta);
636                
637                //2. get a score for the specifity of the query, i.e. how many edges/nodes = precision oriented
638                int nrOfSpecificNodes = 0;
639                for (RDFResourceTree childNode : QueryTreeUtils.getNodes(tree)) {
640                        if(!childNode.isVarNode()){
641                                nrOfSpecificNodes++;
642                        }
643                }
644                double specifityScore = 0d;
645                if(useSpecifity){
646                        specifityScore = Math.log(nrOfSpecificNodes);
647                } else {
648                        specifityScore = 1 / (double) nrOfSpecificNodes;
649                }
650
651                //3.compute the total score
652                double score = coverageWeight * coverageScore + specifityWeight * specifityScore;
653
654                QueryTreeScore queryTreeScore = new QueryTreeScore(score, coverageScore,
655                                new TreeSet<>(Sets.difference(currentPosExamples, uncoveredPosExamples)), uncoveredPosExamples,
656                                coveredNegExamples, new TreeSet<>(Sets.difference(currentNegExamples, coveredNegExamples)),
657                                specifityScore, nrOfSpecificNodes);
658                
659//              QueryTreeScore queryTreeScore = new QueryTreeScore(score, coverageScore,
660//                              null,null,null,null,
661//                              specifityScore, nrOfSpecificNodes);
662                
663                EvaluatedRDFResourceTree evaluatedTree = new EvaluatedRDFResourceTree(tree, uncoveredPositiveExampleTrees, coveredNegativeExampleTrees, queryTreeScore);
664                
665                //TODO use only the heuristic to compute the score
666                score = heuristic.getScore(evaluatedTree);
667                queryTreeScore.setScore(score);
668                queryTreeScore.setAccuracy(score);
669                
670                return evaluatedTree;
671        }
672        
673        /**
674         * Evaluated a query tree such that it returns a set of evaluated query trees.
675         * A set is returned because there are several ways how to convert literal nodes.
676         * @param tree the query tree
677         * @param useSpecifity whether to use SPECIFITY as measure
678         * @return a set of evaluated query trees
679         */
680        private Set<EvaluatedRDFResourceTree> evaluate(RDFResourceTree tree, boolean useSpecifity){
681                Set<EvaluatedRDFResourceTree> evaluatedTrees = new TreeSet<>();
682                
683                LiteralNodeSubsumptionStrategy[] strategies = LiteralNodeSubsumptionStrategy.values();
684                strategies = new LiteralNodeSubsumptionStrategy[]{
685                                LiteralNodeSubsumptionStrategy.DATATYPE,
686//                              LiteralNodeSubsumptionStrategy.INTERVAL,
687//                              LiteralNodeSubsumptionStrategy.MIN,
688//                              LiteralNodeSubsumptionStrategy.MAX,
689                                };
690                for (LiteralNodeSubsumptionStrategy strategy : strategies) {
691                        // 1. get a score for the coverage = recall oriented
692                        List<RDFResourceTree> uncoveredPositiveExampleTrees = new ArrayList<>();
693                        List<RDFResourceTree> coveredNegativeExampleTrees = new ArrayList<>();
694                        
695                        // compute positive examples which are not covered by LGG
696                        for (RDFResourceTree posTree : currentPosExampleTrees) {
697//                              System.out.print(currentPosExampleTrees.indexOf(posTree) + ":");
698                                if(!QueryTreeUtils.isSubsumedBy(posTree, tree, entailment, reasoner)){
699//                                      System.err.println(posTree.getStringRepresentation(true));System.err.println(tree.getStringRepresentation(true));
700//                                      System.out.println("FALSE");
701                                        uncoveredPositiveExampleTrees.add(posTree);
702                                } else {
703//                                      System.out.println("TRUE");
704                                }
705                        }
706                        
707                        // compute negative examples which are covered by LGG
708                        for (RDFResourceTree negTree : currentNegExampleTrees) {
709                                if(QueryTreeUtils.isSubsumedBy(negTree, tree, entailment, reasoner)){
710                                        coveredNegativeExampleTrees.add(negTree);
711                                }
712                        }
713                        
714                        // convert to individuals
715                        Set<OWLIndividual> uncoveredPosExamples = asIndividuals(uncoveredPositiveExampleTrees);
716                        Set<OWLIndividual> coveredNegExamples = asIndividuals(coveredNegativeExampleTrees);
717                        
718                        // compute score
719                        int coveredPositiveExamples = currentPosExampleTrees.size() - uncoveredPositiveExampleTrees.size();
720                        double recall = coveredPositiveExamples / (double)currentPosExampleTrees.size();
721                        double precision = (coveredNegativeExampleTrees.size() + coveredPositiveExamples == 0)
722                                                        ? 0
723                                                        : coveredPositiveExamples / (double)(coveredPositiveExamples + coveredNegativeExampleTrees.size());
724                        
725                        double coverageScore = Heuristics.getFScore(recall, precision, beta);
726                        
727                        // 2. get a score for the specifity of the query, i.e. how many edges/nodes = precision oriented
728                        int nrOfSpecificNodes = 0;
729                        for (RDFResourceTree childNode : QueryTreeUtils.getNodes(tree)) {
730                                if(!childNode.isVarNode()){
731                                        nrOfSpecificNodes++;
732                                }
733                        }
734                        double specifityScore = 0d;
735                        if(useSpecifity){
736                                specifityScore = Math.log(nrOfSpecificNodes);
737                        }
738                        
739                        // 3.compute the total score
740                        double score = coverageWeight * coverageScore + specifityWeight * specifityScore;
741                        
742                        QueryTreeScore queryTreeScore = new QueryTreeScore(score, coverageScore,
743                                        new TreeSet<>(Sets.difference(currentPosExamples, uncoveredPosExamples)), uncoveredPosExamples,
744                                        coveredNegExamples, new TreeSet<>(Sets.difference(currentNegExamples, coveredNegExamples)),
745                                        specifityScore, nrOfSpecificNodes);
746                        
747                        EvaluatedRDFResourceTree evaluatedTree = new EvaluatedRDFResourceTree(tree, uncoveredPositiveExampleTrees, coveredNegativeExampleTrees, queryTreeScore);
748                        
749                        //TODO use only the heuristic to compute the score
750                        score = heuristic.getScore(evaluatedTree);
751                        queryTreeScore.setScore(score);
752                        queryTreeScore.setAccuracy(score);
753                        
754                        evaluatedTrees.add(evaluatedTree);
755                }
756                
757                return evaluatedTrees;
758        }
759
760        /**
761         * Evaluated a query tree such that it returns a set of evaluated query trees.
762         * A set is returned because there are several ways how to convert literal nodes.
763         * @param tree the query tree
764         * @param useSpecifity whether to use SPECIFITY as measure
765         * @return a set of evaluated query trees
766         */
767        private Set<EvaluatedRDFResourceTree> evaluate2(RDFResourceTree tree, boolean useSpecifity){
768                Set<EvaluatedRDFResourceTree> evaluatedTrees = new TreeSet<>();
769                
770                //test different strategies on the conversion of literal nodes
771                Set<OWLClassExpression> combinations = new HashSet<>();
772                
773                for (LiteralNodeConversionStrategy strategy : strategies) {
774                        OWLClassExpression ce = QueryTreeUtils.toOWLClassExpression(tree);
775                        combinations.add(ce);
776                }
777                //compute all combinations of different types of facets
778//              OWLClassExpression ce = tree.asOWLClassExpression(LiteralNodeConversionStrategy.FACET_RESTRICTION);
779//              combinations = ce.accept(new ClassExpressionLiteralCombination());
780                for (OWLClassExpression c : combinations) {
781                        //convert to individuals
782                        SortedSet<OWLIndividual> coveredExamples = reasoner.getIndividuals(c);
783                        Set<OWLIndividual> coveredPosExamples = new TreeSet<>(Sets.intersection(currentPosExamples, coveredExamples));
784                        Set<OWLIndividual> uncoveredPosExamples = new TreeSet<>(Sets.difference(currentPosExamples, coveredExamples));
785                        Set<OWLIndividual> coveredNegExamples = new TreeSet<>(Sets.intersection(currentNegExamples, coveredExamples));
786                        Set<OWLIndividual> uncoveredNegExamples = new TreeSet<>(Sets.difference(currentNegExamples, coveredExamples));
787                        
788                        //compute score
789                        double recall = coveredPosExamples.size() / (double)currentPosExamples.size();
790                        double precision = (coveredNegExamples.size() + coveredPosExamples.size() == 0)
791                                                        ? 0
792                                                        : coveredPosExamples.size() / (double)(coveredPosExamples.size() + coveredNegExamples.size());
793                        
794                        double coverageScore = Heuristics.getFScore(recall, precision, beta);
795                        
796                        //2. get a score for the specificity of the query, i.e. how many edges/nodes = precision oriented
797                        int nrOfSpecificNodes = 0;
798                        for (RDFResourceTree childNode : QueryTreeUtils.getNodes(tree)){
799                                if(!childNode.isVarNode()){
800                                        nrOfSpecificNodes++;
801                                }
802                        }
803                        double specifityScore = 0d;
804                        if(useSpecifity){
805                                specifityScore = Math.log(nrOfSpecificNodes);
806                        }
807                        
808                        //3.compute the total score
809                        double score = coverageWeight * coverageScore + specifityWeight * specifityScore;
810                        
811                        QueryTreeScore queryTreeScore = new QueryTreeScore(
812                                        score, coverageScore,
813                                        coveredPosExamples, uncoveredPosExamples,
814                                        coveredNegExamples, uncoveredNegExamples,
815                                        specifityScore, nrOfSpecificNodes);
816                        
817                        //TODO use only the heuristic to compute the score
818                        EvaluatedRDFResourceTree evaluatedTree = new EvaluatedRDFResourceTree(tree,
819                                        asQueryTrees(uncoveredPosExamples), asQueryTrees(coveredNegExamples), queryTreeScore);
820                        score = heuristic.getScore(evaluatedTree);
821                        queryTreeScore.setScore(score);
822                        queryTreeScore.setAccuracy(score);
823                        
824                        
825                        EvaluatedDescription evaluatedDescription = new EvaluatedDescription(c, queryTreeScore);
826                        
827                        evaluatedTree.setDescription(evaluatedDescription);
828                        
829                        evaluatedTrees.add(evaluatedTree);
830                }
831                return evaluatedTrees;
832        }
833        
834        private EvaluatedDescription<? extends Score> buildCombinedSolution(){
835                EvaluatedDescription<? extends Score> bestCombinedSolution = null;
836                double bestScore = Double.NEGATIVE_INFINITY;
837                LiteralNodeConversionStrategy[] strategies = LiteralNodeConversionStrategy.values();
838                strategies = new LiteralNodeConversionStrategy[]{LiteralNodeConversionStrategy.DATATYPE};
839                for (LiteralNodeConversionStrategy strategy : strategies) {
840                        EvaluatedDescription<? extends Score> combinedSolution;
841                        if(partialSolutions.size() == 1){
842                                combinedSolution = partialSolutions.get(0).asEvaluatedDescription();
843                        } else {
844                                Set<OWLClassExpression> disjuncts = new TreeSet<>();
845                                
846                                Set<OWLIndividual> posCovered = new HashSet<>();
847                                Set<OWLIndividual> negCovered = new HashSet<>();
848                                
849                                //build the union of all class expressions
850                                OWLClassExpression partialDescription;
851                                for (EvaluatedRDFResourceTree partialSolution : partialSolutions) {
852                                        partialDescription = partialSolution.asEvaluatedDescription().getDescription();
853                                        disjuncts.add(partialDescription);
854                                        posCovered.addAll(partialSolution.getTreeScore().getCoveredPositives());
855                                        negCovered.addAll(partialSolution.getTreeScore().getCoveredNegatives());
856                                }
857                                OWLClassExpression unionDescription = dataFactory.getOWLObjectUnionOf(disjuncts);
858                                
859                                Set<OWLIndividual> posNotCovered = Sets.difference(lp.getPositiveExamples(), posCovered);
860                                Set<OWLIndividual> negNotCovered = Sets.difference(lp.getNegativeExamples(), negCovered);
861                                
862                                //compute the coverage
863                                double recall = posCovered.size() / (double)lp.getPositiveExamples().size();
864                                double precision = (posCovered.size() + negCovered.size() == 0)
865                                                                ? 0
866                                                                : posCovered.size() / (double)(posCovered.size() + negCovered.size());
867                                
868                                double coverageScore = Heuristics.getFScore(recall, precision, beta);
869                                
870//                              ScoreTwoValued score = new ScoreTwoValued(posCovered, posNotCovered, negCovered, negNotCovered);
871//                              score.setAccuracy(coverageScore);
872                                QueryTreeScore score = new QueryTreeScore(coverageScore, coverageScore, posCovered, posNotCovered, negCovered, negNotCovered, -1, -1);
873                                
874                                combinedSolution = new EvaluatedDescription(unionDescription, score);
875                        }
876                        if(combinedSolution.getAccuracy() > bestScore){
877                                bestCombinedSolution = combinedSolution;
878                                bestCurrentScore = combinedSolution.getAccuracy();
879                        }
880                }
881                return bestCombinedSolution;
882        }
883        
884        private void reset(){
885                stop = false;
886                isRunning = true;
887                
888                currentBestSolution = null;
889                partialSolutions = new ArrayList<>();
890                
891                bestCurrentScore = minimumTreeScore;
892                
893                MonitorFactory.getTimeMonitor("lgg").reset();
894                nanoStartTime = System.nanoTime();
895        }
896        
897        /* (non-Javadoc)
898         * @see org.dllearner.core.StoppableLearningAlgorithm#stop()
899         */
900        @Override
901        public void stop() {
902                stop = true;
903        }
904        
905        /* (non-Javadoc)
906         * @see org.dllearner.core.AbstractCELA#getCurrentlyBestDescription()
907         */
908        @Override
909        public OWLClassExpression getCurrentlyBestDescription() {
910                return currentBestSolution.getDescription();
911        }
912        
913        /* (non-Javadoc)
914         * @see org.dllearner.core.AbstractCELA#getCurrentlyBestEvaluatedDescription()
915         */
916        @Override
917        public EvaluatedDescription getCurrentlyBestEvaluatedDescription() {
918                return currentBestSolution;
919        }
920        
921        /* (non-Javadoc)
922         * @see org.dllearner.core.StoppableLearningAlgorithm#isRunning()
923         */
924        @Override
925        public boolean isRunning() {
926                return isRunning;
927        }
928        
929//      @Autowired
930//      public void setLearningProblem(PosNegLP learningProblem) {
931//              this.lp = learningProblem;
932//      }
933        
934//      @Autowired
935        @Override
936        public void setReasoner(AbstractReasonerComponent reasoner){
937                super.setReasoner(reasoner);
938//              loadModel();
939        }
940
941        private void loadModel(){
942                model = ModelFactory.createDefaultModel();
943                for (KnowledgeSource ks : reasoner.getSources()) {
944                        if(ks instanceof OWLFile){
945                                try {
946                                        model.read(((OWLFile) ks).getURL().openStream(), null);
947                                } catch (IOException e) {
948                                        e.printStackTrace();
949                                }
950                        } else if(ks instanceof OWLAPIOntology){
951                                ByteArrayInputStream bais = new ByteArrayInputStream(((OWLAPIOntology) ks).getConverter().convert(((OWLAPIOntology) ks).getOntology()));
952                                model.read(bais, null);
953                                try {
954                                        bais.close();
955                                } catch (IOException e) {
956                                        e.printStackTrace();
957                                }
958                        }
959                }
960        }
961        
962        private Set<OWLIndividual> asIndividuals(Collection<RDFResourceTree> trees){
963                Set<OWLIndividual> individuals = new HashSet<>(trees.size());
964                for (RDFResourceTree queryTree : trees) {
965                        individuals.add(tree2Individual.get(queryTree));
966                }
967                return individuals;
968        }
969        
970        private Set<RDFResourceTree> asQueryTrees(Collection<OWLIndividual> individuals){
971                Set<RDFResourceTree> trees = new HashSet<>(individuals.size());
972                for (OWLIndividual ind : individuals) {
973                        trees.add(tree2Individual.inverse().get(ind));
974                }
975                return trees;
976        }
977
978        /**
979         * Computes all trees from the given list {@code allTrees} which are subsumed by {@code tree}.
980         * @param tree the tree
981         * @param trees all trees
982         * @return all trees from the given list {@code allTrees} which are subsumed by {@code tree}
983         */
984        private List<RDFResourceTree> getCoveredTrees(RDFResourceTree tree, List<RDFResourceTree> trees){
985                List<RDFResourceTree> coveredTrees = new ArrayList<>();
986                for (RDFResourceTree queryTree : trees) {
987                        if(QueryTreeUtils.isSubsumedBy(queryTree, tree)){
988                                coveredTrees.add(queryTree);
989                        }
990                }
991                return coveredTrees;
992        }
993
994        /**
995         * Computes all trees from the given list {@code trees} which are not subsumed by {@code tree}.
996         * @param tree the tree
997         * @param trees the trees
998         * @return all trees from the given list {@code trees} which are not subsumed by {@code tree}.
999         */
1000        private List<RDFResourceTree> getUncoveredTrees(RDFResourceTree tree, List<RDFResourceTree> trees){
1001                List<RDFResourceTree> uncoveredTrees = new ArrayList<>();
1002                for (RDFResourceTree queryTree : trees) {
1003                        if(!QueryTreeUtils.isSubsumedBy(queryTree, tree)){
1004                                uncoveredTrees.add(queryTree);
1005                        }
1006                }
1007                return uncoveredTrees;
1008        }
1009
1010        private boolean terminationCriteriaSatisfied() {
1011                //stop was called or time expired
1012                if(stop || isTimeExpired()){
1013                        return true;
1014                }
1015                
1016                // stop if there are no more positive examples to cover
1017                if (stopOnFirstDefinition && currentPosExamples.isEmpty()) {
1018                        return true;
1019                }
1020
1021                // we stop when the score of the last tree added is too low
1022                // (indicating that the algorithm could not find anything appropriate
1023                // in the timeframe set)
1024                if (bestCurrentScore < minimumTreeScore) {
1025                        return true;
1026                }
1027
1028                // stop when almost all positive examples have been covered
1029                if (tryFullCoverage) {
1030                        return false;
1031                } else {
1032                        int maxPosRemaining = (int) Math.ceil(startPosExamplesSize * 0.05d);
1033                        return (currentPosExamples.size() <= maxPosRemaining);
1034                }
1035        }
1036        
1037        private boolean partialSolutionTerminationCriteriaSatisfied(){
1038                return stop || todoList.isEmpty() || currentPosExampleTrees.isEmpty() || (useDisjunction && isPartialSolutionTimeExpired()) || isTimeExpired();
1039        }
1040        
1041        private boolean isPartialSolutionTimeExpired(){
1042                return maxTreeComputationTimeInSeconds > 0 && getRemainingPartialSolutionTime() > 0;
1043        }
1044
1045        private long getRemainingPartialSolutionTime() {
1046                return (long) (maxTreeComputationTimeInSeconds  - (System.currentTimeMillis() - partialSolutionStartTime) / 1000);
1047        }
1048        
1049        /**
1050         * Shows the current setup of the algorithm.
1051         */
1052        private void printSetup(){
1053                String setup = "Setup:";
1054                setup += "\n#Pos. examples:" + currentPosExampleTrees.size();
1055                setup += "\n#Neg. examples:" + currentNegExampleTrees.size();
1056                setup += "\nHeuristic:" + heuristic.getHeuristicType().name();
1057                setup += "\nNoise value=" + noise;
1058                setup += "\nbeta=" + beta;
1059                logger.info(setup);
1060        }
1061        
1062        /**
1063         * @param noisePercentage the noisePercentage to set
1064         */
1065        public void setNoisePercentage(double noisePercentage) {
1066                this.noisePercentage = noisePercentage;
1067        }
1068        
1069        /**
1070         * @param noise the noise to set
1071         */
1072        public void setNoise(double noise) {
1073                this.noise = noise;
1074        }
1075        
1076        /**
1077         * Default value is 1. Lower values force importance of covering positive examples.
1078         * @param beta the beta to set
1079         */
1080        public void setBeta(double beta) {
1081                this.beta = beta;
1082        }
1083        
1084        /**
1085         * Set the max. execution time for the computation of a partial solution. If this value isn't set, the
1086         * max. algorithm runtime will be used, thus, in worst case only one partial solution was computed.
1087         *
1088         * @param maxTreeComputationTimeInSeconds the max. computation for a partial solution tree
1089         */
1090        public void setMaxTreeComputationTimeInSeconds(double maxTreeComputationTimeInSeconds) {
1091                this.maxTreeComputationTimeInSeconds = maxTreeComputationTimeInSeconds;
1092        }
1093        
1094        /**
1095         * @return the heuristic
1096         */
1097        public QueryTreeHeuristic getHeuristic() {
1098                return heuristic;
1099        }
1100        
1101        /**
1102         * @param heuristic the heuristic to set
1103         */
1104        public void setHeuristic(QueryTreeHeuristic heuristic) {
1105                this.heuristic = heuristic;
1106        }
1107        
1108        /**
1109         * @param treeFactory the treeFactory to set
1110         */
1111        public void setTreeFactory(org.dllearner.algorithms.qtl.impl.QueryTreeFactory treeFactory) {
1112                this.treeFactory = treeFactory;
1113        }
1114        
1115        public EvaluatedRDFResourceTree getBestSolution(){
1116                return currentPartialSolutions.last();
1117        }
1118        
1119        public SortedSet<EvaluatedRDFResourceTree> getSolutions(){
1120                return currentPartialSolutions;
1121        }
1122        
1123        public List<EvaluatedRDFResourceTree> getSolutionsAsList(){
1124                //              Collections.sort(list, Collections.reverseOrder());
1125                return new ArrayList<>(currentPartialSolutions);
1126        }
1127        
1128        /**
1129         * @param positiveExampleTrees the positive example trees to set
1130         */
1131        public void setPositiveExampleTrees(Map<OWLIndividual,RDFResourceTree> positiveExampleTrees) {
1132                this.currentPosExampleTrees = new ArrayList<>(positiveExampleTrees.values());
1133                this.currentPosExamples = new HashSet<>(positiveExampleTrees.keySet());
1134                
1135                for (Entry<OWLIndividual, RDFResourceTree> entry : positiveExampleTrees.entrySet()) {
1136                        OWLIndividual ind = entry.getKey();
1137                        RDFResourceTree tree = entry.getValue();
1138                        tree2Individual.put(tree, ind);
1139                }
1140        }
1141        
1142        /**
1143         * @param negativeExampleTrees the negative example trees to set
1144         */
1145        public void setNegativeExampleTrees(Map<OWLIndividual,RDFResourceTree> negativeExampleTrees) {
1146                this.currentNegExampleTrees = new ArrayList<>(negativeExampleTrees.values());
1147                this.currentNegExamples = new HashSet<>(negativeExampleTrees.keySet());
1148                
1149                for (Entry<OWLIndividual, RDFResourceTree> entry : negativeExampleTrees.entrySet()) {
1150                        OWLIndividual ind = entry.getKey();
1151                        RDFResourceTree tree = entry.getValue();
1152                        tree2Individual.put(tree, ind);
1153                }
1154        }
1155        
1156        /**
1157         * @param ks the ks to set
1158         */
1159        @Autowired
1160        public void setKs(SparqlEndpointKS ks) {
1161                this.ks = ks;
1162        }
1163        
1164        /**
1165         * @param maxTreeDepth the maximum depth of the trees, if those have to be generated
1166         * first. The default depth is 2.
1167         */
1168        public void setMaxTreeDepth(int maxTreeDepth) {
1169                this.maxTreeDepth = maxTreeDepth;
1170        }
1171        
1172        /**
1173         * @return the runtime in ms until the best solution was found
1174         */
1175        public long getTimeBestSolutionFound() {
1176                return timeBestSolutionFound;
1177        }
1178        
1179        /* (non-Javadoc)
1180         * @see java.lang.Object#clone()
1181         */
1182        @Override
1183        public Object clone() throws CloneNotSupportedException {
1184                super.clone();
1185                return new QTL2Disjunctive(this);
1186        }
1187}