public void ToStringTest()
        {
            var event1 = new Event("A");
            var node = new EventNode(event1, 0.8);

            Assert.AreEqual("A", node.ToString());
        }
        public void EventNodeTest()
        {
            var event1 = new Event("A");
            var node = new EventNode(event1, 0.8);

            Assert.AreEqual("A", node.InnerEvent.Name);
            Assert.AreEqual(0.8, node.Adjacency);
            Assert.IsNotNull(node.ListOfFollowers);
        }
        public void FindNodeTest()
        {
            var event1 = new Event("A");
            var event2 = new Event("B");
            var event3 = new Event("C");

            var eventNode = new EventNode(event1, 0.8, new List<EventNode> { new EventNode(event2, 0.8, new List<EventNode> { new EventNode(event3, 0.8) }) });

            Assert.AreEqual("C", eventNode.FindNode("C").InnerEvent.Name);
            Assert.IsNull(eventNode.FindNode("D"));
        }
        public void CreateDependencyGraphTest()
        {
            var field = new Field { EventLog = EventLogExample.ThreeCaseEventLog() };
            var miner = new HeuristicMiner(field);
            var event1 = new Event("A");
            var event2 = new Event("B");
            var event3 = new Event("C");
            var eventList = new List<Event> { event1, event2, event3 };
            var matrix = new[,] { { 0.0, 0.8, 0.0 }, { 0.0, 0.0, 0.8 }, { 0.0, 0.0, 0.0 } };

            var actual = miner.CreateDependencyGraph(eventList, matrix, 0.5, 0.0, 0, 10);

            var expected = new EventNode(event1, 0.8, new List<EventNode> { new EventNode(event2, 0.8, new List<EventNode> { new EventNode(event3, 0.8) }) });

            Assert.AreEqual(expected.InnerEvent, actual.InnerEvent);
            Assert.AreEqual(expected.ListOfFollowers[0].InnerEvent, actual.ListOfFollowers[0].InnerEvent);
            Assert.AreEqual(expected.ListOfFollowers[0].ListOfFollowers[0].InnerEvent, actual.ListOfFollowers[0].ListOfFollowers[0].InnerEvent);
        }
示例#5
0
        /// <summary>
        /// A recursive function to call the next level of HandleNode. Also closes open XORs and ANDs right away.
        /// </summary>
        /// <param name="trans"></param>
        /// <param name="node"></param>
        /// <param name="log"></param>
        /// <param name="petriNet"></param>
        /// <author>Jannik Arndt</author>
        public void HandleNodesAndCloseParallelisms(Transition trans, EventNode node, EventLog log, PetriNet petriNet)
        {
            var transitions = HandleNode(trans, node, log, petriNet);

            if (transitions == null)
            {
                return;
            }

            foreach (var transition in transitions)
            {
                foreach (var eventNode in node.ListOfFollowers)
                {
                    if (transition.Name == eventNode.InnerEvent.Name || transition.IsANDJoin)
                    {
                        HandleNodesAndCloseParallelisms(transition, eventNode, log, petriNet);
                    }
                }
            }
        }
示例#6
0
        /// <summary>
        /// Resolves a construct of three parallel events: a AND ( b XOR c )
        /// </summary>
        /// <param name="event1">One event</param>
        /// <param name="a">An event node</param>
        /// <param name="b">An event node</param>
        /// <param name="c">An event node</param>
        /// <param name="petriNet">Current petri net</param>
        /// <returns>A list with final transitions</returns>
        /// <author>Jannik Arndt</author>
        public List <Transition> StartAand_BxorC(Transition event1, EventNode a, EventNode b, EventNode c, PetriNet petriNet)
        {
            var finalTransition = petriNet.AddTransition("AND Join");

            finalTransition.IsANDJoin = true;

            var andTransition = StartAND(event1, new List <EventNode> {
                a
            }, petriNet);

            EndAND(andTransition, petriNet, finalTransition);

            var xorTransition = StartXOR(event1, new List <EventNode> {
                b, c
            }, petriNet);

            EndXOR(xorTransition, petriNet, finalTransition);

            return(new List <Transition> {
                finalTransition
            });
        }
示例#7
0
        /// <summary>
        /// Recursive method to build the dependency graph, which essentially is a bunch of EventNodes put together.
        /// </summary>
        /// <param name="eventlist">A list of all events, automatically generated when creating the dictionary.</param>
        /// <param name="adjacencyMatrix">A filled adjacency matrix.</param>
        /// <param name="threshold">A Value between 0.5 and 1</param>
        /// <param name="adjacency">The current adjacency</param>
        /// <param name="row">The index of the first event.</param>
        /// <param name="maxDepth">Since this is recursive, there is the danger of a StackOverflow, this prevents it. If you choose wisely.</param>
        /// <returns>The root-EventNode of the graph.</returns>
        /// <author>Jannik Arndt</author>
        public EventNode CreateDependencyGraph(List <Event> eventlist, double[,] adjacencyMatrix, double threshold, double adjacency, int row, int maxDepth)
        {
            // exit condition
            if (maxDepth < 1)
            {
                return(null);
            }

            var eventNode = new EventNode(eventlist[row], adjacency);

            // build a flat list as well
            if (adjacency > threshold)
            {
                _listOfNodes.Add(adjacency);
            }

            // Recursively add all child-nodes
            foreach (var index in GetFollowingEventsIndexes(adjacencyMatrix, row, threshold))
            {
                eventNode.ListOfFollowers.Add(CreateDependencyGraph(eventlist, adjacencyMatrix, threshold, adjacencyMatrix[row, index], index, maxDepth - 1));
            }

            return(eventNode);
        }
        /// <summary>
        /// Resolves a construct of three parallel events: a XOR ( b AND c )
        /// </summary>
        /// <param name="event1">Event number one</param>
        /// <param name="a">An Event node</param>
        /// <param name="b">An Event node</param>
        /// <param name="c">An Event node</param>
        /// <param name="petriNet">The current petri net</param>
        /// <returns>A list with final transition</returns>
        /// <author>Jannik Arndt</author>
        public List<Transition> StartAxor_BandC(Transition event1, EventNode a, EventNode b, EventNode c, PetriNet petriNet)
        {
            var finalTransition = petriNet.AddTransition(); // empty transition, will possibly be cleaned up
            finalTransition.Name = FindFirstCommonSuccessor(new List<EventNode> { a, b, c });

            var xorTransition = StartXOR(event1, new List<EventNode> { a }, petriNet);
            EndXOR(xorTransition, petriNet, finalTransition);

            var andSplit = petriNet.AddTransition(); // no idea where this disappears...
            andSplit.AddIncomingPlace(xorTransition[0].IncomingPlaces[0]); // XOR-Split-Place

            var andTransition = StartAND(andSplit, new List<EventNode> { b, c }, petriNet);
            var andJoin = petriNet.AddTransition("AND Join");
            andJoin.IsANDJoin = true;
            var andTransition2 = EndAND(andTransition, petriNet, andJoin);

            andTransition2[0].AddOutgoingPlace(finalTransition.IncomingPlaces.FirstOrDefault());

            return new List<Transition> { finalTransition };
        }
        /// <summary>
        /// Resolves a construct of three parallel events: a AND ( b XOR c )        
        /// </summary>
        /// <param name="event1">One event</param>
        /// <param name="a">An event node</param>
        /// <param name="b">An event node</param>
        /// <param name="c">An event node</param>
        /// <param name="petriNet">Current petri net</param>
        /// <returns>A list with final transitions</returns>
        /// <author>Jannik Arndt</author>
        public List<Transition> StartAand_BxorC(Transition event1, EventNode a, EventNode b, EventNode c, PetriNet petriNet)
        {
            var finalTransition = petriNet.AddTransition("AND Join");
            finalTransition.IsANDJoin = true;

            var andTransition = StartAND(event1, new List<EventNode> { a }, petriNet);
            EndAND(andTransition, petriNet, finalTransition);

            var xorTransition = StartXOR(event1, new List<EventNode> { b, c }, petriNet);
            EndXOR(xorTransition, petriNet, finalTransition);

            return new List<Transition> { finalTransition };
        }
        /// <summary>
        /// A recursive function to call the next level of HandleNode. Also closes open XORs and ANDs right away.
        /// </summary>
        /// <param name="trans"></param>
        /// <param name="node"></param>
        /// <param name="log"></param>
        /// <param name="petriNet"></param>
        /// <author>Jannik Arndt</author>
        public void HandleNodesAndCloseParallelisms(Transition trans, EventNode node, EventLog log, PetriNet petriNet)
        {
            var transitions = HandleNode(trans, node, log, petriNet);
            if (transitions == null)
                return;

            foreach (var transition in transitions)
                foreach (var eventNode in node.ListOfFollowers)
                    if (transition.Name == eventNode.InnerEvent.Name || transition.IsANDJoin)
                        HandleNodesAndCloseParallelisms(transition, eventNode, log, petriNet);
        }
        /// <summary>
        /// Depending on the count of followers this adds one (or more) places and their following transitions.
        /// </summary>
        /// <param name="event1"></param>
        /// <param name="node">An event node</param>
        /// <param name="log">The current event log</param>
        /// <param name="petriNet"></param>
        /// <returns>The newly added transitions. This is where you need to continue working.</returns>
        /// <exception cref="NotImplementedException">If there are more than 3 followers in a non-trivial relation.</exception>
        /// <author>Jannik Arndt</author>
        public List<Transition> HandleNode(Transition event1, EventNode node, EventLog log, PetriNet petriNet)
        {
            // Case: No followers
            if (node.ListOfFollowers.Count == 0)
                return new List<Transition>();

            // one or more more followers => count the AND-relations
            var andRelations = CountANDRelations(node, log);

            // Case: All nodes are AND-related
            if (andRelations == node.ListOfFollowers.Count)
                return StartAND(event1, node.ListOfFollowers, petriNet);

            // Case: All nodes are XOR-related
            if (andRelations == 0)
                return StartXOR(event1, node.ListOfFollowers, petriNet);

            // Case: 3 Followers
            if (node.ListOfFollowers.Count == 3)
            {
                var x = node;
                var a = node.ListOfFollowers[0];
                var b = node.ListOfFollowers[1];
                var c = node.ListOfFollowers[2];

                if (andRelations == 2) // XOR-Relations == 1
                {
                    // There are two and-relations and one xor-relation. Find the xor and order the parameters accordingly
                    if (IsXorRelation(x.InnerEvent, b.InnerEvent, c.InnerEvent, log))
                        return StartAand_BxorC(event1, a, b, c, petriNet);
                    if (IsXorRelation(x.InnerEvent, a.InnerEvent, c.InnerEvent, log))
                        return StartAand_BxorC(event1, b, a, c, petriNet);
                    if (IsXorRelation(x.InnerEvent, a.InnerEvent, b.InnerEvent, log))
                        return StartAand_BxorC(event1, c, a, b, petriNet);
                }
                else // XOR-Relations == 2 && AND-Relations == 1
                {
                    // There are two xor-relations and one and-relation. Find the and and order the parameters accordingly
                    if (IsAndRelation(x.InnerEvent, b.InnerEvent, c.InnerEvent, log))
                        return StartAxor_BandC(event1, a, b, c, petriNet);
                    if (IsAndRelation(x.InnerEvent, a.InnerEvent, c.InnerEvent, log))
                        return StartAxor_BandC(event1, b, a, c, petriNet);
                    if (IsAndRelation(x.InnerEvent, a.InnerEvent, b.InnerEvent, log))
                        return StartAxor_BandC(event1, c, a, b, petriNet);
                }
            }
            if (node.ListOfFollowers.Count > 3)
                return StartXOR(event1, node.ListOfFollowers, petriNet);

            // optional transition
            if (node.ListOfFollowers.Count == 2)
                if (log.EventFollowsEvent(node.ListOfFollowers[0].InnerEvent, node.ListOfFollowers[1].InnerEvent) > 0)
                    return StartOptionalTransition(event1, node.ListOfFollowers[0].InnerEvent.Name, node.ListOfFollowers[1].InnerEvent.Name, petriNet);
                else if (log.EventFollowsEvent(node.ListOfFollowers[1].InnerEvent, node.ListOfFollowers[0].InnerEvent) > 0)
                    return StartOptionalTransition(event1, node.ListOfFollowers[1].InnerEvent.Name, node.ListOfFollowers[0].InnerEvent.Name, petriNet);

            return null;
        }
        /// <summary>
        /// Goes through the dependency-graph and creates a petrinet.
        /// </summary>
        /// <param name="dependencyGraph">An EventNode from CreateDependencyGraph()</param>
        /// <param name="eventLog">The EventLog to calculate AND and XOR-relations</param>
        /// <param name="name">The name of the new PetriNet</param>
        /// <returns>A complete petrinet</returns>
        /// <author>Jannik Arndt, Bernhard Bruns</author>
        public PetriNet CreatePetriNetFromDependencyGraph(EventNode dependencyGraph, EventLog eventLog, string name)
        {
            var petriNet = new PetriNet(name);

            // 1. first event
            var startingPlace = petriNet.AddPlace("", 1);
            var firstEvent = petriNet.AddTransition(dependencyGraph.InnerEvent.Name, incomingPlace: startingPlace);

            // 2. Go through the net and turn Nodes into Places and Transitions, with parallel structures
            HandleNodesAndCloseParallelisms(firstEvent, dependencyGraph, eventLog, petriNet);

            // 3. handle loops
            petriNet = HandleLoops(eventLog, petriNet);

            return petriNet;
        }
        /// <summary>
        /// Recursive method to build the dependency graph, which essentially is a bunch of EventNodes put together.
        /// </summary>
        /// <param name="eventlist">A list of all events, automatically generated when creating the dictionary.</param>
        /// <param name="adjacencyMatrix">A filled adjacency matrix.</param>
        /// <param name="threshold">A Value between 0.5 and 1</param>
        /// <param name="adjacency">The current adjacency</param>
        /// <param name="row">The index of the first event.</param>
        /// <param name="maxDepth">Since this is recursive, there is the danger of a StackOverflow, this prevents it. If you choose wisely.</param>
        /// <returns>The root-EventNode of the graph.</returns>
        /// <author>Jannik Arndt</author>
        public EventNode CreateDependencyGraph(List<Event> eventlist, double[,] adjacencyMatrix, double threshold, double adjacency, int row, int maxDepth)
        {
            // exit condition
            if (maxDepth < 1)
                return null;

            var eventNode = new EventNode(eventlist[row], adjacency);

            // build a flat list as well
            if (adjacency > threshold)
                _listOfNodes.Add(adjacency);

            // Recursively add all child-nodes
            foreach (var index in GetFollowingEventsIndexes(adjacencyMatrix, row, threshold))
                eventNode.ListOfFollowers.Add(CreateDependencyGraph(eventlist, adjacencyMatrix, threshold, adjacencyMatrix[row, index], index, maxDepth - 1));

            return eventNode;
        }
 /// <summary>
 /// Counts the amount of following AND-relations to a given node
 /// </summary>
 /// <param name="node">Current event node</param>
 /// <param name="log">Current event log</param>
 /// <returns>Returns the counted number of and-relations</returns>
 /// <author>Jannik Arndt</author>
 public int CountANDRelations(EventNode node, EventLog log)
 {
     var result = 0;
     for (var index = 0; index < node.ListOfFollowers.Count; index++)
         for (var index2 = index + 1; index2 < node.ListOfFollowers.Count; index2++)
         {
             try
             {
                 if (node.ListOfFollowers[index] != null && node.ListOfFollowers[index2] != null)
                     if (IsAndRelation(node.InnerEvent, node.ListOfFollowers[index].InnerEvent, node.ListOfFollowers[index2].InnerEvent, log))
                         result++;
             }
             catch (NullReferenceException)
             {
                 throw new NullReferenceException("Cannot count AND-relations");
             }
         }
     return result;
 }
示例#15
0
        /// <summary>
        /// Resolves a construct of three parallel events: a XOR ( b AND c )
        /// </summary>
        /// <param name="event1">Event number one</param>
        /// <param name="a">An Event node</param>
        /// <param name="b">An Event node</param>
        /// <param name="c">An Event node</param>
        /// <param name="petriNet">The current petri net</param>
        /// <returns>A list with final transition</returns>
        /// <author>Jannik Arndt</author>
        public List <Transition> StartAxor_BandC(Transition event1, EventNode a, EventNode b, EventNode c, PetriNet petriNet)
        {
            var finalTransition = petriNet.AddTransition(); // empty transition, will possibly be cleaned up

            finalTransition.Name = FindFirstCommonSuccessor(new List <EventNode> {
                a, b, c
            });

            var xorTransition = StartXOR(event1, new List <EventNode> {
                a
            }, petriNet);

            EndXOR(xorTransition, petriNet, finalTransition);

            var andSplit = petriNet.AddTransition();                       // no idea where this disappears...

            andSplit.AddIncomingPlace(xorTransition[0].IncomingPlaces[0]); // XOR-Split-Place

            var andTransition = StartAND(andSplit, new List <EventNode> {
                b, c
            }, petriNet);
            var andJoin = petriNet.AddTransition("AND Join");

            andJoin.IsANDJoin = true;
            var andTransition2 = EndAND(andTransition, petriNet, andJoin);

            andTransition2[0].AddOutgoingPlace(finalTransition.IncomingPlaces.FirstOrDefault());

            return(new List <Transition> {
                finalTransition
            });
        }
        public void FindFirstCommonSuccessorTest()
        {
            var eventLog = EventLogExample.ComplexEventLogVanDerAalst();
            var field = new Field { EventLog = eventLog };
            var miner = new HeuristicMiner(field);

            var event1 = eventLog.Cases[0].EventList[0];
            var event2 = eventLog.Cases[0].EventList[1];
            var event3 = eventLog.Cases[0].EventList[2];

            var eventNode1 = new EventNode(event1, 0.8, new List<EventNode> { new EventNode(event2, 0.8, new List<EventNode> { new EventNode(event3, 0.8) }) });
            var eventNode2 = new EventNode(event1, 0.8, new List<EventNode> { new EventNode(event2, 0.8, new List<EventNode> { new EventNode(event3, 0.8) }) });

            var actual = miner.FindFirstCommonSuccessor(new List<EventNode> { eventNode1, eventNode2 });

            Assert.AreEqual("C", actual);
        }
示例#17
0
        /// <summary>
        /// Depending on the count of followers this adds one (or more) places and their following transitions.
        /// </summary>
        /// <param name="event1"></param>
        /// <param name="node">An event node</param>
        /// <param name="log">The current event log</param>
        /// <param name="petriNet"></param>
        /// <returns>The newly added transitions. This is where you need to continue working.</returns>
        /// <exception cref="NotImplementedException">If there are more than 3 followers in a non-trivial relation.</exception>
        /// <author>Jannik Arndt</author>
        public List <Transition> HandleNode(Transition event1, EventNode node, EventLog log, PetriNet petriNet)
        {
            // Case: No followers
            if (node.ListOfFollowers.Count == 0)
            {
                return(new List <Transition>());
            }

            // one or more more followers => count the AND-relations
            var andRelations = CountANDRelations(node, log);

            // Case: All nodes are AND-related
            if (andRelations == node.ListOfFollowers.Count)
            {
                return(StartAND(event1, node.ListOfFollowers, petriNet));
            }

            // Case: All nodes are XOR-related
            if (andRelations == 0)
            {
                return(StartXOR(event1, node.ListOfFollowers, petriNet));
            }

            // Case: 3 Followers
            if (node.ListOfFollowers.Count == 3)
            {
                var x = node;
                var a = node.ListOfFollowers[0];
                var b = node.ListOfFollowers[1];
                var c = node.ListOfFollowers[2];

                if (andRelations == 2) // XOR-Relations == 1
                {
                    // There are two and-relations and one xor-relation. Find the xor and order the parameters accordingly
                    if (IsXorRelation(x.InnerEvent, b.InnerEvent, c.InnerEvent, log))
                    {
                        return(StartAand_BxorC(event1, a, b, c, petriNet));
                    }
                    if (IsXorRelation(x.InnerEvent, a.InnerEvent, c.InnerEvent, log))
                    {
                        return(StartAand_BxorC(event1, b, a, c, petriNet));
                    }
                    if (IsXorRelation(x.InnerEvent, a.InnerEvent, b.InnerEvent, log))
                    {
                        return(StartAand_BxorC(event1, c, a, b, petriNet));
                    }
                }
                else // XOR-Relations == 2 && AND-Relations == 1
                {
                    // There are two xor-relations and one and-relation. Find the and and order the parameters accordingly
                    if (IsAndRelation(x.InnerEvent, b.InnerEvent, c.InnerEvent, log))
                    {
                        return(StartAxor_BandC(event1, a, b, c, petriNet));
                    }
                    if (IsAndRelation(x.InnerEvent, a.InnerEvent, c.InnerEvent, log))
                    {
                        return(StartAxor_BandC(event1, b, a, c, petriNet));
                    }
                    if (IsAndRelation(x.InnerEvent, a.InnerEvent, b.InnerEvent, log))
                    {
                        return(StartAxor_BandC(event1, c, a, b, petriNet));
                    }
                }
            }
            if (node.ListOfFollowers.Count > 3)
            {
                return(StartXOR(event1, node.ListOfFollowers, petriNet));
            }

            // optional transition
            if (node.ListOfFollowers.Count == 2)
            {
                if (log.EventFollowsEvent(node.ListOfFollowers[0].InnerEvent, node.ListOfFollowers[1].InnerEvent) > 0)
                {
                    return(StartOptionalTransition(event1, node.ListOfFollowers[0].InnerEvent.Name, node.ListOfFollowers[1].InnerEvent.Name, petriNet));
                }
                else if (log.EventFollowsEvent(node.ListOfFollowers[1].InnerEvent, node.ListOfFollowers[0].InnerEvent) > 0)
                {
                    return(StartOptionalTransition(event1, node.ListOfFollowers[1].InnerEvent.Name, node.ListOfFollowers[0].InnerEvent.Name, petriNet));
                }
            }

            return(null);
        }