// Copyright 2012 Georg-August-Universität Göttingen, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.ugoe.cs.autoquest.tasktrees.temporalrelation; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import de.ugoe.cs.autoquest.tasktrees.alignment.algorithms.AlignmentAlgorithm; import de.ugoe.cs.autoquest.tasktrees.alignment.algorithms.AlignmentAlgorithmFactory; import de.ugoe.cs.autoquest.tasktrees.alignment.algorithms.Match; import de.ugoe.cs.autoquest.tasktrees.alignment.algorithms.MatchOccurence; import de.ugoe.cs.autoquest.tasktrees.alignment.algorithms.NumberSequence; import de.ugoe.cs.autoquest.tasktrees.alignment.matrix.ObjectDistanceSubstitionMatrix; import de.ugoe.cs.autoquest.tasktrees.taskequality.TaskEquality; import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration; import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskBuilder; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskFactory; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession; import de.ugoe.cs.autoquest.usageprofiles.SymbolMap; import de.ugoe.cs.util.StopWatch; import de.ugoe.cs.util.console.Console; /** *

* This class implements the major rule for creating task trees based on a set * of recorded user sessions. For this, it first harmonizes all tasks. This * eases later comparison. Then it searches the sessions for iterations and * replaces them accordingly. Then it searches for sub sequences using alignment algorithms * For each found sub sequence, it replaces * the occurrences by creating appropriate {@link ISequence}s. Afterwards, again * searches for iterations and then again for sub sequences until no more * replacements are done. *

*

* * * @author Ralph Krimmel */ public class SequenceForTaskDetectionRuleAlignment implements ISessionScopeRule { /** * The Class RuleApplicationData. */ private static class RuleApplicationData implements Serializable { /** The Constant serialVersionUID. */ private static final long serialVersionUID = -7559657686755522960L; /** The number2task HashMap. Since we align the tasks just by their integer id, * we need this to convert them back to Tasks again*/ private final HashMap number2task; /** The unique tasks, keeps track about all unique tasks * TODO: We Actually just need number2task here, this structure can be * removed in the future.*/ private final HashSet uniqueTasks; /** The substitution matrix used by the alignment algorithm to be able to compare * distances of tasks */ private final ObjectDistanceSubstitionMatrix submat; /** HashMap for keeping track in which sequence which replacement has been performed. * Neccessary for updating the indices of other occurrences accordingly */ public HashMap> replacedOccurences; /** The list of all found matches */ public LinkedList matchseqs; /** The generated NumberSequences. * This is the integer representation of the user sessions */ private ArrayList numberseqs; /** The list of newly created tasks (of one iteration). */ private final LinkedList newTasks; /** The user sessions containing all EventTasks/Instances */ private final List sessions; /** True if we replaced something in the user sessions in one iteration. */ private boolean detectedAndReplacedTasks; /** The result we give autoquest back */ private final RuleApplicationResult result; /** Stop Watch to measure performance */ private final StopWatch stopWatch; /** * Instantiates a new rule application data. * * @param sessions The user sessions */ private RuleApplicationData(List sessions) { this.sessions = sessions; numberseqs = new ArrayList(); uniqueTasks = new HashSet(); number2task = new HashMap(); stopWatch = new StopWatch(); result = new RuleApplicationResult(); submat = new ObjectDistanceSubstitionMatrix(6, -3, false); newTasks = new LinkedList(); this.detectedAndReplacedTasks = true; } /** * Detected and replaced tasks. * * @return true, if successful */ private boolean detectedAndReplacedTasks() { return detectedAndReplacedTasks; } /** * Gets the matchseqs. * * @return the matchseqs */ public LinkedList getMatchseqs() { return matchseqs; } /** * Gets the new tasks. * * @return the new tasks */ public LinkedList getNewTasks() { return newTasks; } /** * Gets the number2task. * * @return the number2task HashMap */ private HashMap getNumber2Task() { return number2task; } /** * Gets the number sequences. * * @return the number sequences */ private ArrayList getNumberSequences() { return numberseqs; } /** * Gets the replaced occurrences. * * @return the replaced occurences */ public HashMap> getReplacedOccurrences() { return replacedOccurences; } /** * Gets the result. * * @return the result */ private RuleApplicationResult getResult() { return result; } /** * Gets the sessions. * * @return the UserSessions as List. */ private List getSessions() { return sessions; } /** * Gets the stop watch. * * @return the stopWatch */ private StopWatch getStopWatch() { return stopWatch; } /** * Gets the submat. * * @return the submat */ private ObjectDistanceSubstitionMatrix getSubmat() { return submat; } /** * Gets the unique tasks. * * @return the unique tasks */ private HashSet getUniqueTasks() { return uniqueTasks; } /** * New task created. * * @param task the task */ private void newTaskCreated(ITask task) { number2task.put(task.getId(), task); newTasks.add(task); } /** * Reset newly created tasks. */ synchronized private void resetNewlyCreatedTasks() { uniqueTasks.addAll(newTasks); newTasks.clear(); } /** * Sets the number sequences. * * @param numberseqs the new number sequences */ private void setNumberSequences(ArrayList numberseqs) { this.numberseqs = numberseqs; } /** * Sets the replaced occurences. * * @param replacedOccurences the replaced occurences */ public void setReplacedOccurences( HashMap> replacedOccurences) { this.replacedOccurences = replacedOccurences; } /** * Update substitution matrix. */ private void updateSubstitutionMatrix() { submat.update(getNewTasks()); resetNewlyCreatedTasks(); } } /** The n threads. */ public static int nThreads = Runtime.getRuntime().availableProcessors() - 1; /** The iteration. */ private int iteration = 0; /**

the task factory to be used for creating substructures for the temporal relationships identified during rul application

. */ private final ITaskFactory taskFactory; /**

the task builder to be used for creating substructures for the temporal relationships identified during rule application

. */ private final ITaskBuilder taskBuilder; /** *

* the task handling strategy to be used for comparing tasks for * preparation, i.e., before the tasks are harmonized *

*/ private final TaskHandlingStrategy preparationTaskHandlingStrategy; /** *

* instantiates the rule and initializes it with a task equality to be * considered when comparing tasks as well as a task factory and builder to * be used for creating task structures. *

* * @param minimalTaskEquality * the task equality to be considered when comparing tasks * @param taskFactory * the task factory to be used for creating substructures * @param taskBuilder * the task builder to be used for creating substructures */ SequenceForTaskDetectionRuleAlignment(TaskEquality minimalTaskEquality, ITaskFactory taskFactory, ITaskBuilder taskBuilder) { this.taskFactory = taskFactory; this.taskBuilder = taskBuilder; this.preparationTaskHandlingStrategy = new TaskHandlingStrategy( minimalTaskEquality); } /* * (non-Javadoc) * * @see * de.ugoe.cs.autoquest.tasktrees.temporalrelation.ISessionScopeRule#apply * (java.util.List) */ @Override public RuleApplicationResult apply(List sessions) { final RuleApplicationData appData = new RuleApplicationData(sessions); harmonizeEventTaskInstancesModel(appData); Console.traceln(Level.INFO, "generating substitution matrix from " + appData.getUniqueTasks().size() + " unique tasks"); appData.getStopWatch().start("substitution matrix"); appData.getSubmat().generate(appData.getUniqueTasks()); appData.getStopWatch().stop("substitution matrix"); Console.traceln(Level.INFO, "Starting main loop"); do { Console.traceln(Level.INFO, "Iteration Number: " + iteration); iteration++; appData.detectedAndReplacedTasks = false; appData.getStopWatch().start("whole loop"); detectAndReplaceIterations(appData); appData.getStopWatch().start("task replacement"); appData.updateSubstitutionMatrix(); detectAndReplaceTasks(appData); // appData.getStopWatch().stop("task replacement"); appData.getStopWatch().stop("whole loop"); appData.getStopWatch().dumpStatistics(System.out); appData.getStopWatch().reset(); } while (appData.detectedAndReplacedTasks()); Console.println("created " + appData.getResult().getNewlyCreatedTasks().size() + " new tasks and " + appData.getResult().getNewlyCreatedTaskInstances().size() + " appropriate instances\n"); if ((appData.getResult().getNewlyCreatedTasks().size() > 0) || (appData.getResult().getNewlyCreatedTaskInstances().size() > 0)) { appData.getResult().setRuleApplicationStatus( RuleApplicationStatus.FINISHED); } return appData.getResult(); } /** * Creates the number sequences. * * @param appData the app data * @return the array list */ private ArrayList createNumberSequences( RuleApplicationData appData) { final ArrayList result = new ArrayList(); for (int i = 0; i < appData.getSessions().size(); i++) { final IUserSession session = appData.getSessions().get(i); final NumberSequence templist = new NumberSequence(session.size()); for (int j = 0; j < session.size(); j++) { final ITaskInstance taskInstance = session.get(j); templist.getSequence()[j] = taskInstance.getTask().getId(); } // Each NumberSequence is identified by its id, beginning to count // at zero templist.setId(i); result.add(templist); } return result; } /** *

* searches for direct iterations of single tasks in all sequences and * replaces them with {@link IIteration}s, respectively appropriate * instances. Also all single occurrences of a task that is iterated * somewhen are replaced with iterations to have again an efficient way for * task comparisons. *

* * @param appData * the rule application data combining all data used for applying * this rule */ private void detectAndReplaceIterations(RuleApplicationData appData) { Console.traceln(Level.FINE, "detecting iterations"); appData.getStopWatch().start("detecting iterations"); final List sessions = appData.getSessions(); final Set iteratedTasks = searchIteratedTasks(sessions); if (iteratedTasks.size() > 0) { replaceIterationsOf(iteratedTasks, sessions, appData); } appData.getStopWatch().stop("detecting iterations"); Console.traceln(Level.INFO, "replaced " + iteratedTasks.size() + " iterated tasks"); } /** * Detect and replace tasks. * * @param appData the rule application data combining all data used for applying * this rule */ private void detectAndReplaceTasks(RuleApplicationData appData) { Console.traceln(Level.FINE, "detecting and replacing tasks"); appData.getStopWatch().start("detecting tasks"); // Create NumberSequences appData.setNumberSequences(this.createNumberSequences(appData)); // Generate pairwise alignments // appData.setMatchseqs(generatePairwiseAlignments(appData)); generatePairwiseAlignments(appData); // Searching each match in all other sessions, counting its occurences searchMatchesInAllSessions(appData); // Sort results to get the most occurring results Console.traceln(Level.INFO, "sorting " + appData.getMatchseqs().size() + " results"); final Comparator comparator = new Comparator() { @Override public int compare(Match m1, Match m2) { return m2.occurenceCount() - m1.occurenceCount(); } }; Collections.sort(appData.getMatchseqs(), comparator); appData.getStopWatch().stop("detecting tasks"); // Replace matches in the sessions Console.traceln(Level.INFO, "Replacing matches in sessions"); appData.getStopWatch().start("replacing tasks"); replaceMatches(appData); appData.getStopWatch().stop("replacing tasks"); } /** * Generate pairwise alignments. * * @param appData the app data */ private void generatePairwiseAlignments(RuleApplicationData appData) { final int numberSeqSize = appData.getNumberSequences().size(); appData.matchseqs = new LinkedList(); Console.traceln(Level.INFO, "generating pairwise alignments from " + numberSeqSize + " sessions with " + nThreads + " threads"); int newThreads = nThreads; if (numberSeqSize < nThreads) { newThreads = numberSeqSize; } final ExecutorService executor = Executors .newFixedThreadPool(newThreads); final int interval = numberSeqSize / newThreads; int rest = numberSeqSize % newThreads; for (int i = 0; i < (numberSeqSize - interval); i += interval) { int offset = 0; if (rest != 0) { offset = 1; rest--; } final int from = i; final int to = i + interval + offset; System.out.println("Creating thread for sessions " + from + " till " + to); final ParallelPairwiseAligner aligner = new ParallelPairwiseAligner( appData, from, to); executor.execute(aligner); } executor.shutdown(); try { executor.awaitTermination(2, TimeUnit.HOURS); } catch (final InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** *

* harmonizes the event task instances by unifying tasks. This is done, as * initially the event tasks being equal with respect to the considered task * equality are distinct objects. The comparison of these distinct objects * is more time consuming than comparing the object references. *

* * @param appData * the rule application data combining all data used for applying * this rule * @return Returns the unique tasks symbol map */ private void harmonizeEventTaskInstancesModel(RuleApplicationData appData) { Console.traceln(Level.INFO, "harmonizing task model of event task instances"); appData.getStopWatch().start("harmonizing event tasks"); final SymbolMap uniqueTasks = preparationTaskHandlingStrategy .createSymbolMap(); final TaskInstanceComparator comparator = preparationTaskHandlingStrategy .getTaskComparator(); int unifiedTasks = 0; ITask task; final List sessions = appData.getSessions(); for (int j = 0; j < sessions.size(); j++) { final IUserSession session = sessions.get(j); for (int i = 0; i < session.size(); i++) { final ITaskInstance taskInstance = session.get(i); task = uniqueTasks.getValue(taskInstance); if (task == null) { uniqueTasks.addSymbol(taskInstance, taskInstance.getTask()); appData.getUniqueTasks().add(taskInstance.getTask()); appData.getNumber2Task().put( taskInstance.getTask().getId(), taskInstance.getTask()); } else { taskBuilder.setTask(taskInstance, task); unifiedTasks++; } } comparator.clearBuffers(); } appData.getStopWatch().stop("harmonizing event tasks"); Console.traceln(Level.INFO, "harmonized " + unifiedTasks + " task occurrences (still " + appData.getUniqueTasks().size() + " different tasks)"); appData.getStopWatch().dumpStatistics(System.out); appData.getStopWatch().reset(); } /** *

* TODO clarify why this is done *

. * * @param iteration the iteration * @param iterationInstances the iteration instances */ private void harmonizeIterationInstancesModel(IIteration iteration, List iterationInstances) { final List iteratedTaskVariants = new LinkedList(); final TaskInstanceComparator comparator = preparationTaskHandlingStrategy .getTaskComparator(); // merge the lexically different variants of iterated task to a unique // list for (final IIterationInstance iterationInstance : iterationInstances) { for (final ITaskInstance executionVariant : iterationInstance) { final ITask candidate = executionVariant.getTask(); boolean found = false; for (final ITask taskVariant : iteratedTaskVariants) { if (comparator.areLexicallyEqual(taskVariant, candidate)) { taskBuilder.setTask(executionVariant, taskVariant); found = true; break; } } if (!found) { iteratedTaskVariants.add(candidate); } } } // if there are more than one lexically different variant of iterated // tasks, adapt the // iteration model to be a selection of different variants. In this case // also adapt // the generated iteration instances to correctly contain selection // instances. If there // is only one variant of an iterated task, simply set this as the // marked task of the // iteration. In this case, the instances can be preserved as is if (iteratedTaskVariants.size() > 1) { final ISelection selection = taskFactory.createNewSelection(); for (final ITask variant : iteratedTaskVariants) { taskBuilder.addChild(selection, variant); } taskBuilder.setMarkedTask(iteration, selection); for (final IIterationInstance instance : iterationInstances) { for (int i = 0; i < instance.size(); i++) { final ISelectionInstance selectionInstance = taskFactory .createNewTaskInstance(selection); taskBuilder.setChild(selectionInstance, instance.get(i)); taskBuilder.setTaskInstance(instance, i, selectionInstance); } } } else { taskBuilder.setMarkedTask(iteration, iteratedTaskVariants.get(0)); } } /** * Match as sequence. * * @param appData , Ruleapplication Data needed to keep track of all created * tasks * @param m The match to be converted into a Task * @return The task of the match with an ISequence as it's root */ synchronized public ISequence matchAsSequence(RuleApplicationData appData, Match m) { final ISequence sequence = taskFactory.createNewSequence(); appData.newTaskCreated(sequence); final int[] first = m.getFirstSequence().getSequence(); final int[] second = m.getSecondSequence().getSequence(); // Both sequences of a match are equally long for (int i = 0; i < m.getFirstSequence().size(); i++) { // Two gaps aligned to each other: Have not seen it happening so // far, just to handle it if ((first[i] == -1) && (second[i] == -1)) { // Do nothing here. } // Both events are equal, we can simply add the task referring to // the number else if (first[i] == second[i]) { taskBuilder.addChild(sequence, appData.getNumber2Task().get(first[i])); } // We have a gap in the first sequence, we need to add the task of // the second sequence as optional else if ((first[i] == -1) && (second[i] != -1)) { final IOptional optional = taskFactory.createNewOptional(); appData.newTaskCreated(optional); taskBuilder.setMarkedTask(optional, appData.getNumber2Task() .get(second[i])); taskBuilder.addChild(sequence, optional); } // We have a gap in the second sequence, we need to add the task of // the first sequence as optional else if ((first[i] != -1) && (second[i] == -1)) { final IOptional optional = taskFactory.createNewOptional(); appData.newTaskCreated(optional); taskBuilder.setMarkedTask(optional, appData.getNumber2Task() .get(first[i])); taskBuilder.addChild(sequence, optional); } // Both tasks are not equal, we need to insert a selection here. // Check if the next position is not a selection else if (i < (first.length - 1)) { if ((first[i] != second[i]) && (((first[i + 1] == second[i + 1]) || (first[i + 1] == -1) || (second[i + 1] == -1)))) { final ISelection selection = taskFactory .createNewSelection(); appData.newTaskCreated(selection); taskBuilder.addChild(selection, appData.getNumber2Task() .get(first[i])); taskBuilder.addChild(selection, appData.getNumber2Task() .get(second[i])); taskBuilder.addChild(sequence, selection); } else { boolean selectionfound = true; final ISelection selection = taskFactory .createNewSelection(); appData.newTaskCreated(selection); final ISequence subsequence1 = taskFactory .createNewSequence(); appData.newTaskCreated(subsequence1); final ISequence subsequence2 = taskFactory .createNewSequence(); appData.newTaskCreated(subsequence2); taskBuilder.addChild(selection, subsequence1); taskBuilder.addChild(selection, subsequence2); taskBuilder.addChild(sequence, selection); while ((i < (first.length - 1)) && selectionfound) { selectionfound = false; taskBuilder.addChild(subsequence1, appData .getNumber2Task().get(first[i])); taskBuilder.addChild(subsequence2, appData .getNumber2Task().get(second[i])); if ((first[i + 1] != second[i + 1]) && (first[i + 1] != -1) && (second[i + 1] != -1)) { selectionfound = true; } else { continue; } i++; } if ((i == (first.length - 1)) && selectionfound) { taskBuilder.addChild(subsequence1, appData .getNumber2Task().get(first[i])); taskBuilder.addChild(subsequence2, appData .getNumber2Task().get(second[i])); } } } else { if ((first[i] != second[i])) { final ISelection selection = taskFactory .createNewSelection(); appData.newTaskCreated(selection); taskBuilder.addChild(selection, appData.getNumber2Task() .get(first[i])); taskBuilder.addChild(selection, appData.getNumber2Task() .get(second[i])); taskBuilder.addChild(sequence, selection); } } } return sequence; } /** *

* replaces all occurrences of all tasks provided in the set with iterations *

. * * @param iteratedTasks the tasks to be replaced with iterations * @param sessions the sessions in which the tasks are to be replaced * @param appData the rule application data combining all data used for applying * this rule */ private void replaceIterationsOf(Set iteratedTasks, List sessions, RuleApplicationData appData) { final Map iterations = new HashMap(); final Map> iterationInstances = new HashMap>(); for (final ITask iteratedTask : iteratedTasks) { final IIteration iteration = taskFactory.createNewIteration(); appData.newTaskCreated(iteration); iterations.put(iteratedTask, iteration); iterationInstances.put(iteration, new LinkedList()); } IIterationInstance iterationInstance; for (final IUserSession session : sessions) { int index = 0; iterationInstance = null; while (index < session.size()) { // we prepared the task instances to refer to unique tasks, if // they are treated // as equal. Therefore, we just compare the identity of the // tasks of the task // instances final ITask currentTask = session.get(index).getTask(); final IIteration iteration = iterations.get(currentTask); if (iteration != null) { if ((iterationInstance == null) || (iterationInstance.getTask() != iteration)) { iterationInstance = taskFactory .createNewTaskInstance(iteration); iterationInstances.get(iteration) .add(iterationInstance);// TODO:: Don't create // TaskInstances here, // use a set of tasks // instead taskBuilder.addTaskInstance(session, index, iterationInstance); index++; } taskBuilder.addChild(iterationInstance, session.get(index)); taskBuilder.removeTaskInstance(session, index); } else { if (iterationInstance != null) { iterationInstance = null; } index++; } } } for (final Map.Entry> entry : iterationInstances .entrySet()) { harmonizeIterationInstancesModel(entry.getKey(), entry.getValue()); } } /** * Replace matches. * * @param appData the app data */ private void replaceMatches(RuleApplicationData appData) { appData.replacedOccurences = new HashMap>(); final int matchSeqSize = appData.getMatchseqs().size(); int newThreads = nThreads; if (matchSeqSize < nThreads) { newThreads = matchSeqSize; } final ExecutorService executor = Executors .newFixedThreadPool(newThreads); final int interval = matchSeqSize / newThreads; int rest = matchSeqSize % newThreads; for (int i = 0; i < (matchSeqSize - interval); i += interval) { int offset = 0; if (rest != 0) { offset = 1; rest--; } final int from = i; final int to = i + interval + offset; System.out .println("Replacement: Creating thread with matches from " + from + " to " + to); // search each match in every other sequence final ParallelMatchReplacer replacer = new ParallelMatchReplacer( appData, from, to); executor.execute(replacer); } executor.shutdown(); try { executor.awaitTermination(2, TimeUnit.HOURS); } catch (final InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** *

* searches the provided sessions for task iterations. If a task is * iterated, it is added to the returned set. *

* * @param sessions the sessions * @return a set of tasks being iterated somewhere */ private Set searchIteratedTasks(List sessions) { final Set iteratedTasks = new HashSet(); for (final IUserSession session : sessions) { for (int i = 0; i < (session.size() - 1); i++) { // we prepared the task instances to refer to unique tasks, if // they are treated // as equal. Therefore, we just compare the identity of the // tasks of the task // instances if (session.get(i).getTask() == session.get(i + 1).getTask()) { iteratedTasks.add(session.get(i).getTask()); } } } return iteratedTasks; } /** * Search matches in all sessions. * * @param appData the app data */ private void searchMatchesInAllSessions(RuleApplicationData appData) { Console.traceln(Level.INFO, "searching for patterns occuring most with " + nThreads + " threads"); // Prepare parallel search of matchseqs final int matchSeqSize = appData.getMatchseqs().size(); int newThreads = nThreads; if (matchSeqSize < nThreads) { newThreads = matchSeqSize; } final int interval = matchSeqSize / newThreads; int rest = matchSeqSize % newThreads; final ExecutorService executor = Executors.newFixedThreadPool(nThreads); for (int i = 0; i < (matchSeqSize - interval); i += interval) { int offset = 0; if (rest != 0) { offset = 1; rest--; } final int from = i; final int to = i + interval + offset; System.out .println("Match finding: Creating thread with matches from " + from + " to " + to); // search each match in every other sequence final ParallelMatchOcurrencesFinder finder = new ParallelMatchOcurrencesFinder( appData, from, to); executor.execute(finder); } executor.shutdown(); try { executor.awaitTermination(2, TimeUnit.HOURS); } catch (final InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return "SequenceForTaskDetectionRuleAlignment"; } /** * The Class ParallelMatchOcurrencesFinder. */ private class ParallelMatchOcurrencesFinder implements Runnable { /** The app data. */ private final RuleApplicationData appData; /** The from. */ private final int from; /** The to. */ private final int to; /** * Instantiates a new parallel match ocurrences finder. * * @param appData the app data * @param from the from * @param to the to */ ParallelMatchOcurrencesFinder(RuleApplicationData appData, int from, int to) { this.appData = appData; this.from = from; this.to = to; } /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { int count = 0; final int size = to - from; for (int i = from; i < to; i++) { final Match pattern = appData.getMatchseqs().get(i); count++; RuleUtils.printProgressPercentage("Match finding progress", count, size); // Skip sequences with more 0 events (scrolls) than other // events. // Both of the pattern sequences are equally long, so the zero // counts just need to be smaller than the length of one // sequence if ((pattern.getFirstSequence().eventCount(0) + pattern.getSecondSequence().eventCount(0) + 1) > pattern .getFirstSequence().size()) { continue; } for (int j = 0; j < appData.getNumberSequences().size(); j++) { final LinkedList startpositions = appData .getNumberSequences().get(j) .containsPattern(pattern); if (startpositions.size() > 0) { for (final Iterator jt = startpositions .iterator(); jt.hasNext();) { final int start = jt.next(); pattern.addOccurence(new MatchOccurence(start, start + pattern.size(), j)); } } } } } } /** * The Class ParallelMatchReplacer. */ private class ParallelMatchReplacer implements Runnable { /** The app data. */ private final RuleApplicationData appData; /** The from. */ private final int from; /** The to. */ private final int to; /** * Instantiates a new parallel match replacer. * * @param appData the app data * @param from the from * @param to the to */ ParallelMatchReplacer(RuleApplicationData appData, int from, int to) { this.appData = appData; this.from = from; this.to = to; } /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { // TODO Cleanup // int count = 0; // int size = to - from; for (int i = from; i < to; i++) { // count++; // Every pattern consists of 2 sequences, therefore the minimum // occurrences here is 2. // We just need the sequences also occurring in other sequences // as well if (appData.getMatchseqs().get(i).occurenceCount() > 2) { final ISequence task = matchAsSequence(appData, appData .getMatchseqs().get(i)); invalidOccurence: for (final Iterator it = appData .getMatchseqs().get(i).getOccurences().iterator(); it .hasNext();) { final MatchOccurence oc = it.next(); // Check if nothing has been replaced in the sequence we // want to replace now synchronized (appData.getReplacedOccurrences()) { if (appData.getReplacedOccurrences().get( oc.getSequenceId()) == null) { appData.getReplacedOccurrences().put( oc.getSequenceId(), new LinkedList()); } else { // check if we have any replaced occurence with // indexes // smaller than ours. If so, we need to adjust // our start // and endpoints // of the replacement. // Also do a check if we have replaced this // specific // MatchOccurence in this sequence already. Jump // to the // next occurence if this is the case. // This is no more neccessary once the matches // are // harmonized. for (final Iterator jt = appData .getReplacedOccurrences() .get(oc.getSequenceId()).iterator(); jt .hasNext();) { final MatchOccurence tmpOC = jt.next(); if ((oc.getStartindex() >= tmpOC .getStartindex()) && (oc.getStartindex() <= tmpOC .getEndindex())) { continue invalidOccurence; } if (oc.getEndindex() >= tmpOC .getStartindex()) { continue invalidOccurence; } else if (oc.getStartindex() > tmpOC .getEndindex()) { final int diff = tmpOC.getEndindex() - tmpOC.getStartindex(); // Just to be sure. if (diff > 0) { oc.setStartindex((oc .getStartindex() - diff) + 1); oc.setEndindex((oc.getEndindex() - diff) + 1); } else { Console.traceln(Level.WARNING, "End index of a Match before start. This should never happen"); } } } } System.out.println("Replacing in sequence" + oc.getSequenceId()); synchronized (appData) { appData.detectedAndReplacedTasks = true; } synchronized (appData.getSessions().get( oc.getSequenceId())) { final ISequenceInstance sequenceInstances = RuleUtils .createNewSubSequenceInRange( appData.getSessions().get( oc.getSequenceId()), oc.getStartindex(), oc.getEndindex(), task, taskFactory, taskBuilder); oc.setEndindex((oc.getStartindex() + sequenceInstances .size()) - RuleUtils.missedOptionals); } } // Adjust the length of the match regarding to the // length of // instance. (OptionalInstances may be shorter) synchronized (appData.getReplacedOccurrences().get( oc.getSequenceId())) { appData.getReplacedOccurrences() .get(oc.getSequenceId()).add(oc); } } } } } } /** * The Class ParallelPairwiseAligner. */ private class ParallelPairwiseAligner implements Runnable { /** The app data. */ private final RuleApplicationData appData; /** The from. */ private final int from; /** The to. */ private final int to; /** * Instantiates a new parallel pairwise aligner. * * @param appData the app data * @param from the from * @param to the to */ ParallelPairwiseAligner(RuleApplicationData appData, int from, int to) { this.appData = appData; this.from = from; this.to = to; } /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { int count = 0; final int size = to - from; for (int i = from; i < to; i++) { final NumberSequence ns1 = appData.getNumberSequences().get(i); count++; RuleUtils.printProgressPercentage("Aligning Progress", count, size); for (int j = 0; j < appData.getNumberSequences().size(); j++) { final NumberSequence ns2 = appData.getNumberSequences() .get(j); if (i != j) { final AlignmentAlgorithm aa = AlignmentAlgorithmFactory .create(); aa.align(ns1, ns2, appData.getSubmat(), 9); synchronized (appData.getMatchseqs()) { appData.getMatchseqs().addAll(aa.getMatches()); } } } } } } }