├── .gitignore ├── LICENSE ├── README.md ├── build.gradle └── src └── main └── java └── edu └── fcse └── alphaalgorithm ├── AlphaAlgorithm.java ├── AlphaPlusAlgorithmMain.java ├── Utils.java ├── Validate.java ├── WorkflowNetwork.java └── tools ├── Event.java ├── Footprint.java ├── LoopLengthOne.java ├── Pair.java ├── Place.java ├── RelationType.java └── Trace.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/gradle,java,intellij,eclipse 2 | 3 | ### Gradle ### 4 | .gradle 5 | build/ 6 | 7 | # Ignore Gradle GUI config 8 | gradle-app.setting 9 | 10 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 11 | !gradle-wrapper.jar 12 | 13 | # Cache of project 14 | .gradletasknamecache 15 | 16 | 17 | ### Java ### 18 | *.class 19 | 20 | # Mobile Tools for Java (J2ME) 21 | .mtj.tmp/ 22 | 23 | # Package Files # 24 | *.jar 25 | *.war 26 | *.ear 27 | 28 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 29 | hs_err_pid* 30 | 31 | 32 | ### Intellij ### 33 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 34 | 35 | *.iml 36 | 37 | ## Directory-based project format: 38 | .idea/ 39 | # if you remove the above rule, at least ignore the following: 40 | 41 | # User-specific stuff: 42 | # .idea/workspace.xml 43 | # .idea/tasks.xml 44 | # .idea/dictionaries 45 | # .idea/shelf 46 | 47 | # Sensitive or high-churn files: 48 | # .idea/dataSources.ids 49 | # .idea/dataSources.xml 50 | # .idea/sqlDataSources.xml 51 | # .idea/dynamic.xml 52 | # .idea/uiDesigner.xml 53 | 54 | # Gradle: 55 | # .idea/gradle.xml 56 | # .idea/libraries 57 | 58 | # Mongo Explorer plugin: 59 | # .idea/mongoSettings.xml 60 | 61 | ## File-based project format: 62 | *.ipr 63 | *.iws 64 | 65 | ## Plugin-specific files: 66 | 67 | # IntelliJ 68 | /out/ 69 | 70 | # mpeltonen/sbt-idea plugin 71 | .idea_modules/ 72 | 73 | # JIRA plugin 74 | atlassian-ide-plugin.xml 75 | 76 | # Crashlytics plugin (for Android Studio and IntelliJ) 77 | com_crashlytics_export_strings.xml 78 | crashlytics.properties 79 | crashlytics-build.properties 80 | fabric.properties 81 | 82 | 83 | ### Eclipse ### 84 | *.pydevproject 85 | .metadata 86 | .gradle 87 | bin/ 88 | tmp/ 89 | *.tmp 90 | *.bak 91 | *.swp 92 | *~.nib 93 | local.properties 94 | .settings/ 95 | .loadpath 96 | 97 | # Eclipse Core 98 | .project 99 | 100 | # External tool builders 101 | .externalToolBuilders/ 102 | 103 | # Locally stored "Eclipse launch configurations" 104 | *.launch 105 | 106 | # CDT-specific 107 | .cproject 108 | 109 | # JDT-specific (Eclipse Java Development Tools) 110 | .classpath 111 | 112 | # Java annotation processor (APT) 113 | .factorypath 114 | 115 | # PDT-specific 116 | .buildpath 117 | 118 | # sbteclipse plugin 119 | .target 120 | 121 | # TeXlipse plugin 122 | .texlipse 123 | 124 | # STS (Spring Tool Suite) 125 | .springBeans 126 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 delix 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AlphaAlgorithm 2 | ============== 3 | 4 | Java implementation of the Process Mining Alpha algorithm 5 | 6 | This is an Eclipse project. Import as existing project, no setup required. 7 | Java 7 is used, can be easily converted to Java 6, only a few lines require changing. 8 | 9 | Can do: 10 | 1. Parse an XES file into an Event Log 11 | 2. Create a WF-net from that Event Log using the Alpha Plus Algorithm 12 | 3. Run traces on the create WF-net 13 | 4. Validate the created WF-net 14 | 5. Eat candy 15 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'idea' 3 | 4 | sourceCompatibility = 1.8 5 | version = '1.0' 6 | jar { 7 | manifest { 8 | attributes 'Implementation-Title': 'Alpha Algorithm', 9 | 'Implementation-Version': version 10 | } 11 | } 12 | 13 | repositories { 14 | mavenCentral() 15 | } 16 | 17 | dependencies { 18 | compile 'org.jsoup:jsoup:1.8.3' 19 | compile 'org.slf4j:slf4j-api:1.7.13' 20 | compile 'org.slf4j:slf4j-jdk14:1.7.13' 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/edu/fcse/alphaalgorithm/AlphaAlgorithm.java: -------------------------------------------------------------------------------- 1 | package edu.fcse.alphaalgorithm; 2 | 3 | import edu.fcse.alphaalgorithm.tools.*; 4 | 5 | import java.util.*; 6 | import java.util.stream.Collectors; 7 | 8 | /** 9 | * Creates a Workflow Net Model from an eventLog. 10 | * 11 | * @author blagoj atanasovski 12 | */ 13 | public class AlphaAlgorithm { 14 | org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(AlphaAlgorithm.class); 15 | public static boolean takeInAccountLoopsLengthTwo = true; 16 | 17 | public static WorkflowNetwork discoverWorkflowNetwork(Set eventsLogArg) { 18 | Set recordedLLOs = new HashSet<>(); 19 | for (Trace trace : eventsLogArg) { 20 | preProcessLLOs(trace, recordedLLOs); 21 | } 22 | 23 | Set eventList = new HashSet<>(); 24 | HashSet startingEvents = new HashSet<>(); 25 | HashSet endingEvents = new HashSet<>(); 26 | 27 | // ProcessMining book page 133 28 | // Steps 1,2,3 29 | AlphaAlgorithm.extractEvents(eventsLogArg, eventList, 30 | startingEvents, endingEvents); 31 | 32 | // Generate footprint matrix from eventsLog 33 | Footprint footprint = new Footprint(eventList, eventsLogArg, 34 | takeInAccountLoopsLengthTwo); 35 | System.out.println("------------------------"); 36 | System.out.println("Footprint matrix:"); 37 | System.out.println(footprint); 38 | System.out.println("------------------------"); 39 | 40 | // Step 4 generate places 41 | Set XL = AlphaAlgorithm.getPlacesFromFootprint(footprint, eventList); 42 | 43 | // Step 5 reduce places, no places that are subsets of other places 44 | // workflowPlaces = YL = all the places except start and sink 45 | Set workflowPlaces = AlphaAlgorithm.reducePlaces(XL); //PL 46 | AlphaAlgorithm.postProcessWF(recordedLLOs, workflowPlaces, eventList); 47 | 48 | // Step 7 create transitions 49 | Set> eventToPlaceTransitions = new HashSet<>(); 50 | Map> eventToPlaceTransitionsMap = new HashMap<>(); 51 | Set> placeToEventTransitions = new HashSet<>(); 52 | AlphaAlgorithm.createEventToPlaceTransitions(eventList, workflowPlaces, eventToPlaceTransitions, eventToPlaceTransitionsMap); 53 | AlphaAlgorithm.createPlaceToEventTransitions(eventList, workflowPlaces, placeToEventTransitions); 54 | 55 | // Step 6 56 | // Source and sink place 57 | Place in = new Place("in", new HashSet<>(), new HashSet<>()); 58 | Place out = new Place("out", new HashSet<>(), new HashSet<>()); 59 | AlphaAlgorithm.connectSourceAndSink(in, out, startingEvents, endingEvents, workflowPlaces, placeToEventTransitions, eventToPlaceTransitions); 60 | WorkflowNetwork network = new WorkflowNetwork(workflowPlaces, eventList, eventToPlaceTransitions, eventToPlaceTransitionsMap, placeToEventTransitions, in, out); 61 | System.out.println(network); 62 | return network; 63 | } 64 | 65 | 66 | /** 67 | * @param eventLog a set of traces from where the event names are extracted 68 | * @param allEvents a set that after this method will contain the names of the 69 | * events present in the eventsLog (Xl) 70 | * @param startingEvents a set of the starting events, events with which the traces 71 | * start with (Xi) 72 | * @param endingEvents a set of ending events, events that the traces end with (Xo) 73 | */ 74 | private static void extractEvents(Set eventLog, Set allEvents, 75 | Set startingEvents, Set endingEvents) { 76 | allEvents.clear(); 77 | startingEvents.clear(); 78 | endingEvents.clear(); 79 | for (Trace singleTrace : eventLog) { 80 | startingEvents.add(singleTrace.getFirstEvent()); 81 | endingEvents.add(singleTrace.getLastEvent()); 82 | allEvents.addAll(singleTrace.getEventsList()); 83 | } 84 | } 85 | 86 | /** 87 | * @return XL, a set of places, each place has input and output events, this 88 | * set can be reduced to YL by the method {@link #reducePlaces(Set)} 89 | */ 90 | private static Set getPlacesFromFootprint(Footprint footprint, Set eventList) { 91 | Set xl = new HashSet<>(); 92 | System.out.println("Getting places from footprint:"); 93 | Set> powerSet = Utils.powerSet(eventList); 94 | System.out.println("Got powerSet: " + powerSet.size()); 95 | powerSet.remove(new HashSet()); 96 | @SuppressWarnings("unchecked") 97 | Set[] powerSetArray = powerSet.toArray(new Set[powerSet.size()]); 98 | System.out.println("Power set cast to array"); 99 | for (int i = 0; i < powerSetArray.length; i++) { 100 | Set first = powerSetArray[i]; 101 | for (int j = 0; j < powerSetArray.length; j++) { 102 | if (i == j) { 103 | continue; 104 | } 105 | 106 | Set second = powerSetArray[j]; 107 | if (footprint.areEventsConnected(first, second)) { 108 | xl.add(new Place(first, second)); 109 | } 110 | } 111 | } 112 | 113 | System.out.println("Places (XL) created from footprint. # of places in XL: " + xl.size()); 114 | return xl; 115 | } 116 | 117 | /** 118 | * @param xl the result from step 4 of the algorithm 119 | * @return Yl the result from step 5 of the algorithm, all subset Places 120 | * removed from Xl 121 | */ 122 | private static Set reducePlaces(Set xl) { 123 | Set toRemove = new HashSet<>(); 124 | Place[] potentialPlaces = xl.toArray(new Place[xl.size()]); 125 | for (int i = 0; i < potentialPlaces.length - 1; i++) { 126 | Place potentialPlace1 = potentialPlaces[i]; 127 | for (int j = i + 1; j < potentialPlaces.length; j++) { 128 | if (potentialPlace1.getInEvents().containsAll( 129 | potentialPlaces[j].getInEvents())) { 130 | if (potentialPlaces[i].getOutEvents().containsAll( 131 | potentialPlaces[j].getOutEvents())) { 132 | toRemove.add(potentialPlaces[j]); 133 | continue; 134 | } 135 | } 136 | 137 | if (potentialPlaces[j].getInEvents().containsAll( 138 | potentialPlaces[i].getInEvents())) { 139 | if (potentialPlaces[j].getOutEvents().containsAll( 140 | potentialPlaces[i].getOutEvents())) { 141 | toRemove.add(potentialPlaces[i]); 142 | } 143 | } 144 | } 145 | } 146 | 147 | Set yl = new HashSet<>(xl); 148 | yl.removeAll(toRemove); 149 | return yl; 150 | } 151 | 152 | private static void createEventToPlaceTransitions(final Set eventList, 153 | final Set workflowPlaces, 154 | final Set> eventToPlaceTransitions, 155 | final Map> eventToPlaceTransitionsMap) { 156 | eventToPlaceTransitions.clear(); 157 | eventToPlaceTransitionsMap.clear(); 158 | for (Event event : eventList) { 159 | Set eventToPlace = new HashSet<>(); 160 | eventToPlaceTransitionsMap.put(event, eventToPlace); 161 | workflowPlaces.stream() 162 | .filter(place -> place.getInEvents().contains(event)) 163 | .forEach(place -> { 164 | eventToPlaceTransitions.add(new Pair<>(event, place)); 165 | eventToPlace.add(place); 166 | }); 167 | } 168 | } 169 | 170 | private static void createPlaceToEventTransitions(final Set eventList, 171 | final Set workflowPlaces, 172 | final Set> placeToEventTransitions) { 173 | placeToEventTransitions.clear(); 174 | for (Event event : eventList) { 175 | placeToEventTransitions.addAll( 176 | workflowPlaces.stream() 177 | .filter(place -> place.getOutEvents().contains(event)) 178 | .map(place -> new Pair<>(place, event)) 179 | .collect(Collectors.toList()) 180 | ); 181 | } 182 | } 183 | 184 | /** 185 | * Source and Sink places are not connected after the transitions are 186 | * created between the other events. 187 | */ 188 | private static void connectSourceAndSink(final Place in, 189 | final Place out, 190 | final Set startingEvents, 191 | final Set endingEvents, 192 | final Set workflowPlaces, 193 | final Set> placeToEventTransitions, 194 | final Set> eventToPlaceTransitions) { 195 | for (Event startEvent : startingEvents) { 196 | in.addOutEvent(startEvent); 197 | placeToEventTransitions 198 | .add(new Pair<>(in, startEvent)); 199 | } 200 | 201 | for (Event endEvent : endingEvents) { 202 | out.addInEvent(endEvent); 203 | eventToPlaceTransitions.add(new Pair<>(endEvent, out)); 204 | } 205 | 206 | workflowPlaces.add(in); 207 | workflowPlaces.add(out); 208 | } 209 | 210 | 211 | private static int checkForCycleLengthOne(List eventsInTrace) { 212 | for (int i = 0; i < eventsInTrace.size() - 1; i++) { 213 | if (eventsInTrace.get(i).equals(eventsInTrace.get(i + 1))) { 214 | return i; 215 | } 216 | } 217 | 218 | return -1; 219 | } 220 | 221 | //TODO test this 222 | private static void preProcessLLOs(Trace singleTrace, Set recordedLLOs) { 223 | int start; 224 | List eventsInTrace = singleTrace.getEventsList(); 225 | while ((start = checkForCycleLengthOne(eventsInTrace)) != -1) { 226 | int prev = start - 1; 227 | int currentEvent = start; 228 | while (currentEvent < eventsInTrace.size() - 1 && eventsInTrace.get(currentEvent).equals(eventsInTrace.get(currentEvent + 1))) { 229 | currentEvent++; 230 | } 231 | 232 | currentEvent++; 233 | LoopLengthOne llo = new LoopLengthOne(eventsInTrace.get(prev), 234 | eventsInTrace.get(start), eventsInTrace.get(currentEvent)); 235 | int numberOfOccurrencesOfRepeatingEvent = currentEvent - start; 236 | for (int i = 0; i < numberOfOccurrencesOfRepeatingEvent; i++) { 237 | eventsInTrace.remove(start); 238 | } 239 | 240 | recordedLLOs.add(llo); 241 | } 242 | 243 | } 244 | 245 | private static void postProcessWF(Set recordedLLOs, Set workflowPlaces, Set eventList) { 246 | Queue lloQueue = new LinkedList<>(recordedLLOs); 247 | while (!lloQueue.isEmpty()) { 248 | LoopLengthOne llo = lloQueue.poll(); 249 | Event in = llo.getPrevEvent(); 250 | Event out = llo.getNextEvent(); 251 | boolean used = false; 252 | for (Place place : workflowPlaces) { 253 | if (place.getInEvents().contains(in) 254 | && place.getOutEvents().contains(out)) { 255 | place.addInEvent(llo.getLoopedEvent()); 256 | place.addOutEvent(llo.getLoopedEvent()); 257 | eventList.add(llo.getLoopedEvent()); 258 | used = true; 259 | 260 | // TODO: BlagojA NOT SURE ABOUT THIS 261 | break; 262 | } 263 | } 264 | 265 | if (!used) { 266 | lloQueue.add(llo); 267 | } 268 | } 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /src/main/java/edu/fcse/alphaalgorithm/AlphaPlusAlgorithmMain.java: -------------------------------------------------------------------------------- 1 | package edu.fcse.alphaalgorithm; 2 | 3 | import edu.fcse.alphaalgorithm.tools.Trace; 4 | 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | public class AlphaPlusAlgorithmMain { 9 | public static void main(String args[]) { 10 | Validate.validateAlgorithmPercentage(); 11 | Set eventLog = checkArgs(args); 12 | //eventLog = Utils.parseXESFile("reviewing.xes"); 13 | AlphaAlgorithm.takeInAccountLoopsLengthTwo = true; 14 | AlphaAlgorithm.discoverWorkflowNetwork(eventLog); 15 | } 16 | 17 | private static Set checkArgs(String[] args) { 18 | boolean error = false; 19 | if (args.length != 2) { 20 | error = true; 21 | } else { 22 | if (args[0].equals("d")) { 23 | switch (args[1]) { 24 | case "L1": 25 | return Utils.demoL1eventLog(); 26 | case "L2": 27 | return Utils.demoL2eventLog(); 28 | case "L7": 29 | return Utils.demoL7eventLog(); 30 | case "LLT": 31 | return Utils.demoLLTeventLog(); 32 | case "chap7": 33 | return Utils.chapter7EventLog(); 34 | 35 | default: 36 | error = true; 37 | break; 38 | } 39 | } else if (args[0].equals("f")) { 40 | return Utils.readInputFromCSV(args[1]); 41 | } else { 42 | error = true; 43 | } 44 | } 45 | if (error) { 46 | usage(); 47 | System.exit(1); 48 | } 49 | return new HashSet<>(); 50 | } 51 | 52 | private static void usage() { 53 | System.out.println("Error reading input parameters!\n Usage:"); 54 | System.out 55 | .println("AlphaPlusAlgorithm.jar demoOrInputFile demoName/fileName"); 56 | System.out 57 | .println("demo - execute the algorithm with one of the predefined demo cases from the ProcessMining book chapters"); 58 | System.out.println("inputFile - specify a CSV file for the event log"); 59 | System.out.println("demoName - available are: L1, L2, L7, LLT, chap7"); 60 | System.out.println("Example: AlphaPlusAlgorithm.jar d chap7"); 61 | System.out.println("Example: AlphaPlusAlgorithm.jar f ~/inputLog.csv"); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/edu/fcse/alphaalgorithm/Utils.java: -------------------------------------------------------------------------------- 1 | package edu.fcse.alphaalgorithm; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.nio.charset.Charset; 7 | import java.nio.file.FileSystems; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.util.ArrayList; 11 | import java.util.HashSet; 12 | import java.util.LinkedList; 13 | import java.util.List; 14 | import java.util.Set; 15 | 16 | import edu.fcse.alphaalgorithm.tools.Event; 17 | import org.jsoup.Jsoup; 18 | import org.jsoup.nodes.Document; 19 | import org.jsoup.nodes.Element; 20 | import org.jsoup.select.Elements; 21 | 22 | import edu.fcse.alphaalgorithm.tools.Trace; 23 | 24 | public class Utils { 25 | /** 26 | * @param originalSet 27 | * @return a set of all the subsets of originalSet 28 | */ 29 | public static Set> powerSet(Set originalSet) { 30 | Set> sets = new HashSet<>(); 31 | if (originalSet.isEmpty()) { 32 | sets.add(new HashSet<>()); 33 | return sets; 34 | } 35 | 36 | List list = new ArrayList<>(originalSet); 37 | E head = list.get(0); 38 | Set rest = new HashSet<>(list.subList(1, list.size())); 39 | for (Set set : powerSet(rest)) { 40 | Set newSet = new HashSet<>(); 41 | newSet.add(head); 42 | newSet.addAll(set); 43 | sets.add(newSet); 44 | sets.add(set); 45 | } 46 | 47 | return sets; 48 | } 49 | 50 | public static Set readInputFromCSV(String fileName) { 51 | Charset charset = Charset.forName("US-ASCII"); 52 | Path file = FileSystems.getDefault().getPath(fileName); 53 | Set toReturn = new HashSet<>(); 54 | try (BufferedReader reader = Files.newBufferedReader(file, charset)) { 55 | String line; 56 | while ((line = reader.readLine()) != null) { 57 | String[] events = line.split(","); 58 | if (events == null || events.length == 0) { 59 | throw new IOException( 60 | "Input file not in correct format, empty line read"); 61 | } 62 | 63 | toReturn.add(new Trace(events)); 64 | } 65 | } catch (IOException x) { 66 | System.err.format("IOException: %s%n", x); 67 | return new HashSet<>(); 68 | } 69 | 70 | return toReturn; 71 | } 72 | 73 | public static Set demoL1eventLog() { 74 | Set eventLog = new HashSet<>(); 75 | eventLog.add(new Trace(new String[]{"a", "b", "c", "d"})); 76 | eventLog.add(new Trace(new String[]{"a", "c", "b", "d"})); 77 | eventLog.add(new Trace(new String[]{"a", "e", "d"})); 78 | return eventLog; 79 | } 80 | 81 | public static Set demoL2eventLog() { 82 | Set eventLog = new HashSet<>(); 83 | eventLog.add(new Trace(new String[]{"a", "b", "c", "d"})); 84 | eventLog.add(new Trace(new String[]{"a", "c", "b", "d"})); 85 | eventLog.add(new Trace(new String[]{"a", "b", "c", "e", "f", "b", 86 | "c", "d"})); 87 | eventLog.add(new Trace(new String[]{"a", "b", "c", "e", "f", "c", 88 | "b", "d"})); 89 | eventLog.add(new Trace(new String[]{"a", "c", "b", "e", "f", "b", 90 | "c", "d"})); 91 | eventLog.add(new Trace(new String[]{"a", "c", "b", "e", "f", "b", 92 | "c", "e", "f", "c", "b", "d"})); 93 | return eventLog; 94 | } 95 | 96 | public static Set demoL7eventLog() { 97 | Set eventLog = new HashSet<>(); 98 | eventLog.add(new Trace(new String[]{"a", "c"})); 99 | eventLog.add(new Trace(new String[]{"a", "b", "c"})); 100 | eventLog.add(new Trace(new String[]{"a", "b", "b", "c"})); 101 | eventLog.add(new Trace(new String[]{"a", "b", "b", "b", "c"})); 102 | return eventLog; 103 | } 104 | 105 | public static Set demoLLTeventLog() { 106 | Set eventLog = new HashSet<>(); 107 | eventLog.add(new Trace(new String[]{"x", "a", "y"})); 108 | eventLog.add(new Trace(new String[]{"x", "a", "b", "y"})); 109 | eventLog.add(new Trace(new String[]{"x", "w",})); 110 | eventLog.add(new Trace(new String[]{"z", "b", "w"})); 111 | eventLog.add(new Trace(new String[]{"z", "b", "a", "y"})); 112 | eventLog.add(new Trace(new String[]{"z", "y"})); 113 | eventLog.add(new Trace(new String[]{"z", "a", "b", "a", "y"})); 114 | 115 | return eventLog; 116 | } 117 | 118 | public static Set chapter7EventLog() { 119 | Set eventLog = new HashSet<>(); 120 | eventLog.add(new Trace(new String[]{"a", "c", "d", "e", "h"})); 121 | eventLog.add(new Trace(new String[]{"a", "b", "d", "e", "g"})); 122 | eventLog.add(new Trace(new String[]{"a", "d", "c", "e", "h"})); 123 | eventLog.add(new Trace(new String[]{"a", "b", "d", "e", "h"})); 124 | eventLog.add(new Trace(new String[]{"a", "c", "d", "e", "g"})); 125 | eventLog.add(new Trace(new String[]{"a", "d", "c", "e", "g"})); 126 | eventLog.add(new Trace(new String[]{"a", "d", "b", "e", "h"})); 127 | eventLog.add(new Trace(new String[]{"a", "c", "d", "e", "f", "d", 128 | "b", "e", "h"})); 129 | eventLog.add(new Trace(new String[]{"a", "d", "b", "e", "g"})); 130 | eventLog.add(new Trace(new String[]{"a", "c", "d", "e", "f", "b", 131 | "d", "e", "h"})); 132 | eventLog.add(new Trace(new String[]{"a", "c", "d", "e", "f", "b", 133 | "d", "e", "g"})); 134 | eventLog.add(new Trace(new String[]{"a", "c", "d", "e", "f", "d", 135 | "b", "e", "g"})); 136 | eventLog.add(new Trace(new String[]{"a", "d", "c", "e", "f", "c", 137 | "d", "e", "h"})); 138 | eventLog.add(new Trace(new String[]{"a", "d", "c", "e", "f", "d", 139 | "b", "e", "h"})); 140 | eventLog.add(new Trace(new String[]{"a", "d", "c", "e", "f", "b", 141 | "d", "e", "g"})); 142 | eventLog.add(new Trace(new String[]{"a", "c", "d", "e", "f", "b", 143 | "d", "e", "f", "d", "b", "e", "g"})); 144 | eventLog.add(new Trace(new String[]{"a", "d", "c", "e", "f", "d", 145 | "b", "e", "g"})); 146 | eventLog.add(new Trace(new String[]{"a", "d", "c", "e", "f", "b", 147 | "d", "e", "f", "b", "d", "e", "g"})); 148 | eventLog.add(new Trace(new String[]{"a", "d", "c", "e", "f", "d", 149 | "b", "e", "f", "b", "d", "e", "h"})); 150 | eventLog.add(new Trace(new String[]{"a", "d", "b", "e", "f", "b", 151 | "d", "e", "f", "d", "b", "e", "g"})); 152 | eventLog.add(new Trace(new String[]{"a", "d", "c", "e", "f", "d", 153 | "b", "e", "f", "c", "d", "e", "f", "d", "b", "e", "g"})); 154 | return eventLog; 155 | } 156 | 157 | /** 158 | * Parses an XES file and returns an event log as a Set of Traces. 159 | * 160 | * @param fileName location of XES file 161 | * @return a set of Traces, an event log 162 | * @throws IOException if errors occur while reading the XES file 163 | */ 164 | public static Set parseXESFile(String fileName) throws IOException { 165 | Document doc = Jsoup.parse(new File(fileName), "UTF-8"); 166 | Elements traces = doc.getElementsByTag("trace"); 167 | Set eventLog = new HashSet<>(); 168 | for (int i = 0; i < traces.size(); i++) { 169 | Element currentTrace = traces.get(i); 170 | Elements events = currentTrace.getElementsByTag("event"); 171 | Trace newTrace = new Trace(); 172 | for (int j = 0; j < events.size(); j++) { 173 | Element currentEvent = events.get(j); 174 | Elements name = currentEvent.getElementsByAttributeValue("key", 175 | "concept:name"); 176 | Element nameN = name.get(0); 177 | Elements lifec = currentEvent.getElementsByAttributeValue( 178 | "key", "lifecycle:transition"); 179 | Element lifc = lifec.get(0); 180 | Event ev = new Event(nameN.attr("value") + " " 181 | + lifc.attr("value")); 182 | newTrace.addEvent(ev); 183 | } 184 | eventLog.add(newTrace); 185 | } 186 | return eventLog; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/edu/fcse/alphaalgorithm/Validate.java: -------------------------------------------------------------------------------- 1 | package edu.fcse.alphaalgorithm; 2 | 3 | import java.util.HashMap; 4 | import java.util.HashSet; 5 | import java.util.Map; 6 | import java.util.Random; 7 | import java.util.Set; 8 | 9 | import edu.fcse.alphaalgorithm.tools.Place; 10 | import edu.fcse.alphaalgorithm.tools.Trace; 11 | 12 | public class Validate { 13 | private static Map logMap; 14 | private static int[] rangeMapper = new int[21]; 15 | private static final int TOTAL_CASES = 1391; 16 | private static final int FOLDS = 5; 17 | private static int[] amountMapper = new int[121]; 18 | 19 | private static void createLog() { 20 | int i = 0; 21 | logMap = new HashMap<>(); 22 | logMap.put(0, new Trace(new String[] { "a", "c", "d", "e", "h" })); 23 | rangeMapper[i] = 455; 24 | amountMapper[i] = 455; 25 | i++; 26 | logMap.put(1, new Trace(new String[] { "a", "b", "d", "e", "g" })); 27 | rangeMapper[i] = rangeMapper[i - 1] + 191; 28 | amountMapper[i] = 191; 29 | i++; 30 | logMap.put(2, new Trace(new String[] { "a", "d", "c", "e", "h" })); 31 | rangeMapper[i] = rangeMapper[i - 1] + 177; 32 | amountMapper[i] = 177; 33 | i++; 34 | logMap.put(3, new Trace(new String[] { "a", "b", "d", "e", "h" })); 35 | rangeMapper[i] = rangeMapper[i - 1] + 144; 36 | amountMapper[i] = 144; 37 | i++; 38 | logMap.put(4, new Trace(new String[] { "a", "c", "d", "e", "g" })); 39 | rangeMapper[i] = rangeMapper[i - 1] + 111; 40 | amountMapper[i] = 111; 41 | i++; 42 | logMap.put(5, new Trace(new String[] { "a", "d", "c", "e", "g" })); 43 | rangeMapper[i] = rangeMapper[i - 1] + 82; 44 | amountMapper[i] = 82; 45 | i++; 46 | logMap.put(6, new Trace(new String[] { "a", "d", "b", "e", "h" })); 47 | rangeMapper[i] = rangeMapper[i - 1] + 56; 48 | amountMapper[i] = 56; 49 | i++; 50 | logMap.put(7, new Trace(new String[] { "a", "c", "d", "e", "f", "d", 51 | "b", "e", "h" })); 52 | rangeMapper[i] = rangeMapper[i - 1] + 47; 53 | amountMapper[i] = 47; 54 | i++; 55 | logMap.put(8, new Trace(new String[] { "a", "d", "b", "e", "g" })); 56 | rangeMapper[i] = rangeMapper[i - 1] + 38; 57 | amountMapper[i] = 38; 58 | i++; 59 | logMap.put(9, new Trace(new String[] { "a", "c", "d", "e", "f", "b", 60 | "d", "e", "h" })); 61 | rangeMapper[i] = rangeMapper[i - 1] + 33; 62 | amountMapper[i] = 33; 63 | i++; 64 | logMap.put(10, new Trace(new String[] { "a", "c", "d", "e", "f", "b", 65 | "d", "e", "g" })); 66 | rangeMapper[i] = rangeMapper[i - 1] + 14; 67 | amountMapper[i] = 14; 68 | i++; 69 | logMap.put(11, new Trace(new String[] { "a", "c", "d", "e", "f", "d", 70 | "b", "e", "g" })); 71 | rangeMapper[i] = rangeMapper[i - 1] + 11; 72 | amountMapper[i] = 11; 73 | i++; 74 | logMap.put(12, new Trace(new String[] { "a", "d", "c", "e", "f", "c", 75 | "d", "e", "h" })); 76 | rangeMapper[i] = rangeMapper[i - 1] + 9; 77 | amountMapper[i] = 9; 78 | i++; 79 | logMap.put(13, new Trace(new String[] { "a", "d", "c", "e", "f", "d", 80 | "b", "e", "h" })); 81 | rangeMapper[i] = rangeMapper[i - 1] + 8; 82 | amountMapper[i] = 8; 83 | i++; 84 | logMap.put(14, new Trace(new String[] { "a", "d", "c", "e", "f", "b", 85 | "d", "e", "g" })); 86 | rangeMapper[i] = rangeMapper[i - 1] + 5; 87 | amountMapper[i] = 5; 88 | i++; 89 | logMap.put(15, new Trace(new String[] { "a", "c", "d", "e", "f", "b", 90 | "d", "e", "f", "d", "b", "e", "g" })); 91 | rangeMapper[i] = rangeMapper[i - 1] + 3; 92 | amountMapper[i] = 3; 93 | i++; 94 | logMap.put(16, new Trace(new String[] { "a", "d", "c", "e", "f", "d", 95 | "b", "e", "g" })); 96 | rangeMapper[i] = rangeMapper[i - 1] + 2; 97 | amountMapper[i] = 2; 98 | i++; 99 | logMap.put(17, new Trace(new String[] { "a", "d", "c", "e", "f", "b", 100 | "d", "e", "f", "b", "d", "e", "g" })); 101 | rangeMapper[i] = rangeMapper[i - 1] + 2; 102 | amountMapper[i] = 2; 103 | i++; 104 | logMap.put(18, new Trace(new String[] { "a", "d", "c", "e", "f", "d", 105 | "b", "e", "f", "b", "d", "e", "h" })); 106 | rangeMapper[i] = rangeMapper[i - 1] + 1; 107 | amountMapper[i] = 1; 108 | i++; 109 | logMap.put(19, new Trace(new String[] { "a", "d", "b", "e", "f", "b", 110 | "d", "e", "f", "d", "b", "e", "g" })); 111 | rangeMapper[i] = rangeMapper[i - 1] + 1; 112 | amountMapper[i] = 1; 113 | i++; 114 | logMap.put(20, new Trace(new String[] { "a", "d", "c", "e", "f", "d", 115 | "b", "e", "f", "c", "d", "e", "f", "d", "b", "e", "g" })); 116 | rangeMapper[i] = rangeMapper[i - 1] + 1; 117 | amountMapper[i] = 1; 118 | i++; 119 | } 120 | 121 | private static Set[] getFolds() { 122 | // Actually I checked, double checked 123 | @SuppressWarnings("unchecked") 124 | Set[] folds = new Set[FOLDS]; 125 | Set pickedCases = new HashSet<>(); 126 | Random r = new Random(); 127 | for (int i = 0; i < FOLDS; i++) { 128 | folds[i] = new HashSet<>(); 129 | for (int j = 0; j < TOTAL_CASES / FOLDS; j++) { 130 | int index = -1; 131 | do { 132 | index = r.nextInt(TOTAL_CASES); 133 | } while (pickedCases.contains(index)); 134 | pickedCases.add(index); 135 | int k = 0; 136 | while (k < rangeMapper.length && index >= rangeMapper[k]) { 137 | k++; 138 | } 139 | 140 | folds[i].add(logMap.get(k)); 141 | } 142 | } 143 | 144 | return folds; 145 | } 146 | 147 | public static void validateAlgorithmPercentage() { 148 | createLog(); 149 | Set validationSet = new HashSet<>(); 150 | Map tmpMapa=new HashMap<>(); 151 | for (Integer key : logMap.keySet()) { 152 | validationSet.add(logMap.get(key)); 153 | tmpMapa.put(logMap.get(key), amountMapper[key]); 154 | } 155 | 156 | Set constructionSet = new HashSet<>(); 157 | for (int percent = 80; percent <= 95; percent += 5f) { 158 | System.out.println(percent); 159 | constructionSet.clear(); 160 | int amount = (int) (Float.parseFloat("" + percent) / 100.0 * TOTAL_CASES); 161 | for (int i = 0; i < rangeMapper.length && amount >= rangeMapper[i]; i++) { 162 | constructionSet.add(logMap.get(i)); 163 | } 164 | 165 | WorkflowNetwork wfNet = AlphaAlgorithm.discoverWorkflowNetwork(constructionSet); 166 | System.out.println("Construction set count: " 167 | + constructionSet.size()); 168 | int failedTraces = 0; 169 | int failedCases=0; 170 | boolean passes; 171 | for (Trace t : validationSet) { 172 | passes = wfNet.runTrace(t); 173 | if (!passes) { 174 | failedTraces++; 175 | failedCases+=tmpMapa.get(t); 176 | } 177 | } 178 | 179 | System.out.println("Failed validation traces: " + failedTraces); 180 | System.out.println("Failed validation cases: "+failedCases); 181 | } 182 | } 183 | 184 | public static void validateAlgorithmCrossFold() { 185 | createLog(); 186 | Set[] folds = getFolds(); 187 | for (int i = 0; i < folds.length; i++) { 188 | Set validationSet = folds[i]; 189 | Set constructionSet = new HashSet(); 190 | for (int j = 0; j < folds.length; j++) { 191 | if (j == i) { 192 | continue; 193 | } 194 | constructionSet.addAll(folds[j]); 195 | } 196 | 197 | WorkflowNetwork wfC = AlphaAlgorithm.discoverWorkflowNetwork(constructionSet); 198 | Place.nameGenerator = 'A'; 199 | boolean passes = true; 200 | System.out.println("Fold: " + i); 201 | System.out.println("Construction set count: " 202 | + constructionSet.size()); 203 | System.out.println("Validation set count: " + validationSet.size()); 204 | int fails = 0; 205 | for (Trace t : validationSet) { 206 | passes = wfC.runTrace(t); 207 | if (!passes) { 208 | fails++; 209 | } 210 | } 211 | 212 | System.out.println("Failed validation traces: " + fails); 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/main/java/edu/fcse/alphaalgorithm/WorkflowNetwork.java: -------------------------------------------------------------------------------- 1 | package edu.fcse.alphaalgorithm; 2 | 3 | import edu.fcse.alphaalgorithm.tools.Event; 4 | import edu.fcse.alphaalgorithm.tools.Pair; 5 | import edu.fcse.alphaalgorithm.tools.Place; 6 | import edu.fcse.alphaalgorithm.tools.Trace; 7 | 8 | import java.util.*; 9 | 10 | /** 11 | * Created by Blagoj Atanasovski 12 | */ 13 | public class WorkflowNetwork { 14 | private final Set eventsList; 15 | private final Set workflowPlaces; 16 | private final Set> eventToPlaceTransitions; 17 | private final Map> eventToPlaceTransitionsMap; 18 | private final Set> placeToEventTransitions; 19 | private final Place in; 20 | private final Place out; 21 | private final Map, Set>> eventPrePostMap; 22 | 23 | public WorkflowNetwork(Set workflowPlaces, 24 | Set eventsList, 25 | Set> eventToPlaceTransitions, 26 | Map> eventToPlaceTransitionsMap, 27 | Set> placeToEventTransitions, 28 | Place in, 29 | Place out) { 30 | this.workflowPlaces = workflowPlaces; 31 | this.eventsList = eventsList; 32 | this.eventToPlaceTransitions = eventToPlaceTransitions; 33 | this.eventToPlaceTransitionsMap = eventToPlaceTransitionsMap; 34 | this.placeToEventTransitions = placeToEventTransitions; 35 | this.in = in; 36 | this.out = out; 37 | this.eventPrePostMap = createActivityPrePostMap(this.workflowPlaces, this.eventsList); 38 | } 39 | 40 | public Set getWorkflowPlaces() { 41 | return workflowPlaces; 42 | } 43 | 44 | public Set getEventsList() { 45 | return eventsList; 46 | } 47 | 48 | public Set> getEventToPlaceTransitions() { 49 | return eventToPlaceTransitions; 50 | } 51 | 52 | public Map> getEventToPlaceTransitionsMap() { 53 | return eventToPlaceTransitionsMap; 54 | } 55 | 56 | public Set> getPlaceToEventTransitions() { 57 | return placeToEventTransitions; 58 | } 59 | 60 | public Place getIn() { 61 | return in; 62 | } 63 | 64 | public Place getOut() { 65 | return out; 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | Set eventList = this.getEventsList(); 71 | Set workflowPlaces = this.getWorkflowPlaces(); 72 | StringBuilder sb = new StringBuilder( 73 | 40 74 | * (this.getPlaceToEventTransitions().size() + this.getEventToPlaceTransitions() 75 | .size()) + eventList.size() + 15 76 | * workflowPlaces.size()); 77 | sb.append("Events:\n"); 78 | for (Event event : eventList) { 79 | sb.append(event).append(", "); 80 | } 81 | 82 | sb.append("\nPlaces:\n"); 83 | sb.append(in).append("\n"); 84 | for (Place place : workflowPlaces) { 85 | sb.append(place).append("\n"); 86 | } 87 | 88 | sb.append(out); 89 | sb.append("\n"); 90 | sb.append("Transitions:\n"); 91 | for (Pair transition : this.getPlaceToEventTransitions()) { 92 | sb.append(String.format("From place (%s) to event [%s]\n", 93 | transition.getFirst(), transition.getSecond())); 94 | } 95 | 96 | for (Pair transition : this.getEventToPlaceTransitions()) { 97 | sb.append(String.format("From event [%s] to place (%s)\n", 98 | transition.getFirst(), transition.getSecond())); 99 | } 100 | 101 | return sb.toString(); 102 | } 103 | 104 | private Map, Set>> createActivityPrePostMap(Set workflowPlaces, Set eventList) { 105 | Map, Set>> eventPrePostMap = new HashMap<>(); 106 | for (Event event : eventList) { 107 | Set first = new HashSet<>(); 108 | Set second = new HashSet<>(); 109 | eventPrePostMap.put(event, new Pair<>(first, second)); 110 | } 111 | 112 | for (Place p : workflowPlaces) { 113 | Set inA = p.getInEvents(); 114 | for (Event event : inA) { 115 | Pair, Set> pair = eventPrePostMap.get(event); 116 | pair.getSecond().add(p); 117 | } 118 | 119 | Set outA = p.getOutEvents(); 120 | for (Event activity : outA) { 121 | Pair, Set> pair = eventPrePostMap.get(activity); 122 | pair.getFirst().add(p); 123 | } 124 | } 125 | 126 | return eventPrePostMap; 127 | } 128 | 129 | public boolean runTrace(Trace trace) { 130 | this.getWorkflowPlaces().forEach(Place::clearToken); 131 | List currentTrace = trace.getEventsList(); 132 | in.putToken(); 133 | for (Event currentActivity : currentTrace) { 134 | // if out place has a token but trace is not finished 135 | if (out.hasToken()) { 136 | return false; 137 | } 138 | 139 | Pair, Set> actPrePost = eventPrePostMap 140 | .get(currentActivity); 141 | 142 | //activity not found in construction set event log 143 | if (actPrePost == null) { 144 | return false; 145 | } 146 | 147 | Set inPlaces = actPrePost.getFirst(); 148 | boolean enabled = true; 149 | for (Place p : inPlaces) { 150 | if (!p.hasToken()) { 151 | enabled = false; 152 | break; 153 | } 154 | } 155 | 156 | if (enabled) { 157 | inPlaces.forEach(Place::takeToken); 158 | actPrePost.getSecond().forEach(Place::putToken); 159 | } 160 | } 161 | 162 | if (out.hasToken()) { 163 | long numTokens = workflowPlaces.stream().filter(place -> place.hasToken()).count(); 164 | return numTokens == 1; 165 | } else { 166 | return false; 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/main/java/edu/fcse/alphaalgorithm/tools/Event.java: -------------------------------------------------------------------------------- 1 | package edu.fcse.alphaalgorithm.tools; 2 | 3 | /** 4 | * Created by Blagoj Atanasovski. 5 | */ 6 | public class Event { 7 | private String name; 8 | 9 | public Event(String name) { 10 | this.name = name; 11 | } 12 | 13 | public String getName() { 14 | return this.name; 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return this.name; 20 | } 21 | 22 | @Override 23 | public boolean equals(Object obj) { 24 | if (obj == null) { 25 | return false; 26 | } 27 | 28 | if (!(obj instanceof Event)) { 29 | return false; 30 | } 31 | 32 | return this.name.equals(((Event) obj).name); 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | return this.name.hashCode(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/edu/fcse/alphaalgorithm/tools/Footprint.java: -------------------------------------------------------------------------------- 1 | package edu.fcse.alphaalgorithm.tools; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * A wrapper for the footprint matrix of an event log. Relations between events 7 | * are represented by the {@code edu.fcse.alphaalgorithm.RelationType} enum. For 8 | * footprint[a][b] the result can be: # = events a and b are not connected > = 9 | * event a precedes b < = event a follows b | = a>b and a eventNameToMatrixIndex = new HashMap<>(); 16 | 17 | public Footprint(Set allEvents, Set eventLog, 18 | boolean lookForLoopsOfLengthTwo) { 19 | int index = 0; 20 | // assign each of the events an index in the matrix 21 | for (Event event : allEvents) { 22 | eventNameToMatrixIndex.put(event.getName(), index++); 23 | } 24 | 25 | int numberOfEvents = allEvents.size(); 26 | // In the beginning there were no connections 27 | footprint = new RelationType[numberOfEvents][numberOfEvents]; 28 | for(RelationType[] row: footprint){ 29 | Arrays.fill(row,RelationType.NOT_CONNECTED); 30 | } 31 | 32 | // And then God said, let there be a Trace 33 | for (Trace singleTrace : eventLog) { 34 | List eventsList = singleTrace.getEventsList(); 35 | for (int i = 0; i < eventsList.size() - 1; i++) { 36 | // currentEventNumber is followed by nextEventNumber in some 37 | // trace 38 | int currentEventNumber = eventNameToMatrixIndex 39 | .get(eventsList.get(i).getName()); 40 | int nextEventNumber = eventNameToMatrixIndex 41 | .get(eventsList.get(i + 1).getName()); 42 | // if this is the first time these two have been 43 | // found next to each other 44 | if (footprint[currentEventNumber][nextEventNumber] == RelationType.NOT_CONNECTED) { 45 | footprint[currentEventNumber][nextEventNumber] = RelationType.PRECEDES; 46 | footprint[nextEventNumber][currentEventNumber] = RelationType.FOLLOWS; 47 | } else if (footprint[currentEventNumber][nextEventNumber] == RelationType.FOLLOWS) { 48 | // if nextEventNumber was before currEventNumber 49 | // in some trace 50 | footprint[currentEventNumber][nextEventNumber] = RelationType.PARALLEL; 51 | footprint[nextEventNumber][currentEventNumber] = RelationType.PARALLEL; 52 | } 53 | 54 | // if some of the other relation types are at this position, 55 | // they are not changed 56 | } 57 | } 58 | 59 | if (lookForLoopsOfLengthTwo) { 60 | for (Trace singleTrace : eventLog) { 61 | List eventsList = singleTrace.getEventsList(); 62 | for (int i = 0; i < eventsList.size() - 2; i++) { 63 | if (eventsList.get(i).equals(eventsList.get(i + 2))) { 64 | int currentEventNumber = eventNameToMatrixIndex 65 | .get(eventsList.get(i).getName()); 66 | int nextEventNumber = eventNameToMatrixIndex 67 | .get(eventsList.get(i + 1).getName()); 68 | footprint[currentEventNumber][nextEventNumber] = RelationType.PRECEDES; 69 | footprint[nextEventNumber][currentEventNumber] = RelationType.PRECEDES; 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | public RelationType getRelationType(Event firstEvent, Event secondEvent) { 77 | int rowIndex = eventNameToMatrixIndex.get(firstEvent.getName()); 78 | int colIndex = eventNameToMatrixIndex.get(secondEvent.getName()); 79 | return footprint[rowIndex][colIndex]; 80 | } 81 | 82 | /** 83 | * @param firstEvent name of one event (i.e. a) 84 | * @param secondEvent name of another event (i.e. b) 85 | * @return false if a # b, true otherwise 86 | */ 87 | public boolean areConnected(Event firstEvent, Event secondEvent) { 88 | return getRelationType(firstEvent, secondEvent) != RelationType.NOT_CONNECTED; 89 | } 90 | 91 | /** 92 | * @param firstEvent name of one event (i.e. a) 93 | * @param secondEvent name of another event (i.e. b) 94 | * @return true if a>b, false otherwise 95 | */ 96 | public boolean isFirstFollowedBySecond(Event firstEvent, Event secondEvent) { 97 | return getRelationType(firstEvent, secondEvent) == RelationType.PRECEDES; 98 | } 99 | 100 | public boolean areEventsConnected(Set inputEvents, 101 | Set outputEvents) { 102 | /* 103 | * (A,B), A = first, B = second 104 | */ 105 | // For every a1,a2 in A => a1#a2 106 | boolean areInputEventsConnectedBetweenThemselves = inputEvents.stream() 107 | .anyMatch(inputEvent1 -> 108 | inputEvents.stream() 109 | .anyMatch(inputEvent2 -> areConnected(inputEvent1, inputEvent2))); 110 | 111 | if (areInputEventsConnectedBetweenThemselves) { 112 | return false; 113 | } 114 | 115 | // For every b1, b2 in B => b1#b2 116 | boolean areOutputEventsConnectedBetweenThemselves = outputEvents.stream() 117 | .anyMatch(outputEvent -> 118 | outputEvents.stream() 119 | .anyMatch(outputEvent2 -> areConnected(outputEvent, outputEvent2))); 120 | if (areOutputEventsConnectedBetweenThemselves) { 121 | return false; 122 | } 123 | 124 | // For every a in A and b in B => a > b in f 125 | boolean allFromBFollowAllFromA = inputEvents.stream() 126 | .allMatch(inputEvent -> 127 | outputEvents.stream() 128 | .allMatch(outputEvent -> isFirstFollowedBySecond(inputEvent, outputEvent))); 129 | return allFromBFollowAllFromA; 130 | } 131 | 132 | @Override 133 | public String toString() { 134 | StringBuilder toReturn = new StringBuilder(" "); 135 | String[] tmp = new String[footprint.length]; 136 | for (String key : eventNameToMatrixIndex.keySet()) { 137 | tmp[eventNameToMatrixIndex.get(key)] = key; 138 | } 139 | 140 | for (int i = 0; i < footprint.length; i++) { 141 | toReturn.append(tmp[i]).append(' '); 142 | } 143 | 144 | toReturn.append('\n'); 145 | for (int i = 0; i < footprint.length; i++) { 146 | toReturn.append(tmp[i]).append(' '); 147 | for (int j = 0; j < footprint.length; j++) { 148 | toReturn.append(footprint[i][j].symbol()).append(' '); 149 | } 150 | 151 | toReturn.append('\n'); 152 | } 153 | 154 | return toReturn.toString(); 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/edu/fcse/alphaalgorithm/tools/LoopLengthOne.java: -------------------------------------------------------------------------------- 1 | package edu.fcse.alphaalgorithm.tools; 2 | 3 | public class LoopLengthOne { 4 | Event prevEvent; 5 | Event loopedEvent; 6 | Event nextEvent; 7 | 8 | public LoopLengthOne(Event prevEvent, Event loopedEvent, 9 | Event nextEvent) { 10 | this.prevEvent = prevEvent; 11 | this.loopedEvent = loopedEvent; 12 | this.nextEvent = nextEvent; 13 | } 14 | 15 | public Event getPrevEvent() { 16 | return prevEvent; 17 | } 18 | 19 | public void setPrevEvent(Event prevEvent) { 20 | this.prevEvent = prevEvent; 21 | } 22 | 23 | public Event getLoopedEvent() { 24 | return loopedEvent; 25 | } 26 | 27 | public void setLoopedEvent(Event loopedEvent) { 28 | this.loopedEvent = loopedEvent; 29 | } 30 | 31 | public Event getNextEvent() { 32 | return nextEvent; 33 | } 34 | 35 | public void setNextEvent(Event nextEvent) { 36 | this.nextEvent = nextEvent; 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "LoopLengthOne [prev=" + prevEvent + ", looped=" 42 | + loopedEvent + ", next=" + nextEvent + "]"; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/edu/fcse/alphaalgorithm/tools/Pair.java: -------------------------------------------------------------------------------- 1 | package edu.fcse.alphaalgorithm.tools; 2 | 3 | public class Pair { 4 | private E first; 5 | private T second; 6 | 7 | public E getFirst() { 8 | return first; 9 | } 10 | 11 | public void setFirst(E first) { 12 | this.first = first; 13 | } 14 | 15 | public T getSecond() { 16 | return second; 17 | } 18 | 19 | public void setSecond(T second) { 20 | this.second = second; 21 | } 22 | 23 | @Override 24 | public int hashCode() { 25 | final int prime = 31; 26 | int result = 1; 27 | result = prime * result + ((first == null) ? 0 : first.hashCode()); 28 | result = prime * result + ((second == null) ? 0 : second.hashCode()); 29 | return result; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object obj) { 34 | if (this == obj) 35 | return true; 36 | if (obj == null) 37 | return false; 38 | if (getClass() != obj.getClass()) 39 | return false; 40 | 41 | Pair other = (Pair) obj; 42 | if (first == null) { 43 | if (other.first != null) 44 | return false; 45 | } else if (!first.equals(other.first)) 46 | return false; 47 | if (second == null) { 48 | if (other.second != null) 49 | return false; 50 | } else if (!second.equals(other.second)) 51 | return false; 52 | return true; 53 | } 54 | 55 | public Pair(E first, T second) { 56 | super(); 57 | this.first = first; 58 | this.second = second; 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return "(" + first + "; " + second + ")"; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/edu/fcse/alphaalgorithm/tools/Place.java: -------------------------------------------------------------------------------- 1 | package edu.fcse.alphaalgorithm.tools; 2 | 3 | import java.util.Set; 4 | 5 | public class Place { 6 | // (Input Activities, Output Activities) 7 | Pair, Set> eventsPair; 8 | /** 9 | * The name of the place is merely symbolic, plays no part in the 10 | * identification of the Place. See {@link #equals(Object)} 11 | */ 12 | String name; 13 | public static char nameGenerator = 'A'; 14 | 15 | private boolean token; 16 | 17 | public Place(Set in, Set out) { 18 | eventsPair = new Pair<>(in, out); 19 | name = "" + nameGenerator++; 20 | } 21 | 22 | public Place(String name, Set in, Set out) { 23 | eventsPair = new Pair<>(in, out); 24 | this.name = name; 25 | } 26 | 27 | public Set getInEvents() { 28 | return eventsPair.getFirst(); 29 | } 30 | 31 | public Set getOutEvents() { 32 | return eventsPair.getSecond(); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return String.format("%s: %s", name, eventsPair); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | final int prime = 31; 43 | int result = 1; 44 | result = prime * result 45 | + ((eventsPair == null) ? 0 : eventsPair.hashCode()); 46 | return result; 47 | } 48 | 49 | @Override 50 | public boolean equals(Object obj) { 51 | if (this == obj) 52 | return true; 53 | if (obj == null) 54 | return false; 55 | if (getClass() != obj.getClass()) 56 | return false; 57 | Place other = (Place) obj; 58 | if (eventsPair == null) { 59 | if (other.eventsPair != null) 60 | return false; 61 | } else if (!eventsPair.equals(other.eventsPair)) 62 | return false; 63 | return true; 64 | } 65 | 66 | /** 67 | * Required for step 5 in the Alpha Algorithm (Generating Yl). Check if a 68 | * the input and output places are supersets to the input and output places 69 | * of another place. Place A with input events {a} and output events {b,e} 70 | * is a superplace of Place B with input {a} and output {b} => Place B can 71 | * be discarded. 72 | * 73 | * @param potentialSubPlace 74 | * @return true if potentialSubPlace.inEvents ⊆ this.inEvents && 75 | * potentialSubPlace.outEvents ⊆ this.outEvents 76 | */ 77 | public boolean isSuperPlace(Place potentialSubPlace) { 78 | if (getInEvents().containsAll(potentialSubPlace.getInEvents())) { 79 | if (getOutEvents().containsAll( 80 | potentialSubPlace.getOutEvents())) { 81 | return true; 82 | } 83 | } 84 | return false; 85 | } 86 | 87 | public void addInEvent(Event event) { 88 | eventsPair.getFirst().add(event); 89 | } 90 | 91 | public void addOutEvent(Event eventName) { 92 | eventsPair.getSecond().add(eventName); 93 | } 94 | 95 | public String getName() { 96 | return name; 97 | } 98 | 99 | public boolean hasToken() { 100 | return token; 101 | } 102 | 103 | public void putToken() { 104 | token = true; 105 | } 106 | 107 | public void takeToken() { 108 | token = false; 109 | } 110 | 111 | public void clearToken(){ token = false;} 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/edu/fcse/alphaalgorithm/tools/RelationType.java: -------------------------------------------------------------------------------- 1 | package edu.fcse.alphaalgorithm.tools; 2 | 3 | public enum RelationType { 4 | // -> 5 | PRECEDES('>'), 6 | // <- 7 | FOLLOWS('<'), 8 | // || 9 | PARALLEL('|'), 10 | // # 11 | NOT_CONNECTED('#'); 12 | 13 | RelationType(char symbol) { 14 | this.sym = symbol; 15 | } 16 | 17 | private final char sym; 18 | 19 | public char symbol() { 20 | return sym; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/edu/fcse/alphaalgorithm/tools/Trace.java: -------------------------------------------------------------------------------- 1 | package edu.fcse.alphaalgorithm.tools; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.Iterator; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | /** 10 | * A wrapper class for a list of events. Each event is represented by it's name. 11 | * 12 | * @author blagoj atanasovski 13 | */ 14 | public class Trace implements Iterable { 15 | private List eventsList; 16 | 17 | public Trace() { 18 | eventsList = new LinkedList<>(); 19 | } 20 | 21 | public Trace(String[] eventsArray) { 22 | eventsList = new ArrayList<>(eventsArray.length); 23 | for (String event : eventsArray) { 24 | eventsList.add(new Event(event)); 25 | } 26 | } 27 | 28 | public Trace(Collection events) { 29 | eventsList = new ArrayList<>(events); 30 | } 31 | 32 | public void addEvent(Event event) { 33 | eventsList.add(event); 34 | } 35 | 36 | @Override 37 | public Iterator iterator() { 38 | return eventsList.iterator(); 39 | } 40 | 41 | @Override 42 | public boolean equals(Object obj) { 43 | if (!(obj instanceof Trace)) { 44 | return false; 45 | } 46 | Trace other = (Trace) obj; 47 | return this.eventsList.equals(other.eventsList); 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return eventsList.hashCode(); 53 | } 54 | 55 | public List getEventsList() { 56 | return eventsList; 57 | } 58 | 59 | public Event getFirstEvent() { 60 | return eventsList.get(0); 61 | } 62 | 63 | public Event getLastEvent() { 64 | return eventsList.get(eventsList.size() - 1); 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return "Trace [eventsList=" + eventsList + "]"; 70 | } 71 | 72 | public void clear() { 73 | eventsList.clear(); 74 | } 75 | 76 | public int size() { 77 | return eventsList.size(); 78 | } 79 | 80 | } 81 | --------------------------------------------------------------------------------