source: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/utils/MostSimilarTaskDeterminer.java @ 2215

Last change on this file since 2215 was 1981, checked in by pharms, 9 years ago
  • corrected filter for most covered events to decide which task pair to merge first
  • merging of identical sequences can be done in any order --> adapted selection of pair to merge first to handle identical sequences based on their smallest id
  • merging only most prominent tasks (except identical tasks) --> otherwise too many mergings of noise
  • new maximum diff level is 25
File size: 36.5 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.tasktrees.temporalrelation.utils;
16
17import java.util.ArrayList;
18import java.util.Collections;
19import java.util.Comparator;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.Iterator;
23import java.util.LinkedList;
24import java.util.List;
25import java.util.Map;
26import java.util.Set;
27import java.util.logging.Level;
28
29import de.ugoe.cs.autoquest.tasktrees.temporalrelation.TaskComparator;
30import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor;
31import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskTraversingVisitor;
32import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
33import de.ugoe.cs.autoquest.tasktrees.treeifc.IStructuringTemporalRelationship;
34import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
35import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo;
36import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
37import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceList;
38import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
39import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskMetric;
40import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskTreeUtils;
41import de.ugoe.cs.util.console.Console;
42
43/**
44 * <p>
45 * This class is a utility class for detecting similar tasks. It compares all tasks in a given
46 * list with each other and then chooses the pair with the highest level of similarity. For
47 * comparison, the tasks are traversed and only the traversals are compared with each other.
48 * </p>
49 * <p>
50 * Several task pairs may have the same similarity level. In this case, the class performs a
51 * filtering of the pairs to result in a merging order for the tasks that is always the same.
52 * </p>
53 * <p>
54 * If provided with many tasks, this class starts several threads to let the comparisons run in
55 * parallel.
56 * </p>
57 *
58 * @author Patrick Harms
59 */
60public class MostSimilarTaskDeterminer {
61
62    /**
63     * If this similarity level is exceeded, two tasks are not considered similar anymore. 25 means
64     * that at most 25% of the elements of both task traversals are not the same.
65     */
66    private static int MAX_DIFF_LEVEL = 25;
67   
68    /**
69     * task comparator used internally
70     */
71    private TaskComparator comparator;
72   
73    /**
74     * for performance reasons, some task comparisons can be excluded beforehand and the exclusions
75     * are stored in this list
76     */
77    private Set<Long> comparisonsToSkip = new HashSet<>();
78   
79    /** TODO comment */
80    private long comparisonCounter = 0;
81   
82    /** TODO comment */
83    private long effectiveComparisonCounter = 0;
84
85    /**
86     * Initialize instances of this class with the task comparator to be used
87     */
88    public MostSimilarTaskDeterminer(TaskComparator comparator) {
89        this.comparator = comparator;
90    }
91
92    /**
93     * add a pair of comparisons that is not done to increase performance
94     */
95    public void addComparisonToSkip(ITask task1, ITask task2) {
96        comparisonsToSkip.add(getMapId(task1, task2));
97    }
98
99    /**
100     * returns a list of most similar tasks. Independent of the order of the provided tasks, the
101     * returned list will always be the same for the same input elements.
102     */
103    public List<SimilarTasks> getMostSimilarTasks(List<ITask> tasks, ITaskModel taskModel) {
104        Console.println("comparing " + tasks.size() + " sequences with each other");
105
106        LinkedList<SimilarTasks> mostSimilarTasksList = performComparisons(taskModel, tasks);
107       
108        Console.println("initially found " + mostSimilarTasksList.size() + " similar sequences");
109       
110        applyFilterForSmallestDiffLevel(mostSimilarTasksList);
111       
112        Console.println("only " + mostSimilarTasksList.size() + " have the smallest diff level");
113       
114        applyFilterForParents(mostSimilarTasksList);
115       
116        Console.println(mostSimilarTasksList.size() + " remain after filtering for parents");
117       
118        applyFilterForMostCoveredEvents(mostSimilarTasksList, taskModel);
119       
120        Console.println("calculated " + mostSimilarTasksList.size() + " most similar sequences");
121
122        return mostSimilarTasksList;
123    }
124
125    /**
126     * filters the given list of similar tasks for those having the smallest diff level, which is
127     * in turn the highest similarity
128     */
129    private void applyFilterForSmallestDiffLevel(LinkedList<SimilarTasks> mostSimilarTasksList) {
130        // determine the smallest diff level
131        int smallestDiffLevel = Integer.MAX_VALUE;
132       
133        for (SimilarTasks candidate : mostSimilarTasksList) {
134            if (candidate.getDiffLevel() < smallestDiffLevel) {
135                smallestDiffLevel = candidate.getDiffLevel();
136            }
137        }
138       
139        if (smallestDiffLevel <= MAX_DIFF_LEVEL) {
140            // remove all entries with a higher diff level
141            Iterator<SimilarTasks> listIterator = mostSimilarTasksList.iterator();
142       
143            while (listIterator.hasNext()) {
144                if (listIterator.next().getDiffLevel() > smallestDiffLevel) {
145                    listIterator.remove();
146                }
147            }
148           
149            Console.println("smallest diff level is " + smallestDiffLevel + " (max is " +
150                            MAX_DIFF_LEVEL + ")");
151        }
152        else {
153            mostSimilarTasksList.clear();
154        }
155    }
156   
157    /**
158     * ensures that the given list of similar tasks does not contain two pairs, where one refers
159     * to a child task of another pair.
160     */
161    private void applyFilterForParents(LinkedList<SimilarTasks> mostSimilarTasksList) {
162       
163        // remove all entries being parents of another entry or where both tasks are
164        // generated through this rule
165        Iterator<SimilarTasks> listIterator = mostSimilarTasksList.iterator();
166        List<SimilarTasks> similarTasksToRemove = new LinkedList<SimilarTasks>();
167   
168        while (listIterator.hasNext()) {
169            SimilarTasks candidate = listIterator.next();
170           
171            ITask task1 = candidate.getLeftHandSide();
172            ITask task2 = candidate.getRightHandSide();
173            for (SimilarTasks potentialChild : mostSimilarTasksList) {
174                ITask task3 = potentialChild.getLeftHandSide();
175                ITask task4 = potentialChild.getRightHandSide();
176                if (TaskTreeUtils.isChild(task3, task1) ||
177                    TaskTreeUtils.isChild(task3, task2) ||
178                    TaskTreeUtils.isChild(task4, task1) ||
179                    TaskTreeUtils.isChild(task4, task2))
180                {
181                    similarTasksToRemove.add(candidate);
182                    break;
183                }
184            }
185        }
186       
187        listIterator = mostSimilarTasksList.iterator();
188       
189        while (listIterator.hasNext()) {
190            SimilarTasks candidate = listIterator.next();
191           
192            for (SimilarTasks toRemove : similarTasksToRemove) {
193                if (candidate == toRemove) {
194                    listIterator.remove();
195                }
196            }
197        }
198    }
199
200    /**
201     * performs a filtering of the detected similar tasks which ensures, that the list does not
202     * contain two pairs referring to the same task and that in such cases always the same pair
203     * will remain.
204     */
205    private void applyFilterForMostCoveredEvents(LinkedList<SimilarTasks> mostSimilarTasksList,
206                                                 final ITaskModel         taskModel)
207    {
208        List<SimilarTasks> sortedList = new ArrayList<>(mostSimilarTasksList);
209
210        // sort the list of similar tasks based on which to merge first
211        Collections.sort(sortedList, new Comparator<SimilarTasks>() {
212            @Override
213            public int compare(SimilarTasks first, SimilarTasks second) {
214                SimilarTasks toMergeFirst = getFirstToMerge(first, second, taskModel);
215
216                if (toMergeFirst == first) {
217                    return -1;
218                }
219                else if (toMergeFirst == null) {
220                    return 0;
221                }
222                else {
223                    return 1;
224                }
225            }
226        });
227
228        // the first in the ordered list is the first to merge, except there are several
229        // firsts in the lists for which it can not be decided which to merge first
230
231        // now remove all subsequent pairs also referring tasks referred by first pairs
232        Set<ITask> referredTasks = new HashSet<>();
233       
234        int index = 0;
235        while (index < sortedList.size()) {
236            SimilarTasks pair = sortedList.get(index);
237           
238            if (referredTasks.contains(pair.getLeftHandSide()) ||
239                referredTasks.contains(pair.getRightHandSide()))
240            {
241                // we found a pair that refers to a task already referred by a preceding pair.
242                // The pair can be removed, if the previous pair referring to the same task also
243                // is to be merged first.
244                for (int i = 0; i < index; i++) {
245                    SimilarTasks previousPair = sortedList.get(i);
246                    if ((previousPair.getLeftHandSide() == pair.getLeftHandSide()) ||
247                        (previousPair.getLeftHandSide() == pair.getRightHandSide()) ||
248                        (previousPair.getRightHandSide() == pair.getLeftHandSide()) ||
249                        (previousPair.getRightHandSide() == pair.getRightHandSide()))
250                    {
251                        if (getFirstToMerge(previousPair, pair, taskModel) == null) {
252                            for (SimilarTasks tmp : sortedList) {
253                                tmp.dump(System.out);
254                            }
255
256                            throw new RuntimeException("several tasks are similar so that it is " +
257                                                       "undecidable which to merge first");
258                        }
259                    }
260                }
261               
262                sortedList.remove(index);
263            }
264            else {
265                referredTasks.add(pair.getLeftHandSide());
266                referredTasks.add(pair.getRightHandSide());
267                index++;
268            }
269        }
270       
271        // now the list is sorted and contains at most one pair for each task referred multiple
272        // times
273        mostSimilarTasksList.clear();
274        mostSimilarTasksList.addAll(sortedList);
275    }
276
277    /**
278     * <p>
279     * compares two similar tasks and decides which of them is to be merged first.
280     * </p>
281     */
282    private SimilarTasks getFirstToMerge(SimilarTasks first,
283                                         SimilarTasks second,
284                                         ITaskModel   taskModel)
285    {
286       
287        int valFirst = getTaskMetric(first, TaskMetric.EVENT_COVERAGE, taskModel);
288        int valSecond = getTaskMetric(second, TaskMetric.EVENT_COVERAGE, taskModel);
289
290        if (valSecond > valFirst) {
291            return second;
292        }
293        else if (valSecond < valFirst) {
294            return first;
295        }
296
297        // no of covered events is equal, try to distinguish by count
298
299        valFirst = getTaskMetric(first, TaskMetric.COUNT, taskModel);
300        valSecond = getTaskMetric(second, TaskMetric.COUNT, taskModel);
301
302        if (valSecond > valFirst) {
303            return second;
304        }
305        else if (valSecond < valFirst) {
306            return first;
307        }
308
309        // count is equal, try to distinguish by depth
310
311        valFirst = getTaskMetric(first, TaskMetric.DEPTH, taskModel);
312        valSecond = getTaskMetric(second, TaskMetric.DEPTH, taskModel);
313
314        if (valSecond < valFirst) {
315            return second;
316        }
317        else if (valSecond > valFirst) {
318            return first;
319        }
320
321        // no of covered events is equal, try to distinguish by count
322
323        valFirst = cumulateTaskMetric(first, TaskMetric.COUNT, taskModel);
324        valSecond = cumulateTaskMetric(second, TaskMetric.COUNT, taskModel);
325
326        if (valSecond > valFirst) {
327            return second;
328        }
329        else if (valSecond < valFirst) {
330            return first;
331        }
332
333        // depth is equal. Calculate for both the similarity
334        // based on which the merging will take place
335        SimilarTasks tmp = SimilarTasks.getMergableLevelOfSimilarity(first, comparator);
336        valFirst = tmp != null ? tmp.getDiffLevel() : Integer.MAX_VALUE;
337           
338        tmp = SimilarTasks.getMergableLevelOfSimilarity(second, comparator);
339        valSecond = tmp != null ? tmp.getDiffLevel() : Integer.MAX_VALUE;
340
341        if (valSecond < valFirst) {
342            return second;
343        }
344        else if (valSecond > valFirst) {
345            return first;
346        }
347       
348        // it may be the case that both tasks are event identical. For example, if through a
349        // merge formerly different children became the same. In this case, merging can be
350        // done in any order. Hence, just return any of the given to be merged first. To ensure
351        // to not define a circle of three tasks to be merged, decide using the ids of the
352        // identical tasks.
353        if ((first.getDiffLevel() == 0) && (second.getDiffLevel() == 0)) {
354            ITask taskWithSmallestId = first.getLeftHandSide();
355            SimilarTasks pairToReturn = first;
356           
357            if (taskWithSmallestId.getId() > first.getRightHandSide().getId()) {
358                taskWithSmallestId = first.getRightHandSide();
359            }
360           
361            if (taskWithSmallestId.getId() > second.getLeftHandSide().getId()) {
362                taskWithSmallestId = second.getLeftHandSide();
363                pairToReturn = second;
364            }
365           
366            if (taskWithSmallestId.getId() > second.getRightHandSide().getId()) {
367                taskWithSmallestId = second.getRightHandSide();
368                pairToReturn = second;
369            }
370           
371            return pairToReturn;
372        }
373       
374        return null;
375    }
376
377    /**
378     * starts several threads performing the task comparisons
379     */
380    private LinkedList<SimilarTasks> performComparisons(ITaskModel taskModel, List<ITask> tasks) {
381        Set<ITask> mostProminentTasks = getMostProminentTasks(taskModel, tasks);
382        LinkedList<SimilarTasks> mostSimilarTasks = new LinkedList<SimilarTasks>();
383        List<Runnable> startedRunnables = new LinkedList<Runnable>();
384
385        comparisonCounter = 0;
386       
387        // groups size is minimal 100 or maximal 1000
388        int groupSize = Math.max
389            (100, Math.min(3000, 1 + (tasks.size() / Runtime.getRuntime().availableProcessors())));
390       
391        synchronized (startedRunnables) {
392            int start1 = 0;
393            int end1;
394            int start2;
395            int end2;
396           
397            do {
398                end1 = Math.min(start1 + groupSize, tasks.size());
399                TaskTraversal[] leftHandTraversals = new TaskTraversal[end1 - start1];
400               
401                Console.traceln(Level.FINE, "calculating left hand traversals from " + start1 +
402                                " to " + end1);
403               
404                int index = 0;
405                for (int j = start1; j < end1; j++) {
406                    leftHandTraversals[index++] =
407                        TaskTraversal.getTraversal(tasks.get(j), null);
408                }
409
410                start2 = 0;
411                do {
412                    end2 = Math.min(start2 + groupSize, end1 - 1);
413                    TaskTraversal[] rightHandTraversals = new TaskTraversal[end2 - start2];
414
415                    if (end2 <= start1) {
416                        Console.traceln(Level.FINE, "calculating right hand traversals from " +
417                                        start2 + " to " + end2);
418                       
419                        // traversals need to be created
420                        index = 0;
421                        for (int j = start2; j < end2; j++) {
422                            rightHandTraversals[index++] =
423                                TaskTraversal.getTraversal(tasks.get(j), null);
424                        }
425                       
426                    }
427                    else {
428                        // traversals can be reused
429                        Console.traceln(Level.FINE, "reusing traversals for right hand from " +
430                                        start2 + " to " + end2);
431                       
432                        rightHandTraversals = leftHandTraversals;
433                    }
434                   
435                    Runnable runnable = new CompareRunnable(taskModel, leftHandTraversals,
436                                                            rightHandTraversals,
437                                                            mostProminentTasks,
438                                                            mostSimilarTasks, startedRunnables);
439                   
440                    while (startedRunnables.size() >=
441                               Math.max(1, Runtime.getRuntime().availableProcessors()))
442                    {
443                        try {
444                            Console.traceln(Level.FINER, "waiting for next thread to finish");
445                            startedRunnables.wait();
446                            Console.traceln(Level.FINER, "thread finished");
447                        }
448                        catch (InterruptedException e) {
449                            // should not happen
450                            Console.logException(e);
451                        }
452                    }
453               
454                    Console.traceln(Level.FINER, "starting next thread");
455                    startedRunnables.add(runnable);
456                    new Thread(runnable).start();
457                    Console.traceln(Level.FINER, "started next thread " + runnable);
458                   
459                    start2 = end2;
460                }
461                while (end2 < (end1 - 1));
462               
463                start1 = end1;
464            }
465            while (end1 < tasks.size());
466           
467           
468            while (startedRunnables.size() > 0) {
469                try {
470                    Console.traceln(Level.FINER, "waiting for next thread to finish");
471                    startedRunnables.wait();
472                    Console.traceln(Level.FINER, "thread finished");
473                }
474                catch (InterruptedException e) {
475                    // should not happen
476                    Console.logException(e);
477                }
478            }
479        }
480       
481        Console.traceln
482            (Level.FINER, "all threads finished, " + comparisonCounter + " comparisons done (" +
483             effectiveComparisonCounter + " effectively)");
484       
485        if (comparisonCounter != (((tasks.size() - 1) * tasks.size()) / 2)) {
486            throw new RuntimeException(comparisonCounter + "  " +
487                                       (((tasks.size() - 1) * tasks.size()) / 2));
488        }
489       
490        return mostSimilarTasks;
491    }
492
493    /**
494     *
495     */
496    private Set<ITask> getMostProminentTasks(ITaskModel model, List<ITask> tasks) {
497        Map<Integer, List<ITask>> sortedTasks = new HashMap<>();
498
499        int maxCoverage = 0;
500
501        for (ITask task : tasks) {
502            int coveredEvents = model.getTaskInfo(task).getMeasureValue(TaskMetric.EVENT_COVERAGE);
503
504            List<ITask> tasksWithSameCoverage = sortedTasks.get(coveredEvents);
505
506            if (tasksWithSameCoverage == null) {
507                tasksWithSameCoverage = new LinkedList<>();
508                sortedTasks.put(coveredEvents, tasksWithSameCoverage);
509            }
510
511            tasksWithSameCoverage.add(task);
512
513            maxCoverage = Math.max(maxCoverage, coveredEvents);
514        }
515
516        Set<ITask> result = new HashSet<>();
517
518        for (int i = maxCoverage; i > 0; i--) {
519            List<ITask> tasksWithSameCoverage = sortedTasks.get(i);
520
521            if (tasksWithSameCoverage != null) {
522                result.addAll(tasksWithSameCoverage);
523
524                if (result.size() * 5 >= tasks.size()) {
525                    break;
526                }
527            }
528        }
529
530        return result;
531    }
532
533    /**
534     * convenience method to get the value of a task metric
535     */
536    private int getTaskMetric(SimilarTasks similarTasks, TaskMetric metric, ITaskModel taskModel) {
537        if (similarTasks == null) {
538            return 0;
539        }
540       
541        ITaskInfo info1 = taskModel.getTaskInfo(similarTasks.getLeftHandSide());
542        ITaskInfo info2 = taskModel.getTaskInfo(similarTasks.getRightHandSide());
543       
544        return info1.getMeasureValue(metric) + info2.getMeasureValue(metric);
545    }
546   
547    /**
548     * convenience method to get the cumulative value of a task metric
549     */
550    private int cumulateTaskMetric(SimilarTasks     similarTasks,
551                                   final TaskMetric metric,
552                                   final ITaskModel taskModel)
553    {
554        final int[] value = new int[1];
555        value[0] = 0;
556       
557        DefaultTaskTraversingVisitor visitor = new DefaultTaskTraversingVisitor() {
558            @Override
559            public void visit(IStructuringTemporalRelationship relationship) {
560                value[0] += taskModel.getTaskInfo(relationship).getMeasureValue(metric);
561                super.visit(relationship);
562            }
563        };
564       
565        similarTasks.getLeftHandSide().accept(visitor);
566        similarTasks.getRightHandSide().accept(visitor);
567       
568        return value[0];
569    }
570
571    /**
572     * convenience method to check if a specific comparison shall be skipped
573     */
574    private boolean isComparisonToSkip(ITask task1, ITask task2) {
575        return comparisonsToSkip.contains(getMapId(task1, task2));
576    }
577
578    /**
579     * convenience method to get a unique id for representing a pair of two tasks
580     */
581    private long getMapId(ITask task1, ITask task2) {
582        if (task1.getId() < task2.getId()) {
583            return (((long) task1.getId()) << 32) + task2.getId();
584        }
585        else {
586            return (((long) task2.getId()) << 32) + task1.getId();
587        }
588    }
589
590    /**
591     * Runnable performing a subset of task comparisons in a dedicated thread
592     */
593    public class CompareRunnable implements Runnable {
594
595        /** */
596        private ITaskModel taskModel;
597       
598        /** */
599        private TaskTraversal[] leftHandTraversals;
600       
601        /** */
602        private TaskTraversal[] rightHandTraversals;
603       
604        /** */
605        private Set<ITask> mostProminentTasks;
606       
607        /** */
608        private List<SimilarTasks> mostSimilarTasksList;
609
610        /** */
611        private List<Runnable> unfinishedRunnables;
612       
613        /**
614         *
615         */
616        public CompareRunnable(ITaskModel         taskModel,
617                               TaskTraversal[]    leftHandTraversals,
618                               TaskTraversal[]    rightHandTraversals,
619                               Set<ITask>         mostProminentTasks,
620                               List<SimilarTasks> mostSimilarTasksList,
621                               List<Runnable>     unfinishedRunnables)
622        {
623            this.taskModel = taskModel;
624            this.leftHandTraversals = leftHandTraversals;
625            this.rightHandTraversals = rightHandTraversals;
626            this.mostProminentTasks = mostProminentTasks;
627            this.mostSimilarTasksList = mostSimilarTasksList;
628            this.unfinishedRunnables = unfinishedRunnables;
629        }
630
631        /* (non-Javadoc)
632         * @see java.lang.Runnable#run()
633         */
634        @Override
635        public void run() {
636            SimilarTasks mostSimilarTasks = SimilarTasks.UNEQUAL_TASKS;
637            int mostSimilarDiffLevel = MAX_DIFF_LEVEL;
638            List<SimilarTasks> allMostSimilarTasks = new LinkedList<SimilarTasks>();
639            int counter = 0;
640            int effectiveComparisons = 0;
641           
642            LEFT_HAND_TRAVERSAL:
643            for (int i = 0; i < leftHandTraversals.length; i++) {
644               
645                ITask leftHandTask = leftHandTraversals[i].getTask();
646               
647                RIGHT_HAND_TRAVERSAL:
648                for (int j = 0; j < rightHandTraversals.length; j++) {
649
650                    ITask rightHandTask = rightHandTraversals[j].getTask();
651                   
652                    try {
653                        if (leftHandTask == rightHandTask) {
654                            continue LEFT_HAND_TRAVERSAL;
655                        }
656                       
657                        counter++;
658
659                        if (isComparisonToSkip(leftHandTask, rightHandTask)) {
660                            continue RIGHT_HAND_TRAVERSAL;
661                        }
662                       
663                        if (!isBasicallySimilar(leftHandTraversals[i], rightHandTraversals[j])) {
664                            continue RIGHT_HAND_TRAVERSAL;
665                        }
666                       
667                        effectiveComparisons++;
668
669                        SimilarTasks similarTasks = SimilarTasks.compareTraversals
670                            (leftHandTraversals[i], rightHandTraversals[j], comparator);
671
672                        if (similarTasks.getDiffLevel() > 0) {
673                            if (!mostProminentTasks.contains(leftHandTask) ||
674                                !mostProminentTasks.contains(rightHandTask))
675                            {
676                                // the tasks are not fully identical. Hence, we merge them only,
677                                // if both are most prominent ones. If they were fully identical,
678                                // they may have changed through merging initially different
679                                // children which may have become the same. In that case, they
680                                // must be merged even if they are not most prominent.
681                                continue RIGHT_HAND_TRAVERSAL;
682                            }
683
684                            SimilarTasks similarTasks2 = SimilarTasks.compareTraversals
685                                (rightHandTraversals[j], leftHandTraversals[i], comparator);
686                           
687                            if ((similarTasks.getDiffLevel() <= mostSimilarDiffLevel) ||
688                                (similarTasks2.getDiffLevel() <= mostSimilarDiffLevel)) {
689                               
690                                similarTasks = getSimilarTasksToPrefer(similarTasks, similarTasks2);
691                            }
692                        }
693                       
694                        if (similarTasks.isInBetweenDifference() ||
695                            (similarTasks.getPatch().getDeltas().size() == 0))
696                        {
697                            if (similarTasks.getDiffLevel() < mostSimilarDiffLevel) {
698                                mostSimilarTasks = similarTasks;
699                                mostSimilarDiffLevel = mostSimilarTasks.getDiffLevel();
700                                allMostSimilarTasks.clear();
701                                allMostSimilarTasks.add(similarTasks);
702                            }
703                            else if (similarTasks.getDiffLevel() == mostSimilarDiffLevel) {
704                                allMostSimilarTasks.add(similarTasks);
705                            }
706                        }
707                    }
708                    catch (Exception e) {
709                        e.printStackTrace();
710
711                        SimilarTasks similarTasks1 = SimilarTasks.compareTraversals
712                            (leftHandTraversals[i], rightHandTraversals[j], comparator);
713
714                        SimilarTasks similarTasks2 = SimilarTasks.compareTraversals
715                            (rightHandTraversals[j], leftHandTraversals[i], comparator);
716
717                        similarTasks1.dump(System.err);
718
719                        similarTasks2.dump(System.err);
720                    }
721                }
722            }
723           
724            synchronized (unfinishedRunnables) {
725                mostSimilarTasksList.addAll(allMostSimilarTasks);
726                comparisonCounter += counter;
727                effectiveComparisonCounter += effectiveComparisons;
728               
729                for (int i = 0; i < unfinishedRunnables.size(); i++) {
730                    if (unfinishedRunnables.get(i) == this) {
731                        unfinishedRunnables.remove(i);
732                        unfinishedRunnables.notify();
733                    }
734                }
735            }
736        }
737
738        /**
739         *
740         */
741        private boolean isBasicallySimilar(TaskTraversal traversal1, TaskTraversal traversal2) {
742            int length1 = traversal1.size();
743            int length2 = traversal2.size();
744            int maxLength = Math.max(length1, length2);
745            int lengthDiff = 100 * Math.abs(length1 - length2) / maxLength;
746           
747            if (lengthDiff > MAX_DIFF_LEVEL) {
748                return false;
749            }
750            else {
751                ITask firstTask1 = traversal1.get(0);
752                ITask firstTask2 = traversal2.get(0);
753               
754                if (!comparator.equals(firstTask1, firstTask2)) {
755                    return false;
756                }
757               
758                ITask lastTask1 = traversal1.get(traversal1.size() - 1);
759                ITask lastTask2 = traversal2.get(traversal2.size() - 1);
760               
761                if (!comparator.equals(lastTask1, lastTask2)) {
762                    return false;
763                }
764               
765                if (TaskTreeUtils.isChild(traversal1.getTask(), traversal2.getTask()) ||
766                    TaskTreeUtils.isChild(traversal2.getTask(), traversal1.getTask()))
767                {
768                    return false;
769                }
770               
771                return true;
772            }
773        }
774
775        /**
776         *
777         */
778        private SimilarTasks getSimilarTasksToPrefer(SimilarTasks similarTasks1,
779                                                     SimilarTasks similarTasks2)
780        {
781            if (similarTasks2.getDiffLevel() > similarTasks1.getDiffLevel()) {
782                return similarTasks2;
783            }
784            else if (similarTasks1.getDiffLevel() > similarTasks2.getDiffLevel()) {
785                return similarTasks1;
786            }
787            else if (similarTasks1.getDiffLevel() == similarTasks2.getDiffLevel()) {
788                ITask first = similarTasks1.getLeftHandSide();
789                ITask second = similarTasks2.getLeftHandSide();
790               
791                long valFirst = getTaskMetric(first, TaskMetric.EVENT_COVERAGE);
792                long valSecond = getTaskMetric(second, TaskMetric.EVENT_COVERAGE);
793
794                if (valSecond > valFirst) {
795                    return similarTasks2;
796                }
797                else if (valSecond < valFirst) {
798                    return similarTasks1;
799                }
800
801                // no of covered events is equal, try to distinguish by count
802
803                valFirst = getTaskMetric(first, TaskMetric.COUNT);
804                valSecond = getTaskMetric(second, TaskMetric.COUNT);
805
806                if (valSecond > valFirst) {
807                    return similarTasks2;
808                }
809                else if (valSecond < valFirst) {
810                    return similarTasks1;
811                }
812
813                // count is equal, try to distinguish by depth
814
815                valFirst = getTaskMetric(first, TaskMetric.DEPTH);
816                valSecond = getTaskMetric(second, TaskMetric.DEPTH);
817
818                if (valSecond < valFirst) {
819                    return similarTasks2;
820                }
821                else if (valSecond > valFirst) {
822                    return similarTasks1;
823                }
824
825                // no of covered events is equal, try to distinguish by count
826
827                valFirst = cumulateTaskMetric(first, TaskMetric.COUNT);
828                valSecond = cumulateTaskMetric(second, TaskMetric.COUNT);
829
830                if (valSecond > valFirst) {
831                    return similarTasks2;
832                }
833                else if (valSecond < valFirst) {
834                    return similarTasks1;
835                }
836
837                // depth is equal. Calculate for both the similarity
838                // based on which the merging will take place
839                SimilarTasks tmp =
840                    SimilarTasks.getMergableLevelOfSimilarity(similarTasks1, comparator);
841               
842                valFirst = tmp != null ? tmp.getDiffLevel() : Integer.MAX_VALUE;
843               
844                tmp = SimilarTasks.getMergableLevelOfSimilarity(similarTasks2, comparator);
845                valSecond = tmp != null ? tmp.getDiffLevel() : Integer.MAX_VALUE;
846
847                if (valSecond < valFirst) {
848                    return similarTasks2;
849                }
850                else if (valSecond > valFirst) {
851                    return similarTasks1;
852                }
853
854                valFirst = getFirstTimestamp(first);
855                valSecond = getFirstTimestamp(second);
856               
857                if (valSecond > valFirst) {
858                    return similarTasks1;
859                }
860                else if (valSecond < valFirst) {
861                    return similarTasks2;
862                }
863
864                similarTasks1.dump(System.out);
865                similarTasks2.dump(System.out);
866
867                throw new RuntimeException
868                    ("several tasks are similar so that it is undecidable which to merge first");
869            }
870           
871            return null;
872        }
873
874        /**
875         *
876         */
877        private int getTaskMetric(ITask task, TaskMetric metric) {
878            ITaskInfo info = taskModel.getTaskInfo(task);
879            return info.getMeasureValue(metric);
880        }
881       
882        /**
883         * convenience method to get the cumulative value of a task metric
884         */
885        private int cumulateTaskMetric(ITask            task,
886                                       final TaskMetric metric)
887        {
888            final int[] value = new int[1];
889            value[0] = 0;
890           
891            DefaultTaskTraversingVisitor visitor = new DefaultTaskTraversingVisitor() {
892                @Override
893                public void visit(IStructuringTemporalRelationship relationship) {
894                    value[0] += taskModel.getTaskInfo(relationship).getMeasureValue(metric);
895                    super.visit(relationship);
896                }
897            };
898           
899            task.accept(visitor);
900           
901            return value[0];
902        }
903
904        /**
905         *
906         */
907        private long getFirstTimestamp(ITask task) {
908            long timestamp = Long.MAX_VALUE;
909           
910            final List<IEventTaskInstance> eventTaskInstances = new LinkedList<>();
911            for (ITaskInstance instance : task.getInstances()) {
912                eventTaskInstances.clear();
913               
914                instance.accept(new DefaultTaskInstanceTraversingVisitor() {
915
916                    @Override
917                    public void visit(IEventTaskInstance eventTaskInstance) {
918                        eventTaskInstances.add(eventTaskInstance);
919                    }
920
921                    @Override
922                    public void visit(ITaskInstanceList taskInstanceList) {
923                        for (ITaskInstance child : taskInstanceList) {
924                            if (eventTaskInstances.size() > 0) {
925                                break;
926                            }
927                           
928                            child.accept(this);
929                        }
930                    }
931                   
932                });
933               
934                if (eventTaskInstances.size() > 0) {
935                    long newTimestamp =
936                        ((IEventTaskInstance) eventTaskInstances.get(0)).getEvent().getTimestamp();
937                   
938                    if (timestamp > newTimestamp) {
939                        timestamp = newTimestamp;
940                    }
941                }
942            }
943           
944            return timestamp;
945        }
946       
947        /* (non-Javadoc)
948         * @see java.lang.Object#toString()
949         */
950        @Override
951        public String toString() {
952            return "CompareThread(" + leftHandTraversals.length + ", " +
953                rightHandTraversals.length + ")";
954        }
955
956    }
957}
Note: See TracBrowser for help on using the repository browser.