Beispiel #1
0
        private static List <WorkflowComponent> ClusterReversedModel(string name, List <Data> allData, List <WorkflowComponent> matchedComponents, List <WorkflowComponent> reversedComponents, ReversalMode mode, List <WorkflowComponent> components)
        {
            //// Identify Reversed Components
            //foreach (var component in matchedComponents)
            //	if (component is IReversableWorkflow reversable && reversable.ReversedInputs.Count > 0)
            //		reversedComponents.Add(component);

            // 3.1. Graph only with reversed links of non-reversible variables only, both forward and backward (can be thought as undirected)
            var reversedLinksGraph = new Graph();

            foreach (Data data in reversedComponents.GetAllData())
            {
                reversedLinksGraph.AddVertex(data.Id);
            }
            foreach (WorkflowComponent component in reversedComponents)
            {
                AddComponentAndEdgesBoth(reversedLinksGraph, component, mode);
            }

            // 3.2. Forward graph - To trace forward dependencies
            var forwardGraph = new Graph();

            foreach (Data data in allData)
            {
                forwardGraph.AddVertex(data.Id);
            }
            foreach (WorkflowComponent component in matchedComponents)
            {
                AddComponentAndEdgesForward(forwardGraph, component);
            }

            //forwardGraph = GraphBuilder.FromTwoSets(matchedComponents, allData, name1: c => CS(c.Id), from1to2: c => c.ModelDataOutputs, to1from2: c => c.ModelDataInputs);

            // 3.3. Reversed graph - To trace backward dependencies
            var reversedGraph = new Graph();

            foreach (Data data in allData)
            {
                reversedGraph.AddVertex(data.Id);
            }
            foreach (WorkflowComponent component in matchedComponents)
            {
                AddComponentAndEdgesBackWard(reversedGraph, component);
            }

            // 3.4 Group reversed components
            var visitedComponents      = new HashSet <string>();
            var reversedComponentsHash = new HashSet <string>(reversedComponents.Select(c => CS(c.Id)));
            var componentsHash         = new HashSet <string>(matchedComponents.Select(c => CS(c.Id)));
            var componentsUnderStudy   = new Stack <string>();
            var groups = new List <HashSet <string> >();
            HashSet <string> groupForwardHash  = null;           // Used to reduce unnecesary visits, if node is visited for one component in the group it shouldn't be visited more
            HashSet <string> groupBackwardHash = null;           // Used to reduce unnecesary visits, if node is visited for one component in the group it shouldn't be visited more

            foreach (WorkflowComponent component in reversedComponents)
            {
                // 3.4.1. If the component has not been studied start studie and prepare a gropup for the component
                if (!visitedComponents.Contains(CS(component.Id)))
                {
                    groups.Add(new HashSet <string>());
                    groupForwardHash  = new HashSet <string>();
                    groupBackwardHash = new HashSet <string>();
                    componentsUnderStudy.Push(CS(component.Id));
                }

                while (componentsUnderStudy.Count > 0)
                {
                    // 3.4.2. Get the component under study to investigate its dependencies
                    string componentUnderStudy = componentsUnderStudy.Pop();

                    if (visitedComponents.Contains(componentUnderStudy))
                    {
                        continue;
                    }

                    // 3.4.3. Group with adajacent reversed components
                    HashSet <string> adjacent = reversedLinksGraph.DepthFirstSearch(componentUnderStudy);
                    foreach (string adj in adjacent)
                    {
                        visitedComponents.Add(adj);
                    }

                    // If there is more than one component joint by non-reversible variables
                    if (adjacent.Where(a => reversedComponentsHash.Contains(a)).Count() > 1)
                    {
                        // 3.4.4. Extend with others dependencies
                        HashSet <string> affectedForward  = forwardGraph.DepthFirstSearch(adjacent, groupForwardHash);
                        HashSet <string> affectedBackward = reversedGraph.DepthFirstSearch(adjacent, groupBackwardHash);
                        affectedForward.IntersectWith(affectedBackward);
                        adjacent.UnionWith(affectedForward);
                    }

                    // 3.4.5. If dependencies include a new reversed model push to the stack to be studied
                    foreach (string adj in adjacent)
                    {
                        // If a component is a reversed component and hasn't been added yet
                        if (!visitedComponents.Contains(adj) && reversedComponentsHash.Contains(adj))
                        {
                            componentsUnderStudy.Push(adj);
                        }
                        else
                        {
                            visitedComponents.Add(adj);
                        }

                        // Add to the group of dependent model from the first reversed model under study
                        if (componentsHash.Contains(adj))
                        {
                            groups.Last().Add(adj);
                        }
                    }
                }
            }

            // 3.5 Create Globally Reversed Workflows for each group
            var globalReversedComponents = new List <WorkflowComponent>();
            var componentsDict           = matchedComponents.ToDictionary(c => CS(c.Id));

            foreach (HashSet <string> group in groups)
            {
                if (group.Count > 1)
                {
                    var         groupComponents = group.Select(c => componentsDict[c]).ToList();             // new List<WorkflowComponent>();
                    List <Data> groupData       = groupComponents.GetAllData();
                    var(groupInputs, groupOutputs, _) = groupComponents.GetInputsOutputsStatus(groupData);

                    GlobalReversalMode globalMode = (mode == ReversalMode.GroupNonReversibleOnly)
                                                ? GlobalReversalMode.ReverseModelsWhenReversibleVariables
                                                : GlobalReversalMode.NoReversedModels;
                    string   globalName     = WorkflowGlobal.GetGlobalWorkflowName(name, components, groupComponents);
                    Workflow globalWorkflow = ScheduleWorkflowGlobal(globalName, "", groupInputs, groupOutputs, groupComponents, globalMode);

                    globalReversedComponents.Add(globalWorkflow);
                }
                else
                {
                    // If the group consists of one component only, it is already a reversed model and does not have any unfeasible reversal
                    globalReversedComponents.Add(componentsDict[group.First()]);
                }
            }

            // 3.6. Add the rest of components
            globalReversedComponents.AddRange(matchedComponents.Where(c => !visitedComponents.Contains(CS(c.Id))));

            return(globalReversedComponents);
        }
Beispiel #2
0
        public static Workflow ScheduleWorkflowGlobal(string name, string description, List <Data> inputs, List <Data> outputs, List <WorkflowComponent> components, GlobalReversalMode mode)
        {
            List <WorkflowComponent> workflowComponents = components.GetAllComponents();
            var allData = inputs.Concat(outputs).ToList();

            // 0. Samity checks, check is not more than 1 output
            if (mode == GlobalReversalMode.NoReversedModels)
            {
                if (!AllSanityCheck(workflowComponents))
                {
                    mode = GlobalReversalMode.ReverseModelsWhenReversibleVariables;
                }
            }
            else if (mode == GlobalReversalMode.Global)
            {
                if (!AllSanityCheck(workflowComponents))
                {
                    throw new ArgumentException($"A default workflow does not exist. Therefore the gloabl workflow cannot be created");
                }
            }

            if (mode == GlobalReversalMode.ReverseModelsWhenReversibleVariables)
            {
                NonReversibleSanityCheck(workflowComponents);
            }

            // 1. Find Model-Variable matching. Which variables are inputs, and wich are outputs for each model
            MatchingDictionary outputsDict = MatchModelsWithVariablesGlobal(allData, components, mode);

            // 2. Determine which models are reversed, and create reversed Workflows (Model or Global) to cater for them
            ReverseComponents(name, outputsDict, workflowComponents, out List <WorkflowComponent> matchedComponents, out List <WorkflowComponent> reversedComponents);

            // 4. Cluster Strongly Connected Components to get an acyclic graph
            List <WorkflowComponent> clusteredComponents = ClusterStronglyConnectedComponents(name, allData, matchedComponents);

            // 5. Schedule the acyclic graph to get the final order of the components
            List <WorkflowComponent> scheduledComponents = ScheduleAcyclic(allData, clusteredComponents);

            Workflow workflow = null;

            if (clusteredComponents.Count < matchedComponents.Count)
            {
                workflow = ScheduleWorkflowSCC(name.Split(':').First(), description, inputs, outputs, workflowComponents, matchedComponents);
            }
            else
            {
                workflow = new WorkflowGlobal(name, description, inputs, outputs, components, scheduledComponents, DeafaultSolvers(), mode != GlobalReversalMode.Global, mode.ToString());
            }
            workflow.DependencyAnalysis = new GraphBasedDependencyAnalysis(matchedComponents);
            return(workflow);
        }
Beispiel #3
0
        private static MatchingDictionary MatchModelsWithVariablesGlobal(List <Data> allData, List <WorkflowComponent> components, GlobalReversalMode mode)
        {
            if (mode == GlobalReversalMode.NoReversedModels || mode == GlobalReversalMode.Global)
            {
                List <WorkflowComponent>      originalComponents = components.GetAllComponents();
                Dictionary <string, IOStatus> status             = originalComponents.GetInputsOutputsStatus(allData, out List <Data> inputs, out List <Data> outputs);
                foreach (string key in status.Keys)
                {
                    if (status[key] == IOStatus.Conflict)
                    {
                        throw new ArgumentException($"The following varibale \"{key}\" is of non-reversible type and output of more than one model. Therefore the workflow cannot be created");
                    }
                }

                var inputsHash = new HashSet <string>(inputs.Select(d => d.Id));

                var bipartite = new BipartiteGraph();
                Primes.Reset();
                var completeMatching = new MatchingDictionary(originalComponents.Select(c => c.Id));

                // 1.1 Add Nodes for the Variables
                // Filter-out the selected inputs and outputs, as they don't belong to the bipartite graph
                foreach (Data data in allData.Where(d => !inputsHash.Contains(d.Id)))
                {
                    bipartite.AddNode(data.Id, GraphNode.Type.Type1);
                }

                // 1.2 Add Nodes for the Models, and edges between Variables and Nodes
                foreach (WorkflowComponent component in originalComponents)
                {
                    completeMatching.Add(component.Id, new HashSet <string>());

                    bipartite.AddNode(CS(component.Id), GraphNode.Type.Type2);
                    GraphNode modelNode = bipartite.GetNode(CS(component.Id));

                    // Filter-out the selected inputs, as they don't belong to the bipartite graph
                    foreach (Data data in component.ModelDataInputs.Where(d => !inputsHash.Contains(d.Id)))
                    {
                        bipartite.AddDirectedEdge(data.Id, modelNode, Primes.Next(), 1);
                    }

                    // Filter-out the selected inputs, as they don't belong to the bipartite graph
                    foreach (Data data in component.ModelDataOutputs.Where(d => !inputsHash.Contains(d.Id)))
                    {
                        bipartite.AddDirectedEdge(data.Id, modelNode, Primes.Next(), 0);
                    }

                    // How many output the model need
                    if (component.ModelDataOutputs.Count > 1)
                    {
                        bipartite.modelAndItsOutputs.Add(CS(component.Id), component.ModelDataOutputs.Count);                         //signle output models are not stored
                    }
                }

                // 1.3 Associate matchings to each model, maping models to set of outputs
                var matches = new MaximumMatchings2(bipartite, true);

                if (matches.OverConstrainedModels.Count > 0)
                {
                    throw new ArgumentException($"The inputs conbination is not valid as the following models are overconstraint:\r\n\t" +
                                                matches.OverConstrainedModels.Aggregate((total, last) => total += "\r\n\t" + last) + "\r\n");
                }

                Matching matching = matches.OrderedFilteredMaximumMatchings.FirstOrDefault()
                                    ?? throw new NullReferenceException("NO suitable matchings were found");

                completeMatching.CompleteWithMatching(matching);

                return(completeMatching);
            }
            else
            {
                Dictionary <string, IOStatus> status = components.GetInputsOutputsStatus(allData, out List <Data> inputs, out List <Data> outputs);


                var inputsHash  = new HashSet <string>(inputs.Select(d => d.Id));
                var outputsHash = new HashSet <string>();

                List <WorkflowComponent>      originalComponents = components.GetAllComponents();
                Dictionary <string, IOStatus> originalStatus     = originalComponents.GetDataStatus(allData);

                var nonReversibleData = allData.Where(d => !(d is DoubleData)).ToList();
                Dictionary <string, IOStatus> nonReversibleStatus = components.GetDataStatus(nonReversibleData);
                foreach (string data in nonReversibleStatus.Keys)
                {
                    if (nonReversibleStatus[data] == IOStatus.Input)
                    {
                        inputsHash.Add(data);                         // Should be already there
                    }
                    else if (nonReversibleStatus[data] == IOStatus.Output || nonReversibleStatus[data] == IOStatus.Both)
                    {
                        outputsHash.Add(data);
                    }
                }

                var reversedInputsHash  = new HashSet <string>();
                var reversedOutputsHash = new HashSet <string>();
                foreach (WorkflowComponent component in components)
                {
                    if (component is IReversableWorkflow rw)
                    {
                        foreach (Data data in rw.ReversedInputs)
                        {
                            reversedInputsHash.Add(data.Id);
                        }

                        foreach (Data data in rw.ReversedOutputs)
                        {
                            reversedOutputsHash.Add(data.Id);
                        }

                        IEnumerable <Data> NonReversableReversedInputs  = rw.ReversedInputs.Where(d => !(d is DoubleData));
                        IEnumerable <Data> NonReversableReversedOutputs = rw.ReversedOutputs.Where(d => !(d is DoubleData));

                        // If the model id the upper end of a reversal throug non-reversible variables.
                        int difference = NonReversableReversedOutputs.Count() - NonReversableReversedInputs.Count();
                        while (difference > 0)
                        {
                            // Assign as many reversible variable to the inputs as reversals end in the component
                            IEnumerable <Data> reversableReversedInputs = rw.ReversedInputs.Where(d => d is DoubleData);
                            foreach (Data data in reversableReversedInputs.Take(difference))
                            {
                                inputsHash.Add(data.Id);
                            }
                        }
                    }
                }

                // Relax one input per reversal -> lowerEndOfReversal might have more elements than non-reversible reversal, but those should be already not in inputHash
                IEnumerable <string> lowerEndOfReversals = reversedOutputsHash.Except(reversedInputsHash);
                inputsHash.ExceptWith(lowerEndOfReversals);

                var bipartite = new BipartiteGraph();
                Primes.Reset();
                var completeMatching = new MatchingDictionary(originalComponents.Select(c => c.Id));

                // 1.1 Add Nodes for the Variables
                // Filter-out the selected inputs and outputs, as they don't belong to the bipartite graph
                foreach (Data data in allData.Where(d => !inputsHash.Contains(d.Id) && !outputsHash.Contains(d.Id)))
                {
                    bipartite.AddNode(data.Id, GraphNode.Type.Type1);
                }

                // 1.2 Add Nodes for the Models, and edges between Variables and Nodes
                foreach (WorkflowComponent component in originalComponents)
                {
                    completeMatching.Add(component.Id, new HashSet <string>());

                    // if the component has all its inputs and outputs determined do not add to the graph
                    bool addModel = component.ModelDataOutputs.Count > component.ModelDataOutputs.Where(d => inputsHash.Count > -1 && outputsHash.Contains(d.Id)).Count();
                    if (addModel)
                    {
                        bipartite.AddNode(component.Id, GraphNode.Type.Type2);
                    }

                    GraphNode modelNode = bipartite.GetNode(component.Id);

                    int uniqueNonReversibleOutputs = 0;

                    // Filter-out the selected inputs, as they don't belong to the bipartite graph
                    foreach (Data data in component.ModelDataInputs.Where(d => !inputsHash.Contains(d.Id) && !outputsHash.Contains(d.Id)))
                    {
                        bipartite.AddDirectedEdge(data.Id, modelNode, Primes.Next(), 1);
                    }

                    // Filter-out the selected inputs, as they don't belong to the bipartite graph
                    foreach (Data data in component.ModelDataOutputs.Where(d => !inputsHash.Contains(d.Id)))
                    {
                        if (outputsHash.Contains(data.Id))
                        {
                            uniqueNonReversibleOutputs++;
                            completeMatching[component.Id].Add(data.Id);
                        }
                        else
                        {
                            bipartite.AddDirectedEdge(data.Id, modelNode, Primes.Next(), 0);
                        }
                    }

                    // How many output the model need
                    if (component.ModelDataOutputs.Count - uniqueNonReversibleOutputs > 1 && addModel)
                    {
                        bipartite.modelAndItsOutputs.Add(component.Id, component.ModelDataOutputs.Count - uniqueNonReversibleOutputs);                         //signle output models are not stored
                    }
                }

                // 1.3 Associate matchings to each model, maping models to set of outputs
                var matches = new MaximumMatchings2(bipartite, true);

                if (matches.OverConstrainedModels.Count > 0)
                {
                    throw new ArgumentException($"The inputs conbination is not valid as the following models are overconstraint:\r\n\t" +
                                                matches.OverConstrainedModels.Aggregate((total, last) => total += "\r\n\t" + last) + "\r\n");
                }

                Matching matching = matches.OrderedFilteredMaximumMatchings.FirstOrDefault()
                                    ?? throw new NullReferenceException("NO suitable matchings were found");

                completeMatching.CompleteWithMatching(matching);

                return(completeMatching);
            }
        }