public void CleanUpPetriNetTest()
        {
            var petriNet = new Model.PetriNet.PetriNet();
            var place1   = petriNet.AddPlace("1");
            var place2   = petriNet.AddPlace("2");
            var place3   = petriNet.AddPlace("3");
            var place4   = petriNet.AddPlace("4");

            petriNet.AddTransition("eins", incomingPlace: place1, outgoingPlace: place2);
            petriNet.AddTransition("", incomingPlace: place2, outgoingPlace: place3);
            petriNet.AddTransition("drei", incomingPlace: place3, outgoingPlace: place4);

            petriNet = PetriNetOperation.CleanUpPetriNet(petriNet);
            Assert.IsTrue(petriNet.Transitions.Count == 2 && petriNet.Places.Count == 3, "Transitions =" + petriNet.Transitions.Count + " Places =" + petriNet.Places.Count);
        }
Beispiel #2
0
        /// <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);
        }