private bool ChangeEdgeTailNodeWithoutCleanup(T edgeId, T newTailNodeId) { // Do not attend this unless all dependencies are satisfied. if (!AllDependenciesSatisfied) { return(false); } // Retrieve the activity edge. if (!EdgeLookup.TryGetValue(edgeId, out Edge <T, TEdgeContent> _)) { return(false); } // Retrieve the new tail event node. if (!NodeLookup.TryGetValue(newTailNodeId, out Node <T, TNodeContent> newTailNode)) { return(false); } // Remove the connection from the current tail node. Node <T, TNodeContent> oldTailNode = EdgeTailNodeLookup[edgeId]; oldTailNode.OutgoingEdges.Remove(edgeId); EdgeTailNodeLookup.Remove(edgeId); // Attach to the new tail node. newTailNode.OutgoingEdges.Add(edgeId); EdgeTailNodeLookup.Add(edgeId, newTailNode); return(true); }
private void ResolveUnsatisfiedSuccessorActivities(T activityId) { // Check to make sure the node really exists. if (!NodeLookup.TryGetValue(activityId, out Node <T, TActivity> dependencyNode)) { return; } // Check to see if any existing activities were expecting this activity // as a dependency. If so, then then hook their nodes to this activity with an edge. if (UnsatisfiedSuccessorsLookup.TryGetValue(activityId, out HashSet <Node <T, TActivity> > unsatisfiedSuccessorNodes)) { // If the dependency node is an End or Isolated node, then convert it. if (dependencyNode.NodeType == NodeType.End) { dependencyNode.SetNodeType(NodeType.Normal); } else if (dependencyNode.NodeType == NodeType.Isolated) { dependencyNode.SetNodeType(NodeType.Start); } foreach (Node <T, TActivity> successorNode in unsatisfiedSuccessorNodes) { T edgeId = EdgeIdGenerator(); var edge = new Edge <T, TEvent>(EventGenerator(edgeId)); dependencyNode.OutgoingEdges.Add(edgeId); EdgeTailNodeLookup.Add(edgeId, dependencyNode); successorNode.IncomingEdges.Add(edgeId); EdgeHeadNodeLookup.Add(edgeId, successorNode); EdgeLookup.Add(edgeId, edge); } UnsatisfiedSuccessorsLookup.Remove(activityId); } }
public virtual void Reset() { EdgeLookup.Clear(); NodeLookup.Clear(); UnsatisfiedSuccessorsLookup.Clear(); EdgeHeadNodeLookup.Clear(); EdgeTailNodeLookup.Clear(); }
public Node <T, TNodeContent> EdgeTailNode(T key) { Node <T, TNodeContent> output = null; if (EdgeTailNodeLookup.ContainsKey(key)) { output = EdgeTailNodeLookup[key]; } return(output); }
protected GraphBuilderBase( Graph <T, TEdgeContent, TNodeContent> arrowGraph, Func <T> edgeIdGenerator, Func <T> nodeIdGenerator, Func <T, TEvent> eventGenerator) : this(edgeIdGenerator, nodeIdGenerator, eventGenerator) { if (arrowGraph is null) { throw new ArgumentNullException(nameof(arrowGraph)); } foreach (Edge <T, TEdgeContent> edge in arrowGraph.Edges) { EdgeLookup.Add(edge.Id, edge); } foreach (Node <T, TNodeContent> node in arrowGraph.Nodes) { // Assimilate incoming edges. if (node.NodeType != NodeType.Start && node.NodeType != NodeType.Isolated) { foreach (T edgeId in node.IncomingEdges) { EdgeHeadNodeLookup.Add(edgeId, node); } } // Assimilate Outgoing edges. if (node.NodeType != NodeType.End && node.NodeType != NodeType.Isolated) { foreach (T edgeId in node.OutgoingEdges) { EdgeTailNodeLookup.Add(edgeId, node); } } NodeLookup.Add(node.Id, node); } // Check all edges are used. if (!EdgeLookup.Keys.OrderBy(x => x).SequenceEqual(EdgeHeadNodeLookup.Keys.OrderBy(x => x))) { throw new ArgumentException(Properties.Resources.ListOfEdgeIdsAndEdgesReferencedByHeadNodesDoNotMatch); } if (!EdgeLookup.Keys.OrderBy(x => x).SequenceEqual(EdgeTailNodeLookup.Keys.OrderBy(x => x))) { throw new ArgumentException(Properties.Resources.ListOfEdgeIdsAndEdgesReferencedByTailNodesDoNotMatch); } // Check all nodes are used. IEnumerable <T> edgeNodeLookupIds = EdgeHeadNodeLookup.Values.Select(x => x.Id).Union(EdgeTailNodeLookup.Values.Select(x => x.Id)); if (!NodeLookup.Values.Where(x => x.NodeType != NodeType.Isolated).Select(x => x.Id).OrderBy(x => x).SequenceEqual(edgeNodeLookupIds.OrderBy(x => x))) { throw new ArgumentException(Properties.Resources.ListOfNodeIdsAndEdgesReferencedByTailNodesDoNotMatch); } }
private void ResolveUnsatisfiedSuccessorActivities(T activityId) { // Check to make sure the edge really exists. if (!EdgeLookup.ContainsKey(activityId)) { return; } // Check to see if any existing activities were expecting this activity // as a dependency. If so, then then hook up their tail nodes to this // activity's head node with a dummy edge. if (UnsatisfiedSuccessorsLookup.TryGetValue(activityId, out HashSet <Node <T, TEvent> > unsatisfiedSuccessorTailNodes)) { // We know that there are unsatisfied dependencies, so create a head node. T headEventId = NodeIdGenerator(); var headNode = new Node <T, TEvent>(EventGenerator(headEventId)); headNode.IncomingEdges.Add(activityId); EdgeHeadNodeLookup.Add(activityId, headNode); NodeLookup.Add(headNode.Id, headNode); foreach (Node <T, TEvent> tailNode in unsatisfiedSuccessorTailNodes) { T dummyEdgeId = EdgeIdGenerator(); var dummyEdge = new Edge <T, TActivity>(DummyActivityGenerator(dummyEdgeId)); tailNode.IncomingEdges.Add(dummyEdgeId); EdgeHeadNodeLookup.Add(dummyEdgeId, tailNode); headNode.OutgoingEdges.Add(dummyEdgeId); EdgeTailNodeLookup.Add(dummyEdgeId, headNode); EdgeLookup.Add(dummyEdge.Id, dummyEdge); } UnsatisfiedSuccessorsLookup.Remove(activityId); } else { // No existing activities were expecting this activity as a dependency, // so attach it directly to the end node via a dummy. T headEventId = NodeIdGenerator(); Node <T, TEvent> dependencyHeadNode = new Node <T, TEvent>(EventGenerator(headEventId)); dependencyHeadNode.IncomingEdges.Add(activityId); EdgeHeadNodeLookup.Add(activityId, dependencyHeadNode); NodeLookup.Add(dependencyHeadNode.Id, dependencyHeadNode); T dummyEdgeId = EdgeIdGenerator(); var dummyEdge = new Edge <T, TActivity>(DummyActivityGenerator(dummyEdgeId)); dependencyHeadNode.OutgoingEdges.Add(dummyEdgeId); EdgeTailNodeLookup.Add(dummyEdgeId, dependencyHeadNode); EdgeLookup.Add(dummyEdgeId, dummyEdge); EndNode.IncomingEdges.Add(dummyEdgeId); EdgeHeadNodeLookup.Add(dummyEdgeId, EndNode); } }
//private void RemoveParallelIncomingEdges(Node<T, TActivity> node) //{ // if (node is null) // { // throw new ArgumentNullException(nameof(node)); // } // // Clean up any dummy edges that are parallel coming into the head node. // if (node.NodeType == NodeType.Start || node.NodeType == NodeType.Isolated) // { // return; // } // // First, find the tail nodes that connect to this node via ALL edges. // // In a vertex graph, all edges should be removable. // var tailNodeParallelEdgesLookup = new Dictionary<T, HashSet<T>>(); // IEnumerable<T> removableIncomingEdgeIds = // node.IncomingEdges.Select(x => EdgeLookup[x]) // .Where(x => x.Content.CanBeRemoved) // .Select(x => x.Id); // foreach (T incomingEdgeId in removableIncomingEdgeIds) // { // T tailNodeId = EdgeTailNodeLookup[incomingEdgeId].Id; // if (!tailNodeParallelEdgesLookup.TryGetValue(tailNodeId, out HashSet<T> edgeIds)) // { // edgeIds = new HashSet<T>(); // tailNodeParallelEdgesLookup.Add(tailNodeId, edgeIds); // } // if (!edgeIds.Contains(incomingEdgeId)) // { // edgeIds.Add(incomingEdgeId); // } // } // // Now find the tail nodes that connect to this node via multiple edges. // IList<T> setsOfMoreThanOneEdge = // tailNodeParallelEdgesLookup // .Where(x => x.Value.Count > 1) // .Select(x => x.Key) // .ToList(); // foreach (T tailNodeId in setsOfMoreThanOneEdge) // { // Node<T, TActivity> tailNode = EdgeTailNodeLookup[tailNodeId]; // IList<T> edgeIds = tailNodeParallelEdgesLookup[tailNodeId].ToList(); // int length = edgeIds.Count; // // Leave one edge behind. // for (int i = 1; i < length; i++) // { // T edgeId = edgeIds[i]; // // Remove the edge from the tail node. // tailNode.OutgoingEdges.Remove(edgeId); // EdgeTailNodeLookup.Remove(edgeId); // // Remove the edge from the head node. // node.IncomingEdges.Remove(edgeId); // EdgeHeadNodeLookup.Remove(edgeId); // // Remove the edge completely. // EdgeLookup.Remove(edgeId); // } // } //} private void RemoveRedundantIncomingEdges(T nodeId, IDictionary <T, HashSet <T> > nodeIdAncestorLookup) { if (nodeIdAncestorLookup is null) { throw new ArgumentNullException(nameof(nodeIdAncestorLookup)); } Node <T, TActivity> node = NodeLookup[nodeId]; if (node.NodeType == NodeType.Start || node.NodeType == NodeType.Isolated) { return; } // Go through all the incoming edges and collate the // ancestors of their tail nodes. var tailNodeAncestors = new HashSet <T>(node.IncomingEdges .Select(x => EdgeTailNodeLookup[x].Id) .SelectMany(x => nodeIdAncestorLookup[x])); // Go through the incoming edges and remove any that connect // directly to any ancestors of the edges' tail nodes. // In a vertex graph, all edges should be removable. foreach (T edgeId in node.IncomingEdges.Select(x => EdgeLookup[x]).Where(x => x.Content.CanBeRemoved).Select(x => x.Id).ToList()) { Node <T, TActivity> tailNode = EdgeTailNodeLookup[edgeId]; T edgeTailNodeId = tailNode.Id; if (tailNodeAncestors.Contains(edgeTailNodeId)) { // Remove the edge from the tail node. tailNode.OutgoingEdges.Remove(edgeId); EdgeTailNodeLookup.Remove(edgeId); // Remove the edge from the node itself. node.IncomingEdges.Remove(edgeId); EdgeHeadNodeLookup.Remove(edgeId); // Remove the edge completely. EdgeLookup.Remove(edgeId); } } // Go through all the remaining incoming edges and repeat. foreach (T tailNodeId in node.IncomingEdges.Select(x => EdgeTailNodeLookup[x].Id).ToList()) { RemoveRedundantIncomingEdges(tailNodeId, nodeIdAncestorLookup); } }
public bool RemoveDummyActivity(T activityId) { // Retrieve the activity's edge. if (!EdgeLookup.TryGetValue(activityId, out Edge <T, TActivity> edge)) { return(false); } if (!edge.Content.IsDummy) { return(false); } if (!edge.Content.CanBeRemoved) { return(false); } Node <T, TEvent> tailNode = EdgeTailNodeLookup[activityId]; Node <T, TEvent> headNode = EdgeHeadNodeLookup[activityId]; // Check to make sure that no other edges will be made parallel // by removing this edge. if (HaveDecendantOrAncestorOverlap(tailNode, headNode) && !ShareMoreThanOneEdge(tailNode, headNode)) { return(false); } // Remove the edge from the tail node. tailNode.OutgoingEdges.Remove(activityId); EdgeTailNodeLookup.Remove(activityId); // Remove the edge from the head node. headNode.IncomingEdges.Remove(activityId); EdgeHeadNodeLookup.Remove(activityId); // Remove the edge completely. EdgeLookup.Remove(activityId); // If the head node is not the End node, and it has no more incoming // edges, then transfer the head node's outgoing edges to the tail node. if (headNode.NodeType != NodeType.End && headNode.NodeType != NodeType.Isolated && !headNode.IncomingEdges.Any()) { IList <T> headNodeOutgoingEdgeIds = headNode.OutgoingEdges.ToList(); foreach (T headNodeOutgoingEdgeId in headNodeOutgoingEdgeIds) { bool changeTailSuccess = ChangeEdgeTailNode(headNodeOutgoingEdgeId, tailNode.Id); if (!changeTailSuccess) { throw new InvalidOperationException($@"Unable to change tail node of edge {headNodeOutgoingEdgeId} to node {tailNode.Id} when removing dummy activity {activityId}"); } } } else if (tailNode.NodeType != NodeType.Start && tailNode.NodeType != NodeType.Isolated && !tailNode.OutgoingEdges.Any()) { // If the tail node is not the Start node, and it has no more outgoing // edges, then transfer the tail node's incoming edges to the head node. IList <T> tailNodeIncomingEdgeIds = tailNode.IncomingEdges.ToList(); foreach (T tailNodeIncomingEdgeId in tailNodeIncomingEdgeIds) { bool changeHeadSuccess = ChangeEdgeHeadNode(tailNodeIncomingEdgeId, headNode.Id); if (!changeHeadSuccess) { throw new InvalidOperationException($@"Unable to change head node of edge {tailNodeIncomingEdgeId} to node {headNode.Id} when removing dummy activity {activityId}"); } } } return(true); }
public override bool AddActivity(TActivity activity, HashSet <T> dependencies) { if (activity == null) { throw new ArgumentNullException(nameof(activity)); } if (dependencies is null) { throw new ArgumentNullException(nameof(dependencies)); } if (EdgeLookup.ContainsKey(activity.Id)) { return(false); } if (dependencies.Contains(activity.Id)) { return(false); } // Create a new edge for the activity. var edge = new Edge <T, TActivity>(activity); EdgeLookup.Add(edge.Id, edge); // We expect dependencies at some point. if (dependencies.Any()) { // Since we use dummy edges to connect all tail nodes, we can create // a new tail node for this edge. T tailEventId = NodeIdGenerator(); var tailNode = new Node <T, TEvent>(EventGenerator(tailEventId)); tailNode.OutgoingEdges.Add(edge.Id); EdgeTailNodeLookup.Add(edge.Id, tailNode); NodeLookup.Add(tailNode.Id, tailNode); // Check which of the expected dependencies currently exist. IList <T> existingDependencies = EdgeLookup.Keys.Intersect(dependencies).ToList(); IList <T> nonExistingDependencies = dependencies.Except(existingDependencies).ToList(); // If any expected dependencies currently exist, then hook up their head // node to this edge's tail node with dummy edges. foreach (T dependencyId in existingDependencies) { Node <T, TEvent> dependencyHeadNode = EdgeHeadNodeLookup[dependencyId]; T dummyEdgeId = EdgeIdGenerator(); var dummyEdge = new Edge <T, TActivity>(DummyActivityGenerator(dummyEdgeId)); tailNode.IncomingEdges.Add(dummyEdgeId); EdgeHeadNodeLookup.Add(dummyEdgeId, tailNode); // If the head node of the dependency is the End node, then convert it. if (dependencyHeadNode.NodeType == NodeType.End) { dependencyHeadNode.SetNodeType(NodeType.Normal); } dependencyHeadNode.OutgoingEdges.Add(dummyEdgeId); EdgeTailNodeLookup.Add(dummyEdgeId, dependencyHeadNode); EdgeLookup.Add(dummyEdgeId, dummyEdge); } // If any expected dependencies currently do not exist, then record their // IDs and add this edge's tail node as an unsatisfied successor. foreach (T dependencyId in nonExistingDependencies) { if (!UnsatisfiedSuccessorsLookup.TryGetValue(dependencyId, out HashSet <Node <T, TEvent> > tailNodes)) { tailNodes = new HashSet <Node <T, TEvent> >(); UnsatisfiedSuccessorsLookup.Add(dependencyId, tailNodes); } tailNodes.Add(tailNode); } } else { // No dependencies, so attach it directly to the start node. StartNode.OutgoingEdges.Add(edge.Id); EdgeTailNodeLookup.Add(edge.Id, StartNode); } ResolveUnsatisfiedSuccessorActivities(edge.Id); return(true); }
public override bool RemoveActivityDependencies(T activityId, HashSet <T> dependencies) { if (dependencies is null) { throw new ArgumentNullException(nameof(dependencies)); } if (!NodeLookup.TryGetValue(activityId, out Node <T, TActivity> node)) { return(false); } if (!dependencies.Any()) { return(true); } RemoveUnsatisfiedSuccessorActivityDependencies(activityId, dependencies); if (node.NodeType == NodeType.Start || node.NodeType == NodeType.Isolated) { return(true); } // If any dependencies currently exist, remove them. var existingDependencyLookup = new HashSet <T>(NodeLookup.Keys.Intersect(dependencies)); IList <T> incomingEdgeIds = node.IncomingEdges.ToList(); int length = incomingEdgeIds.Count; for (int i = 0; i < length; i++) { T edgeId = incomingEdgeIds[i]; Node <T, TActivity> tailNode = EdgeTailNodeLookup[edgeId]; if (!existingDependencyLookup.Contains(tailNode.Id)) { continue; } // Remove the edge from the tail node. tailNode.OutgoingEdges.Remove(edgeId); EdgeTailNodeLookup.Remove(edgeId); if (!tailNode.OutgoingEdges.Any()) { if (tailNode.NodeType == NodeType.Normal) { tailNode.SetNodeType(NodeType.End); } else if (tailNode.NodeType == NodeType.Start) { tailNode.SetNodeType(NodeType.Isolated); } } // Remove the edge from the head node. node.IncomingEdges.Remove(edgeId); EdgeHeadNodeLookup.Remove(edgeId); // Remove the edge completely. EdgeLookup.Remove(edgeId); } if (!node.IncomingEdges.Any()) { if (node.NodeType == NodeType.Normal) { node.SetNodeType(NodeType.Start); } else if (node.NodeType == NodeType.End) { node.SetNodeType(NodeType.Isolated); } } return(true); }
public override bool RemoveActivity(T activityId) { // Retrieve the activity's node. if (!NodeLookup.TryGetValue(activityId, out Node <T, TActivity> node)) { return(false); } if (!node.Content.CanBeRemoved) { return(false); } RemoveUnsatisfiedSuccessorActivity(activityId); NodeLookup.Remove(node.Id); if (node.NodeType == NodeType.Isolated) { return(true); } if (node.NodeType == NodeType.End || node.NodeType == NodeType.Normal) { IList <T> incomingEdgeIds = node.IncomingEdges.ToList(); int length = incomingEdgeIds.Count; for (int i = 0; i < length; i++) { T edgeId = incomingEdgeIds[i]; Node <T, TActivity> tailNode = EdgeTailNodeLookup[edgeId]; // Remove the edge from the tail node. tailNode.OutgoingEdges.Remove(edgeId); EdgeTailNodeLookup.Remove(edgeId); if (!tailNode.OutgoingEdges.Any()) { if (tailNode.NodeType == NodeType.Normal) { tailNode.SetNodeType(NodeType.End); } else if (tailNode.NodeType == NodeType.Start) { tailNode.SetNodeType(NodeType.Isolated); } } // Remove the edge from the head node. node.IncomingEdges.Remove(edgeId); EdgeHeadNodeLookup.Remove(edgeId); if (!node.IncomingEdges.Any()) { if (node.NodeType == NodeType.Normal) { node.SetNodeType(NodeType.Start); } else if (node.NodeType == NodeType.End) { node.SetNodeType(NodeType.Isolated); } } // Remove the edge completely. EdgeLookup.Remove(edgeId); } } if (node.NodeType == NodeType.Start || node.NodeType == NodeType.Normal) { IList <T> outgoingEdgeIds = node.OutgoingEdges.ToList(); int length = outgoingEdgeIds.Count; for (int i = 0; i < length; i++) { T edgeId = outgoingEdgeIds[i]; Node <T, TActivity> headNode = EdgeHeadNodeLookup[edgeId]; // Remove the edge from the head node. headNode.IncomingEdges.Remove(edgeId); EdgeHeadNodeLookup.Remove(edgeId); if (!headNode.IncomingEdges.Any()) { if (headNode.NodeType == NodeType.Normal) { headNode.SetNodeType(NodeType.Start); } else if (headNode.NodeType == NodeType.End) { headNode.SetNodeType(NodeType.Isolated); } } // Remove the edge from the tail node. node.OutgoingEdges.Remove(edgeId); EdgeTailNodeLookup.Remove(edgeId); if (!node.OutgoingEdges.Any()) { if (node.NodeType == NodeType.Normal) { node.SetNodeType(NodeType.End); } else if (node.NodeType == NodeType.Start) { node.SetNodeType(NodeType.Isolated); } } // Remove the edge completely. EdgeLookup.Remove(edgeId); } } return(true); }
public override bool AddActivityDependencies(T activityId, HashSet <T> dependencies) { if (dependencies is null) { throw new ArgumentNullException(nameof(dependencies)); } if (!NodeLookup.TryGetValue(activityId, out Node <T, TActivity> node)) { return(false); } if (!dependencies.Any()) { return(true); } if (dependencies.Contains(activityId)) { return(false); } // If the node is an Start or Isolated node, then convert it. if (node.NodeType == NodeType.Start) { node.SetNodeType(NodeType.Normal); } else if (node.NodeType == NodeType.Isolated) { node.SetNodeType(NodeType.End); } // Check which of the expected dependencies currently exist. IList <T> existingDependencies = NodeLookup.Keys.Intersect(dependencies).ToList(); IList <T> nonExistingDependencies = dependencies.Except(existingDependencies).ToList(); // If any expected dependencies currently exist, generate an edge to connect them. foreach (T dependencyId in existingDependencies) { Node <T, TActivity> dependencyNode = NodeLookup[dependencyId]; T edgeId = EdgeIdGenerator(); var edge = new Edge <T, TEvent>(EventGenerator(edgeId)); node.IncomingEdges.Add(edgeId); EdgeHeadNodeLookup.Add(edgeId, node); // If the dependency node is an End or Isolated node, then convert it. if (dependencyNode.NodeType == NodeType.End) { dependencyNode.SetNodeType(NodeType.Normal); } else if (dependencyNode.NodeType == NodeType.Isolated) { dependencyNode.SetNodeType(NodeType.Start); } dependencyNode.OutgoingEdges.Add(edgeId); EdgeTailNodeLookup.Add(edgeId, dependencyNode); EdgeLookup.Add(edgeId, edge); } // If any expected dependencies currently do not exist, then record their // IDs and add this node as an unsatisfied successor. foreach (T dependencyId in nonExistingDependencies) { if (!UnsatisfiedSuccessorsLookup.TryGetValue(dependencyId, out HashSet <Node <T, TActivity> > successorNodes)) { successorNodes = new HashSet <Node <T, TActivity> >(); UnsatisfiedSuccessorsLookup.Add(dependencyId, successorNodes); } successorNodes.Add(node); } return(true); }