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); }
/// <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> /// 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> /// 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; }
/// <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); }
/// <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); }