/// <summary> /// Build a List of Columns from a PetriNet /// </summary> /// <param name="petriNet"></param> /// <returns></returns> /// <author>Jannik Arndt</author> public static List <Column> Build(PetriNet petriNet) { // Initialize GlobalColumnList.Clear(); Column firstColumn = new Column(0); Place startPlace = (Place)petriNet.GetSources()[0]; firstColumn.HashSetOfNodes.Add(startPlace); GlobalColumnList.Add(firstColumn); // Recursively add all columns AddPlace(startPlace, 1); // Correct the end-place Place endPlace = (Place)petriNet.GetSinks()[0]; Column temp = FindNode(endPlace); if (temp != null && temp.ColumnNumber < GlobalColumnList.Count) { temp.HashSetOfNodes.Remove(endPlace); TrimGlobalColumnList(); GetOrCreateColumn(GlobalColumnList.Count).HashSetOfNodes.Add(endPlace); } return(GlobalColumnList); }
/// <summary> /// Remove old "End"-Places, combine sinks and add new "End"-Place /// </summary> /// <param name="petriNet">The petrinet to work on</param> /// <author>Jannik Arndt</author> public void FixEnding(PetriNet petriNet) { foreach (Place endPlace in petriNet.Places.Where(place => place.Name.Equals("End"))) { endPlace.Name = ""; } List <Place> endPlaces = (from sink in petriNet.GetSinks() where sink.GetType() == typeof(Place) select sink as Place).ToList(); if (endPlaces.Count > 1) { foreach (Place endPlace in endPlaces.Skip(1)) { foreach (Transition incomingTransition in endPlace.IncomingTransitions) { endPlaces[0].IncomingTransitions.Add(incomingTransition); incomingTransition.OutgoingPlaces.Clear(); incomingTransition.OutgoingPlaces.Add(endPlaces[0]); } petriNet.Places.Remove(endPlace); } } endPlaces[0].Name = "End"; }
/// <summary> /// Goes through the steps of the (improved) HeuristicMiner Algorithm: /// Find starting-events => build adjacency-matrix => convert into petrinet /// </summary> /// <returns>A PetriNet as a ProcessModel</returns> /// <exception cref="ArgumentNullException">If the field-parameter is null.</exception> /// <author>Jannik Arndt</author> public ProcessModel Mine() { if (_field == null) { throw new Exception("The field parameter was null"); } // Get parameters from MinerSettings var threshold = MinerSettings.GetAsDouble("AdjacencyThresholdSlider") / 100; var maxDepth = MinerSettings.GetAsInt("MaximumRecursionDepthSlider"); if (threshold < 0 || threshold > 1) { throw new Exception("Threshold must be between 0 and 1."); } if (maxDepth < 1) { throw new Exception("Maximum recursion depth must be more greater than 1."); } // Statistics var processingTimeStart = Process.GetCurrentProcess().TotalProcessorTime; // 1. Create Adjacency Matrix var adjacencyMatrix = CreateAdjacencyMatrix(_field); // 2. Calculate the indexes of the first events var startindexes = GetColumnsWithOnlyNegativeEntries(adjacencyMatrix); // for every sub-processmodel foreach (var startindex in startindexes) { // 3. Create the dependency-Graph var rootNode = CreateDependencyGraph(_field.EventLog.ListOfUniqueEvents, adjacencyMatrix, threshold, 0.0, startindex, maxDepth); // 4. Turn the dependency-graph into a petri-net _listOfSubPetriNets.Add(CreatePetriNetFromDependencyGraph(rootNode, _field.EventLog, _field.Infotext)); } // 5. Connect all Petrinets by one XOR-Split if (_listOfSubPetriNets.Count > 1) { // 5a. Connect all subnets to one start var startingPlace = _finalPetriNet.AddPlace("Start"); foreach (var petriNet in _listOfSubPetriNets) { var xorTransition = _finalPetriNet.AddTransition(incomingPlace: startingPlace); _finalPetriNet.MergeWithPetriNet(petriNet, xorTransition); } // 5b. Add following transitions to all subnet-endings foreach (var openEndPlace in _finalPetriNet.GetSinks()) { if (openEndPlace.GetType() == typeof(Place)) { _finalPetriNet.AddTransition(incomingPlace: (Place)openEndPlace); } } } else { _finalPetriNet = _listOfSubPetriNets[0]; } // 6. Connect all open endings _finalPetriNet = FixEnding(_finalPetriNet); // 7. Delete empty transitions _finalPetriNet = PetriNetOperation.CleanUpPetriNet(_finalPetriNet); // 8. Save information about the mining-process var processingTimeEnd = Process.GetCurrentProcess().TotalProcessorTime; var processingTime = Math.Abs((processingTimeEnd - processingTimeStart).TotalMilliseconds) < 1 ? "< 15 ms" : (processingTimeEnd - processingTimeStart).TotalMilliseconds + " ms"; _field.Information.Add("Processing Time", processingTime); _field.Information.Add("Number of Events", _finalPetriNet.Transitions.Count(s => s.IsLoop == false).ToString(CultureInfo.InvariantCulture)); _field.Information.Add("Number of Transitions", _finalPetriNet.Transitions.Count.ToString(CultureInfo.InvariantCulture)); _field.Information.Add("Number of Places", _finalPetriNet.Places.Count.ToString(CultureInfo.InvariantCulture)); _field.Information.Add("Loops in the net", _finalPetriNet.CountLoops().ToString(CultureInfo.InvariantCulture)); _field.Information.Add("Events used", _finalPetriNet.CountTransitionsWithoutANDs() - _finalPetriNet.CountLoops() + " of " + _field.EventLog.ListOfUniqueEvents.Count); _field.Information.Add("Parallel Models", startindexes.Count.ToString(CultureInfo.InvariantCulture)); if (_listOfNodes.Count > 0) { _field.Information.Add("Minimal Adjacency", _listOfNodes.Min().ToString(CultureInfo.InvariantCulture)); _field.Information.Add("Average Adjacency", Math.Round(_listOfNodes.Average(), 3).ToString(CultureInfo.InvariantCulture)); if (Math.Abs(_listOfNodes.Min() - _listOfNodes.Average()) > 0.0001) { _field.Information.Add("Standard Deviation", CalculateStandardDeviation(_listOfNodes).ToString(CultureInfo.InvariantCulture)); } } return(_finalPetriNet); }