source: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/SequenceForTaskDetectionRule.java @ 1337

Last change on this file since 1337 was 1337, checked in by pharms, 10 years ago
  • solved problem with iteration detection, that caused detected iterations at the end of a session to be filled with iterated events of the next session(s).
File size: 38.1 KB
RevLine 
[1113]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
[1107]15package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
16
[1285]17import java.util.HashMap;
18import java.util.HashSet;
[1107]19import java.util.Iterator;
20import java.util.LinkedList;
21import java.util.List;
[1285]22import java.util.Map;
23import java.util.Set;
24import java.util.logging.Level;
[1107]25
[1146]26import de.ugoe.cs.autoquest.tasktrees.taskequality.TaskEquality;
[1127]27import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
[1294]28import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance;
[1127]29import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
[1294]30import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance;
[1107]31import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
[1294]32import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance;
[1146]33import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
34import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskBuilder;
35import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskFactory;
36import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
37import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceList;
38import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
[1256]39import de.ugoe.cs.autoquest.usageprofiles.SymbolMap;
[1119]40import de.ugoe.cs.autoquest.usageprofiles.TrieProcessor;
[1129]41import de.ugoe.cs.util.StopWatch;
[1285]42import de.ugoe.cs.util.console.Console;
[1107]43
44/**
45 * <p>
46 * TODO comment
47 * </p>
48 *
49 * @author Patrick Harms
50 */
[1146]51class SequenceForTaskDetectionRule implements ISessionScopeRule {
[1107]52   
53    /**
54     * <p>
[1146]55     * the task factory to be used for creating substructures for the temporal
[1107]56     * relationships identified during rule
57     * </p>
58     */
[1146]59    private ITaskFactory taskFactory;
[1107]60    /**
61     * <p>
[1146]62     * the task builder to be used for creating substructures for the temporal relationships
[1107]63     * identified during rule application
64     * </p>
65     */
[1146]66    private ITaskBuilder taskBuilder;
[1107]67
68    /**
69     * <p>
[1285]70     * the task comparator to be used for comparing tasks for preparation
[1107]71     * </p>
72     */
[1285]73    private TaskHandlingStrategy preparationTaskHandlingStrategy;
74   
[1107]75    /**
76     * <p>
[1285]77     * the task comparator to be used for comparing tasks during iteration detection an trie
78     * generation
79     * </p>
80     */
81    private TaskHandlingStrategy identityTaskHandlingStrategy;;
82   
83    /**
84     * <p>
[1146]85     * instantiates the rule and initializes it with a task equality rule manager and the minimal
86     * task equality identified sublist must have to consider them as iterated.
[1107]87     * </p>
88     */
[1189]89    SequenceForTaskDetectionRule(TaskEquality minimalTaskEquality,
90                                 ITaskFactory taskFactory,
91                                 ITaskBuilder taskBuilder)
[1107]92    {
[1146]93        this.taskFactory = taskFactory;
94        this.taskBuilder = taskBuilder;
[1107]95       
[1285]96        this.preparationTaskHandlingStrategy = new TaskHandlingStrategy(minimalTaskEquality);
97        this.identityTaskHandlingStrategy = new TaskHandlingStrategy(TaskEquality.IDENTICAL);
[1107]98    }
99
[1119]100    /* (non-Javadoc)
101     * @see java.lang.Object#toString()
102     */
103    @Override
104    public String toString() {
[1127]105        return "SequenceForTaskDetectionRule";
[1119]106    }
107
[1146]108    /* (non-Javadoc)
109     * @see de.ugoe.cs.autoquest.tasktrees.temporalrelation.ISessionScopeRule#apply(java.util.List)
[1107]110     */
111    @Override
[1146]112    public RuleApplicationResult apply(List<IUserSession> sessions) {
[1127]113        RuleApplicationData appData = new RuleApplicationData(sessions);
[1337]114       
[1127]115        // this is the real rule application. Loop while something is replaced.
[1256]116        harmonizeEventTaskInstancesModel(appData);
[1107]117        do {
[1127]118            System.out.println();
[1107]119           
[1127]120            appData.getStopWatch().start("whole loop");
121            detectAndReplaceIterations(appData);
[1133]122
[1127]123            appData.getStopWatch().start("task replacement");
124            detectAndReplaceTasks(appData);
125            appData.getStopWatch().stop("task replacement");
126            appData.getStopWatch().stop("whole loop");
[1107]127           
[1146]128            //((TaskTreeNodeComparator) taskComparator).getStopWatch().dumpStatistics(System.out);
129            //((TaskTreeNodeComparator) taskComparator).getStopWatch().reset();
[1127]130           
131            appData.getStopWatch().dumpStatistics(System.out);
132            appData.getStopWatch().reset();
133           
[1107]134        }
[1146]135        while (appData.detectedAndReplacedTasks());
[1107]136       
[1146]137        System.out.println
138            ("created " + appData.getResult().getNewlyCreatedTasks().size() +
139             " new tasks and " + appData.getResult().getNewlyCreatedTaskInstances().size() +
140             " appropriate instances\n");
[1107]141       
[1146]142        if ((appData.getResult().getNewlyCreatedTasks().size() > 0) ||
143            (appData.getResult().getNewlyCreatedTaskInstances().size() > 0))
144        {
[1127]145            appData.getResult().setRuleApplicationStatus(RuleApplicationStatus.FINISHED);
[1119]146        }
147       
[1127]148        return appData.getResult();
149    }
150
151    /**
[1256]152     * <p>
153     * TODO: comment
154     * </p>
155     *
[1127]156     * @param appData
157     */
[1256]158    private void harmonizeEventTaskInstancesModel(RuleApplicationData appData) {
[1285]159        Console.traceln(Level.INFO, "harmonizing task model of event task instances");
[1256]160        appData.getStopWatch().start("harmonizing event tasks");
[1285]161
162        SymbolMap<ITaskInstance, ITask> uniqueTasks =
163            preparationTaskHandlingStrategy.createSymbolMap();
[1294]164        TaskInstanceComparator comparator = preparationTaskHandlingStrategy.getTaskComparator();
[1256]165       
166        int unifiedTasks = 0;
[1285]167        ITask task;
[1256]168        List<IUserSession> sessions = appData.getSessions();
169        for (IUserSession session : sessions) {
[1285]170            Console.traceln(Level.FINE, "handling session " + session);
[1256]171            for (ITaskInstance taskInstance : session) {
[1285]172                task = uniqueTasks.getValue(taskInstance);
[1256]173               
174                if (task == null) {
[1285]175                    uniqueTasks.addSymbol(taskInstance, taskInstance.getTask());
[1256]176                }
177                else {
178                    taskBuilder.setTask(taskInstance, task);
179                    unifiedTasks++;
180                }
181            }
[1285]182           
183            comparator.clearBuffers();
[1256]184        }
185       
186        appData.getStopWatch().stop("harmonizing event tasks");
[1285]187        Console.traceln(Level.INFO, "harmonized " + unifiedTasks + " task occurrences (still " +
188                        uniqueTasks.size() + " different tasks)");
189
[1256]190        appData.getStopWatch().dumpStatistics(System.out);
[1285]191        appData.getStopWatch().reset();
[1256]192    }
193
194    /**
195     * @param appData
196     */
[1127]197    private void detectAndReplaceIterations(RuleApplicationData appData) {
[1285]198        Console.traceln(Level.FINE, "detecting iterations");
[1127]199        appData.getStopWatch().start("detecting iterations");
[1119]200       
[1146]201        List<IUserSession> sessions = appData.getSessions();
[1127]202       
[1285]203        Set<ITask> iteratedTasks = searchIteratedTasks(sessions);
[1146]204       
[1285]205        if (iteratedTasks.size() > 0) {
206            replaceIterationsOf(iteratedTasks, sessions, appData);
[1127]207        }
208       
209        appData.getStopWatch().stop("detecting iterations");
[1285]210        Console.traceln(Level.INFO, "replaced " + iteratedTasks.size() + " iterated tasks");
[1127]211    }
212
213    /**
[1146]214     *
[1127]215     */
[1285]216    private Set<ITask> searchIteratedTasks(List<IUserSession> sessions) {
217        Set<ITask> iteratedTasks = new HashSet<ITask>();
[1146]218        for (IUserSession session : sessions) {
219            for (int i = 0; i < (session.size() - 1); i++) {
[1285]220                // we prepared the task instances to refer to unique tasks, if they are treated
221                // as equal. Therefore, we just compare the identity of the tasks of the task
222                // instances
223                if (session.get(i).getTask() == session.get(i + 1).getTask()) {
224                    iteratedTasks.add(session.get(i).getTask());
[1146]225                }
226            }
227        }
228       
[1285]229        return iteratedTasks;
[1146]230    }
231
232    /**
233     *
234     */
[1285]235    private void replaceIterationsOf(Set<ITask>          iteratedTasks,
[1146]236                                     List<IUserSession>  sessions,
237                                     RuleApplicationData appData)
[1127]238    {
[1285]239        Map<ITask, IIteration> iterations = new HashMap<ITask, IIteration>();
[1294]240        Map<IIteration, List<IIterationInstance>> iterationInstances =
241                new HashMap<IIteration, List<IIterationInstance>>();
[1285]242           
243        for (ITask iteratedTask : iteratedTasks) {
244            IIteration iteration = taskFactory.createNewIteration();
245            iterations.put(iteratedTask, iteration);
[1294]246            iterationInstances.put(iteration, new LinkedList<IIterationInstance>());
[1285]247        }
248       
[1337]249        IIterationInstance iterationInstance;
[1127]250       
[1146]251        for (IUserSession session : sessions) {
252            int index = 0;
[1337]253            iterationInstance = null;
254
[1146]255            while (index < session.size()) {
[1285]256                // we prepared the task instances to refer to unique tasks, if they are treated
257                // as equal. Therefore, we just compare the identity of the tasks of the task
258                // instances
259                ITask currentTask = session.get(index).getTask();
260                IIteration iteration = iterations.get(currentTask);
261                if (iteration != null) {
262                    if ((iterationInstance == null) || (iterationInstance.getTask() != iteration))
263                    {
[1146]264                        iterationInstance = taskFactory.createNewTaskInstance(iteration);
[1285]265                        iterationInstances.get(iteration).add(iterationInstance);
[1146]266                        taskBuilder.addTaskInstance(session, index, iterationInstance);
267                        index++;
268                    }
269                   
270                    taskBuilder.addChild(iterationInstance, session.get(index));
271                    taskBuilder.removeTaskInstance(session, index);
272                }
273                else {
274                    if (iterationInstance != null) {
275                        iterationInstance = null;
276                    }
277                    index++;
278                }
279            }
280        }
281       
[1294]282        for (Map.Entry<IIteration, List<IIterationInstance>> entry : iterationInstances.entrySet())
283        {
[1285]284            harmonizeIterationInstancesModel(entry.getKey(), entry.getValue());
285        }
[1146]286    }
[1127]287
[1146]288    /**
289     *
290     */
[1294]291    private void harmonizeIterationInstancesModel(IIteration               iteration,
292                                                  List<IIterationInstance> iterationInstances)
[1146]293    {
294        List<ITask> iteratedTaskVariants = new LinkedList<ITask>();
[1294]295        TaskInstanceComparator comparator = preparationTaskHandlingStrategy.getTaskComparator();
[1146]296       
297        // merge the lexically different variants of iterated task to a unique list
[1294]298        for (IIterationInstance iterationInstance : iterationInstances) {
[1146]299            for (ITaskInstance executionVariant : iterationInstance) {
300                ITask candidate = executionVariant.getTask();
[1127]301           
[1146]302                boolean found = false;
303                for (ITask taskVariant : iteratedTaskVariants) {
[1285]304                    if (comparator.areLexicallyEqual(taskVariant, candidate)) {
[1146]305                        taskBuilder.setTask(executionVariant, taskVariant);
306                        found = true;
307                        break;
308                    }
[1127]309                }
[1146]310               
311                if (!found) {
312                    iteratedTaskVariants.add(candidate);
313                }
[1107]314            }
315        }
316       
[1146]317        // if there are more than one lexically different variant of iterated tasks, adapt the
318        // iteration model to be a selection of different variants. In this case also adapt
319        // the generated iteration instances to correctly contain selection instances. If there
320        // is only one variant of an iterated task, simply set this as the marked task of the
321        // iteration. In this case, the instances can be preserved as is
322        if (iteratedTaskVariants.size() > 1) {
323            ISelection selection = taskFactory.createNewSelection();
324           
325            for (ITask variant : iteratedTaskVariants) {
326                taskBuilder.addChild(selection, variant);
327            }
328           
329            taskBuilder.setMarkedTask(iteration, selection);
330           
[1294]331            for (IIterationInstance instance : iterationInstances) {
[1146]332                for (int i = 0; i < instance.size(); i++) {
[1294]333                    ISelectionInstance selectionInstance =
334                        taskFactory.createNewTaskInstance(selection);
335                    taskBuilder.setChild(selectionInstance, instance.get(i));
[1146]336                    taskBuilder.setTaskInstance(instance, i, selectionInstance);
337                }
338            }
339        }
340        else {
341            taskBuilder.setMarkedTask(iteration, iteratedTaskVariants.get(0));
342        }
[1107]343    }
344
345    /**
[1127]346     * @param appData
347     */
348    private void detectAndReplaceTasks(RuleApplicationData appData) {
[1285]349        Console.traceln(Level.FINE, "detecting and replacing tasks");
[1127]350        appData.getStopWatch().start("detecting tasks");
351       
352        getSequencesOccuringMostOften(appData);
353
354        appData.getStopWatch().stop("detecting tasks");
355        appData.getStopWatch().start("replacing tasks");
356       
357        replaceSequencesOccurringMostOften(appData);
358       
359        appData.getStopWatch().stop("replacing tasks");
[1285]360        Console.traceln(Level.INFO, "detected and replaced " + appData.getLastFoundTasks().size() +
361                        " tasks occuring " + appData.getLastFoundTasks().getOccurrenceCount() +
[1287]362                        " times");
[1127]363    }
364
365    /**
[1119]366     * @return
[1107]367     */
[1127]368    private void getSequencesOccuringMostOften(RuleApplicationData appData) {
[1285]369        Console.traceln(Level.FINE, "determining most prominent tasks");
[1119]370
[1127]371        Tasks tasks;
[1119]372        boolean createNewTrie = (appData.getLastTrie() == null) ||
[1146]373            appData.detectedAndReplacedTasks(); // tree has changed
[1107]374       
[1119]375        do {
376            if (createNewTrie) {
377                createNewTrie(appData);
378            }
[1107]379           
[1127]380            MaxCountAndLongestTasksFinder finder = new MaxCountAndLongestTasksFinder();
[1119]381            appData.getLastTrie().process(finder);
382       
[1127]383            tasks = finder.getFoundTasks();
[1119]384           
385            createNewTrie = false;
386           
[1146]387            for (List<ITaskInstance> task : tasks) {
[1119]388                if (task.size() >= appData.getTrainedSequenceLength()) {
389                    // Trie must be recreated with a longer sequence length to be sure that
390                    // the found sequences occurring most often are found in their whole length
391                    appData.setTrainedSequenceLength(appData.getTrainedSequenceLength() + 1);
392                    createNewTrie = true;
393                    break;
394                }
[1107]395            }
396        }
[1119]397        while (createNewTrie);
398       
[1256]399        // create a normal trie for comparison
400        /*System.out.println("creating test trie for comparison");
401       
402        appData.getStopWatch().start("testtrie");
[1285]403        Trie<ITaskInstance> trie = new Trie<ITaskInstance>(identityTaskComparator);
[1256]404       
405        for (IUserSession session : appData.getSessions()) {
406            trie.train(session.getExecutedTasks(), appData.getTrainedSequenceLength());
407        }
408       
409        appData.getStopWatch().stop("testtrie");
410       
411        MaxCountAndLongestTasksFinder finder = new MaxCountAndLongestTasksFinder();
412        trie.process(finder);
413
414        boolean allTasksEqual = finder.getFoundTasks().size() == tasks.size();
415       
416        allTasksEqual &= finder.getFoundTasks().occurrenceCount == tasks.occurrenceCount;
417       
418        for (List<ITaskInstance> task1 : finder.getFoundTasks()) {
419            boolean foundTask = false;
420            for (List<ITaskInstance> task2 : tasks) {
421                boolean tasksEqual = false;
422                if (task1.size() == task2.size()) {
423                    tasksEqual = true;
424                    for (int i = 0; i < task1.size(); i++) {
[1285]425                        if (!identityTaskComparator.equals(task1.get(i).getTask(), task2.get(i).getTask()))
[1256]426                        {
427                            tasksEqual = false;
428                        }
429                    }
430                }
431               
432                if (tasksEqual) {
433                    foundTask = true;
434                    break;
435                }
436            }
437           
438            if (!foundTask) {
439                System.out.println("different is " + task1);
440                allTasksEqual = false;
441                break;
442            }
443        }
444       
445        if (!allTasksEqual) {
446            System.out.println(finder.getFoundTasks());
447            System.out.println(tasks);
448           
449            throw new IllegalArgumentException("both tries calculated different subsequences");
[1285]450        }
[1256]451       
452        /*TrieStatisticsDumper dumper = new TrieStatisticsDumper();
453        appData.getLastTrie().process(dumper);
454        dumper.dumpCounters();*/
455       
[1127]456        appData.setLastFoundTasks(tasks);
457
[1285]458        Console.traceln(Level.FINE, "found " + appData.getLastFoundTasks().size() + " tasks " +
459                        "occurring " + appData.getLastFoundTasks().getOccurrenceCount() + " times");
[1107]460    }
461
462    /**
[1119]463     * @param parent
464     * @return
[1107]465     */
[1127]466    private void createNewTrie(RuleApplicationData appData) {
[1285]467        Console.traceln(Level.FINER, "training trie with a maximum sequence length of " +
468                        appData.getTrainedSequenceLength());
[1119]469
470        appData.getStopWatch().start("training trie");
[1285]471       
472        // we prepared the task instances to refer to unique tasks, if they are treated
473        // as equal. Therefore, we just compare the identity of the tasks of the task
474        // instances
475        appData.setLastTrie(new TaskInstanceTrie(identityTaskHandlingStrategy));
[1119]476   
[1256]477        appData.getLastTrie().trainSessions
478            (appData.getSessions(), appData.getTrainedSequenceLength());
[1127]479       
[1119]480        appData.getStopWatch().stop("training trie");
481    }
482
483    /**
[1127]484     * @param appData
485     */
486    private void replaceSequencesOccurringMostOften(RuleApplicationData appData) {
[1146]487        appData.detectedAndReplacedTasks(false);
[1127]488
489        if ((appData.getLastFoundTasks().size() > 0) &&
490            (appData.getLastFoundTasks().getOccurrenceCount() > 1))
491        {
[1285]492            Console.traceln(Level.FINER, "replacing tasks occurrences");
[1127]493
[1146]494            for (List<ITaskInstance> task : appData.getLastFoundTasks()) {
495                ISequence sequence = taskFactory.createNewSequence();
496               
[1285]497                Console.traceln(Level.FINEST, "replacing " + sequence.getId() + ": " + task);
[1127]498
[1294]499                List<ISequenceInstance> sequenceInstances =
[1146]500                    replaceTaskOccurrences(task, appData.getSessions(), sequence);
[1119]501               
[1146]502                harmonizeSequenceInstancesModel(sequence, sequenceInstances,task.size());
[1285]503                appData.detectedAndReplacedTasks
504                    (appData.detectedAndReplacedTasks() || (sequenceInstances.size() > 0));
[1127]505
[1146]506                if (sequenceInstances.size() < appData.getLastFoundTasks().getOccurrenceCount()) {
[1285]507                    Console.traceln(Level.FINE, sequence.getId() + ": replaced task only " +
508                                    sequenceInstances.size() + " times instead of expected " +
509                                    appData.getLastFoundTasks().getOccurrenceCount());
[1127]510                }
511            }
512        }
513       
514    }
515
516    /**
[1146]517     *
518     */
[1294]519    private void harmonizeSequenceInstancesModel(ISequence               sequence,
520                                                 List<ISequenceInstance> sequenceInstances,
521                                                 int                     sequenceLength)
[1146]522    {
[1294]523        TaskInstanceComparator comparator = preparationTaskHandlingStrategy.getTaskComparator();
[1146]524       
525        // ensure for each subtask that lexically different variants are preserved
526        for (int subTaskIndex = 0; subTaskIndex < sequenceLength; subTaskIndex++) {
527            List<ITask> subTaskVariants = new LinkedList<ITask>();
528           
[1294]529            for (ISequenceInstance sequenceInstance : sequenceInstances) {
[1146]530                ITask candidate = sequenceInstance.get(subTaskIndex).getTask();
531               
532                boolean found = false;
533               
534                for (int i = 0; i < subTaskVariants.size(); i++) {
[1285]535                    if (comparator.areLexicallyEqual(subTaskVariants.get(i), candidate)) {
[1146]536                        taskBuilder.setTask
537                            (sequenceInstance.get(subTaskIndex), subTaskVariants.get(i));
538                       
539                        found = true;
540                        break;
541                    }
542                }
543               
544                if (!found) {
545                    subTaskVariants.add(candidate);
546                }
547            }
548           
549            // if there are more than one lexically different variant of the sub task at
550            // the considered position, adapt the sequence model at that position to have
551            // a selection of the different variants. In this case also adapt the
552            // generated sequence instances to correctly contain selection instances. If
553            // there is only one variant of sub tasks at the given position, simply set
554            // this variant as the sub task of the selection. In this case, the instances
555            // can be preserved as is
556            if (subTaskVariants.size() > 1) {
557                ISelection selection = taskFactory.createNewSelection();
558               
559                for (ITask variant : subTaskVariants) {
560                    taskBuilder.addChild(selection, variant);
561                }
562               
563                taskBuilder.addChild(sequence, selection);
564               
[1294]565                for (ISequenceInstance instance : sequenceInstances) {
566                    ISelectionInstance selectionInstance =
[1146]567                        taskFactory.createNewTaskInstance(selection);
[1294]568                    taskBuilder.setChild(selectionInstance, instance.get(subTaskIndex));
[1146]569                    taskBuilder.setTaskInstance(instance, subTaskIndex, selectionInstance);
570                }
571            }
572            else if (subTaskVariants.size() == 1) {
573                taskBuilder.addChild(sequence, subTaskVariants.get(0));
574            }
575        }
576    }
577
578    /**
[1127]579     * @param tree
580     */
[1294]581    private List<ISequenceInstance> replaceTaskOccurrences(List<ITaskInstance> task,
582                                                           List<IUserSession>  sessions,
583                                                           ISequence           temporalTaskModel)
[1127]584    {
[1294]585        List<ISequenceInstance> sequenceInstances = new LinkedList<ISequenceInstance>();
[1146]586       
587        for (IUserSession session : sessions) {
[1127]588            int index = -1;
[1119]589               
[1127]590            do {
[1146]591                index = getSubListIndex(session, task, ++index);
[1127]592
593                if (index > -1) {
[1146]594                    sequenceInstances.add
595                        (RuleUtils.createNewSubSequenceInRange
596                             (session, index, index + task.size() - 1, temporalTaskModel,
597                              taskFactory, taskBuilder));
[1107]598                }
599            }
[1127]600            while (index > -1);
[1107]601        }
[1146]602       
603        return sequenceInstances;
[1107]604    }
605
606    /**
[1146]607     * @param trie
608     * @param object
[1127]609     * @return
610     */
[1146]611    private int getSubListIndex(ITaskInstanceList   list,
612                                List<ITaskInstance> subList,
613                                int                 startIndex)
[1127]614    {
[1146]615        boolean matchFound;
616        int result = -1;
[1127]617       
[1146]618        for (int i = startIndex; i <= list.size() - subList.size(); i++) {
619            matchFound = true;
[1127]620           
[1146]621            for (int j = 0; j < subList.size(); j++) {
[1285]622                // we prepared the task instances to refer to unique tasks, if they are treated
623                // as equal. Therefore, we just compare the identity of the tasks of the task
624                // instances
625                if (list.get(i + j).getTask() != subList.get(j).getTask()) {
[1146]626                    matchFound = false;
627                    break;
628                }
[1127]629            }
630           
[1146]631            if (matchFound) {
632                result = i;
633                break;
634            }
[1127]635        }
[1107]636       
[1146]637        return result;
[1107]638    }
639
640    /**
641     * @param trie
642     * @param object
643     * @return
644     */
[1146]645    private int getSubListIndex(List<ITaskInstance> list,
646                                List<ITaskInstance> subList,
[1107]647                                int                 startIndex)
648    {
649        boolean matchFound;
650        int result = -1;
651       
652        for (int i = startIndex; i <= list.size() - subList.size(); i++) {
653            matchFound = true;
654           
655            for (int j = 0; j < subList.size(); j++) {
[1285]656                // we prepared the task instances to refer to unique tasks, if they are treated
657                // as equal. Therefore, we just compare the identity of the tasks of the task
658                // instances
659                if (list.get(i + j) != subList.get(j)) {
[1107]660                    matchFound = false;
661                    break;
662                }
663            }
664           
665            if (matchFound) {
666                result = i;
667                break;
668            }
669        }
670       
671        return result;
672    }
673   
[1119]674    /**
675     * @author Patrick Harms
676     */
[1146]677    private class MaxCountAndLongestTasksFinder implements TrieProcessor<ITaskInstance> {
[1119]678       
679        /**
680         *
681         */
682        private int currentCount;
683       
684        /**
685         *
686         */
[1146]687        private List<List<ITaskInstance>> foundTasks = new LinkedList<List<ITaskInstance>>();
[1119]688
689        /**
690         *
691         */
[1127]692        public MaxCountAndLongestTasksFinder() {
[1119]693            super();
694            this.currentCount = 0;
695        }
696
697        /* (non-Javadoc)
698         * @see de.ugoe.cs.autoquest.usageprofiles.TrieProcessor#process(java.util.List, int)
699         */
700        @Override
[1146]701        public TrieProcessor.Result process(List<ITaskInstance> foundTask, int count) {
702            if (foundTask.size() < 2) {
703                // ignore single tasks
[1119]704                return TrieProcessor.Result.CONTINUE;
705            }
706           
707            if (count < 2) {
708                // ignore singular occurrences
709                return TrieProcessor.Result.SKIP_NODE;
710            }
711
712            if (this.currentCount > count) {
[1146]713                // ignore this children of this task, as they may have only smaller counts than
[1127]714                // the already found tasks
[1119]715                return TrieProcessor.Result.SKIP_NODE;
716            }
717           
718            if (this.currentCount < count) {
[1127]719                // the provided task occurs more often that all tasks found so far.
720                // clear all found tasks and use the new count as the one searched for
721                foundTasks.clear();
[1119]722                this.currentCount = count;
723            }
724           
725            if (this.currentCount == count) {
[1127]726                // the task is of interest. Sort it into the other found tasks so that
[1119]727                // the longest come first
728                boolean added = false;
[1127]729                for (int i = 0; i < foundTasks.size(); i++) {
[1146]730                    if (foundTasks.get(i).size() < foundTask.size()) {
[1119]731                        // defensive copy
[1146]732                        foundTasks.add(i, new LinkedList<ITaskInstance>(foundTask)); // defensive copy
[1119]733                        added = true;
734                        break;
735                    }
736                }
737               
738                if (!added) {
[1146]739                    foundTasks.add(new LinkedList<ITaskInstance>(foundTask)); // defensive copy
[1119]740                }
741            }
742           
743            return TrieProcessor.Result.CONTINUE;
744        }
745
746        /**
[1133]747         *  @return
[1119]748         */
[1127]749        public Tasks getFoundTasks() {
[1119]750            removePermutationsOfShorterTasks();
[1127]751            return new Tasks(currentCount, foundTasks);
[1119]752        }
753
754        /**
755         *
756         */
757        private void removePermutationsOfShorterTasks() {
758            // now iterate the sorted list and for each task remove all other tasks that are shorter
759            // (i.e. come later in the sorted list) and that represent a subpart of the task
[1127]760            for (int i = 0; i < foundTasks.size(); i++) {
761                for (int j = i + 1; j < foundTasks.size();) {
762                    if (foundTasks.get(j).size() < foundTasks.get(i).size()) {
[1119]763                        // found a task that is a potential subtask. Check for this and remove the
764                        // subtask if needed
[1146]765                        List<ITaskInstance> longTask = foundTasks.get(i);
766                        List<ITaskInstance> shortTask = foundTasks.get(j);
[1119]767                       
768                        if (getSubListIndex(longTask, shortTask, 0) > -1) {
[1127]769                            foundTasks.remove(j);
[1119]770                        }
771                        else {
772                            j++;
773                        }
774                    }
775                    else {
776                        j++;
777                    }
778                }
779            }
780        }
781
782    }
783   
[1256]784//    /**
785//     * @author Patrick Harms
786//     */
787//    private class TrieStatisticsDumper implements TrieProcessor<ITaskInstance> {
788//       
789//        /**
790//         *
791//         */
792//        private Map<Integer, Integer> counters = new HashMap<Integer, Integer>();
793//       
794//        /* (non-Javadoc)
795//         * @see de.ugoe.cs.autoquest.usageprofiles.TrieProcessor#process(java.util.List, int)
796//         */
797//        @Override
798//        public TrieProcessor.Result process(List<ITaskInstance> subsequence, int count) {
799//            if (subsequence.size() == 1) {
800//                Integer value = counters.get(count);
801//               
802//                if (value == null) {
803//                    value = 0;
804//                }
805//               
806//                counters.put(count, value + 1);
807//               
808//                return TrieProcessor.Result.CONTINUE;
809//            }
810//            else {
811//                // ignore singular occurrences
812//                return TrieProcessor.Result.SKIP_NODE;
813//            }
814//        }
815//
816//        /**
817//         *  @return
818//         */
819//        public void dumpCounters() {
820//            int dumpedCounters = 0;
821//           
822//            int count = 1;
823//            while (dumpedCounters < counters.size()) {
824//                Integer value = counters.get(count++);
825//                if (value != null) {
826//                    System.out.println(value + " symbols occurred " + count + " times");
827//                    dumpedCounters++;
828//                }
829//            }
830//        }
831//
832//    }
833   
[1119]834    /**
835     *
836     */
[1133]837    private static class RuleApplicationData {
[1119]838       
839        /**
840         *
841         */
[1146]842        private List<IUserSession> sessions;
[1119]843       
844        /**
845         *
846         */
[1256]847        private TaskInstanceTrie lastTrie;
[1119]848       
849        /**
[1127]850         * default and minimum trained sequence length is 3
[1119]851         */
[1127]852        private int trainedSequenceLength = 3;
[1119]853       
854        /**
855         *
856         */
[1127]857        private Tasks lastFoundTasks = new Tasks(Integer.MAX_VALUE, null);
[1119]858       
859        /**
[1127]860         *
[1119]861         */
[1146]862        private boolean detectedAndReplacedTasks;
[1119]863       
864        /**
865         *
866         */
[1127]867        private RuleApplicationResult result = new RuleApplicationResult();
868       
869        /**
870         *
871         */
[1119]872        private StopWatch stopWatch = new StopWatch();
873       
874        /**
875         *
876         */
[1146]877        private RuleApplicationData(List<IUserSession> sessions) {
[1127]878            this.sessions = sessions;
[1119]879        }
880
881        /**
[1127]882         * @return the tree
[1119]883         */
[1146]884        private List<IUserSession> getSessions() {
[1127]885            return sessions;
[1119]886        }
887
888        /**
[1127]889         * @param lastTrie the lastTrie to set
[1119]890         */
[1256]891        private void setLastTrie(TaskInstanceTrie lastTrie) {
[1127]892            this.lastTrie = lastTrie;
[1119]893        }
894
895        /**
896         * @return the lastTrie
897         */
[1256]898        private TaskInstanceTrie getLastTrie() {
[1119]899            return lastTrie;
900        }
901
902        /**
[1127]903         * @param trainedSequenceLength the trainedSequenceLength to set
904         */
905        private void setTrainedSequenceLength(int trainedSequenceLength) {
906            this.trainedSequenceLength = trainedSequenceLength;
907        }
908
909        /**
[1119]910         * @return the trainedSequenceLength
911         */
912        private int getTrainedSequenceLength() {
913            return trainedSequenceLength;
914        }
915
916        /**
[1127]917         * @param lastFoundSequences the lastFoundSequences to set
[1119]918         */
[1127]919        private void setLastFoundTasks(Tasks lastFoundSequences) {
920            this.lastFoundTasks = lastFoundSequences;
[1119]921        }
922
923        /**
[1127]924         * @return the lastFoundSequences
[1119]925         */
[1127]926        private Tasks getLastFoundTasks() {
927            return lastFoundTasks;
[1119]928        }
929
930        /**
931         *
932         */
[1146]933        private void detectedAndReplacedTasks(boolean detectedAndReplacedTasks) {
934            this.detectedAndReplacedTasks = detectedAndReplacedTasks;
[1119]935        }
936
937        /**
938         *
939         */
[1146]940        private boolean detectedAndReplacedTasks() {
941            return detectedAndReplacedTasks;
[1119]942        }
[1127]943       
944        /**
945         * @return the result
946         */
947        private RuleApplicationResult getResult() {
948            return result;
949        }
950
951        /**
952         * @return the stopWatch
953         */
954        private StopWatch getStopWatch() {
955            return stopWatch;
956        }
957
[1119]958    }
959   
960
961    /**
962     * @author Patrick Harms
963     */
[1146]964    private static class Tasks implements Iterable<List<ITaskInstance>> {
[1119]965       
966        /**
967         *
968         */
969        private int occurrenceCount;
970       
971        /**
972         *
973         */
[1146]974        private List<List<ITaskInstance>> sequences;
[1119]975
976        /**
977         * @param occurrenceCount
978         * @param sequences
979         */
[1146]980        private Tasks(int occurrenceCount, List<List<ITaskInstance>> sequences) {
[1119]981            super();
982            this.occurrenceCount = occurrenceCount;
983            this.sequences = sequences;
984        }
985
986        /**
987         * @return
988         */
989        private int getOccurrenceCount() {
990            return occurrenceCount;
991        }
992
993        /**
994         * @return
995         */
996        private int size() {
997            return this.sequences.size();
998        }
999
1000        /**
1001         *
1002         */
1003
1004        /* (non-Javadoc)
1005         * @see java.lang.Iterable#iterator()
1006         */
1007        @Override
[1146]1008        public Iterator<List<ITaskInstance>> iterator() {
[1119]1009            return this.sequences.iterator();
1010        }
1011
[1146]1012        /* (non-Javadoc)
[1256]1013         * @see java.lang.Object#toString()
[1146]1014         */
1015        @Override
[1256]1016        public String toString() {
1017            StringBuffer result = new StringBuffer();
1018            result.append(this.occurrenceCount);
1019            result.append(" occurrences:\n");
[1146]1020           
[1256]1021            for (List<ITaskInstance> task : sequences) {
1022                result.append(task);
1023                result.append("\n");
[1146]1024            }
1025           
[1256]1026            return result.toString();
[1146]1027        }
1028
1029    }
[1337]1030   
1031    // methods for internal testing
1032//    private void checkMatchingOfSessions(List<List<Event>>  flattenedSessions,
1033//                                         List<IUserSession> sessions,
1034//                                         String             when)
1035//    {
1036//        List<List<Event>> currentFlattenedSessions = flattenSessions(sessions);
1037//        if (flattenedSessions.size() != currentFlattenedSessions.size()) {
1038//            System.out.println("################## number of sessions changed after " + when);
1039//        }
1040//        else {
1041//            for (int i = 0; i < flattenedSessions.size(); i++) {
1042//                List<Event> expected = flattenedSessions.get(i);
1043//                List<Event> current = currentFlattenedSessions.get(i);
1044//           
1045//                if (expected.size() != current.size()) {
1046//                    System.out.println
1047//                        ("################## length of session " + i + " changed after " + when);
1048//                }
1049//                else {
1050//                    for (int j = 0; j < expected.size(); j++) {
1051//                        if (!expected.get(j).equals(current.get(j))) {
1052//                            System.out.println("################## event " + j + " of session " +
1053//                                               i + " changed after " + when);
1054//                        }
1055//                    }
1056//                }
1057//            }     
1058//        }
1059//    }
1060//
1061//    private List<List<Event>> flattenSessions(List<IUserSession> sessions) {
1062//        List<List<Event>> flattenedSessions = new ArrayList<List<Event>>();
1063//        for (IUserSession session : sessions) {
1064//            List<Event> flattenedUserSession = new ArrayList<Event>();
1065//            flatten(session, flattenedUserSession);
1066//            flattenedSessions.add(flattenedUserSession);
1067//        }
1068//
1069//        return flattenedSessions;
1070//    }
1071//
1072//    private void flatten(IUserSession iUserSession, List<Event> flattenedUserSession) {
1073//        for (ITaskInstance instance : iUserSession) {
1074//            flatten(instance, flattenedUserSession);
1075//        }
1076//    }
1077//
1078//    private void flatten(ITaskInstance instance, List<Event> flattenedUserSession) {
1079//        if (instance instanceof ITaskInstanceList) {
1080//            for (ITaskInstance child : (ITaskInstanceList) instance) {
1081//                flatten(child, flattenedUserSession);
1082//            }
1083//        }
1084//        else if (instance instanceof ISelectionInstance) {
1085//            flatten(((ISelectionInstance) instance).getChild(), flattenedUserSession);
1086//        }
1087//        else if (instance instanceof IOptionalInstance) {
1088//            flatten(((IOptionalInstance) instance).getChild(), flattenedUserSession);
1089//        }
1090//        else if (instance instanceof IEventTaskInstance) {
1091//            flattenedUserSession.add(((IEventTaskInstance) instance).getEvent());
1092//        }
1093//    }
1094
[1107]1095}
Note: See TracBrowser for help on using the repository browser.