internal static ConstraintNetwork GenerateQualitativeConstraintNetwork(GKOStructuringContext strContext, RelationFamily calculus, Log log)
        {
            ConstraintNetwork resultNetwork = new ConstraintNetwork(calculus, strContext.Id + "_" + calculus.Name, log);
            List <ConfigurationConstraintTree> constraintTrees = strContext.StructuralConstraints.Where(x => x.RelationFamily == calculus).ToList();

            // Creating the constraints restricting that self edges should be labeled with the Equals relation from the calculus
            DomainConstraint            equalsDomainConstraint = DomainConstraint.SelfEqualsConstraint(calculus);
            ConfigurationConstraintTree equalsConstraint       = new ConfigurationConstraintTree(null, equalsDomainConstraint, strContext.Components.Where(x => x.Active).ToList(), log);

            // Adding the constraints restricting that self edges should be labeled with the Equals relation from the calculus
            constraintTrees.Add(equalsConstraint);

            resultNetwork.Context = strContext;
            // Adding all components as nodes in the constraint network
            strContext.Components.Where(x => x.Active).ToList().ForEach(x => resultNetwork.Nodes.Add(new Node(x)));

            // Creating all edges in the constraint network, so it is a complete graph
            foreach (var startNode in resultNetwork.Nodes)
            {
                foreach (var endNode in resultNetwork.Nodes)
                {
                    QualitativeEdge edge = new QualitativeEdge(resultNetwork, startNode, endNode);
                    resultNetwork.Edges.Add(new Tuple <Node, Node>(startNode, endNode), edge);
                }
            }

            // Each constraint tree has a number of configuration constraints
            foreach (var constraintTree in constraintTrees)
            {
                List <ConfigurationConstraint> constraints = constraintTree.GetAllConfigurationConstraints();

                // ToDo: Add check for equal constraints to avoid redundancy

                // Adding all constraints as edges in the networks
                constraints.ForEach(x =>
                {
                    // Hack: this uses the fact that each constraint has at least one allowed relation and all have the same signature and logic (i.e. start and end node)
                    Node startNode             = x.AllowedRelations[0].GetStartNode(x);
                    Node endNode               = x.AllowedRelations[0].GetEndNode(x);
                    Tuple <Node, Node> edgeKey = new Tuple <Node, Node>(startNode, endNode);
                    QualitativeEdge edge       = resultNetwork.Edges[edgeKey] as QualitativeEdge;

                    // Adding the constraint to the edge
                    edge.Constraints.Add(x);
                });
            }

            return(resultNetwork);
        }
        /// <summary>
        /// Generates the metric constraint network for the structured components
        /// </summary>
        /// <param name="structuringContexts">The list of structured components to generate the networks for.
        /// They must include the metric relation family in the list of included ones</param>
        /// <returns></returns>
        internal static List <ConstraintNetwork> GenerateMetricConstraintNetworks(List <GKOStructuringContext> structuringContexts, TmsManager tms, StructuralReasonerOptions options, Log log)
        {
            List <ConstraintNetwork>           constraintNetworks = new List <ConstraintNetwork>();
            List <List <Node> >                clusters           = new List <List <Node> >();
            List <ConfigurationConstraintTree> constraintTrees    = new List <ConfigurationConstraintTree>();
            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();
            // Adding all constraint trees to the list
            foreach (var context in structuringContexts)
            {
                context.StructuralConstraints.ForEach(x =>
                {
                    // Additionally assign the special metric values based on the context of the constraints
                    x.AssignSpecialMetricValues(context);
                    constraintTrees.Add(x);
                });
            }
            stopwatch.Stop();
            log.AddItem(LogType.Info, String.Format("Assigning special metric values finished ({0} ms).", stopwatch.ElapsedMilliseconds));

            stopwatch.Restart();
            // Combine the nodes in the constraint trees in linked clusters,
            // this serve as base for generating the constraint networks
            foreach (var constraintTree in constraintTrees)
            {
                clusters.AddRange(constraintTree.GetNodeClusters());
                ConstraintNetwork.NormalizeClusters(clusters);
            }
            stopwatch.Stop();
            log.AddItem(LogType.Info, String.Format("Finding node clusters finished ({0} ms).", stopwatch.ElapsedMilliseconds));

            #region TCSP specific
            // These constraints limit the domain of the decision variables for TCSP
            if (options.MetricReasoningAlgorithm == MetricReasoningAlgorithm.Tcsp)
            {
                stopwatch.Restart();
                List <Node> nodes = clusters.SelectMany(x => x).Where(x => x != null).Distinct().ToList();

                // Add min and max constraint for the constrained attributes
                foreach (var constrAttribute in nodes)
                {
                    List <GKOComponent> compList = new List <GKOComponent>()
                    {
                        constrAttribute.Component
                    };
                    DomainConstraint            minConstraint = DomainConstraint.MinValueConstraint(constrAttribute.Attribute, tms.MetricDomain);
                    DomainConstraint            maxConstraint = DomainConstraint.MaxValueConstraint(constrAttribute.Attribute, tms.MetricDomain);
                    ConfigurationConstraintTree minTree       = new ConfigurationConstraintTree(null, minConstraint, compList, log);
                    ConfigurationConstraintTree maxTree       = new ConfigurationConstraintTree(null, maxConstraint, compList, log);

                    constraintTrees.Add(minTree);
                    constraintTrees.Add(maxTree);
                }

                stopwatch.Stop();
                log.AddItem(LogType.Info, String.Format("Creating Min/Max constraints for TCSP finished ({0} ms).", stopwatch.ElapsedMilliseconds));
            }
            #endregion

            stopwatch.Restart();

            // Generating a new constraint network for each found node cluster
            foreach (var nodeCluster in clusters)
            {
                ConstraintNetwork network = new ConstraintNetwork(StructuralRelationsManager.MetricRelationsFamily, log);
                network.Nodes = nodeCluster;

                constraintNetworks.Add(network);
            }

            // Adding all configuration constraints in the constraint networks
            foreach (var constraintTree in constraintTrees)
            {
                ConfigurationConstraint[] treeConstraints = constraintTree.GetAllConfigurationConstraints().ToArray();

                for (int i = 0; i < treeConstraints.Length; i++)
                {
                    ConstraintNetwork.ApplyMetricConstraint(treeConstraints[i], constraintNetworks, log);
                }
            }

            // UId has to be assigned to the networks
            for (int i = 0; i < constraintNetworks.Count; i++)
            {
                constraintNetworks[i].UId = "MetricConstraintNetwork_" + (i + 1).ToString();
            }

            stopwatch.Stop();
            log.AddItem(LogType.Info, String.Format("Generating the constraint networks finished ({0} ms).", stopwatch.ElapsedMilliseconds));

            return(constraintNetworks);
        }